diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /fs | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'fs')
117 files changed, 4004 insertions, 2041 deletions
diff --git a/fs/Makefile b/fs/Makefile index 78dd720ce..7f2b18f5f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -11,38 +11,58 @@ SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv ifdef CONFIG_MINIX_FS FS_SUBDIRS := $(FS_SUBDIRS) minix +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) minix endif + ifdef CONFIG_EXT_FS FS_SUBDIRS := $(FS_SUBDIRS) ext endif + ifdef CONFIG_EXT2_FS FS_SUBDIRS := $(FS_SUBDIRS) ext2 endif + ifdef CONFIG_MSDOS_FS FS_SUBDIRS := $(FS_SUBDIRS) msdos else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos endif + ifdef CONFIG_PROC_FS FS_SUBDIRS := $(FS_SUBDIRS) proc endif + ifdef CONFIG_ISO9660_FS FS_SUBDIRS := $(FS_SUBDIRS) isofs +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) isofs endif + ifdef CONFIG_NFS_FS FS_SUBDIRS := $(FS_SUBDIRS) nfs +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) nfs endif + ifdef CONFIG_XIA_FS FS_SUBDIRS := $(FS_SUBDIRS) xiafs +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) xiafs endif + ifdef CONFIG_UMSDOS_FS FS_SUBDIRS := $(FS_SUBDIRS) umsdos else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos endif + ifdef CONFIG_SYSV_FS FS_SUBDIRS := $(FS_SUBDIRS) sysv +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv endif + ifdef CONFIG_HPFS_FS FS_SUBDIRS := $(FS_SUBDIRS) hpfs endif @@ -50,7 +70,7 @@ endif ifdef CONFIG_BINFMT_ELF BINFMTS := $(BINFMTS) binfmt_elf.o else -MODULES := $(MODULES) binfmt_elf.o +MODULE_OBJS := $(MODULE_OBJS) binfmt_elf.o endif .c.s: @@ -61,10 +81,10 @@ endif $(AS) -o $*.o $< OBJS= open.o read_write.o inode.o devices.o file_table.o buffer.o super.o \ - block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o \ + block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o readdir.o \ select.o fifo.o locks.o filesystems.o dcache.o $(BINFMTS) -all: fs.o filesystems.a modules modules_fs +all: fs.o filesystems.a fs.o: $(OBJS) $(LD) -r -o fs.o $(OBJS) @@ -75,36 +95,18 @@ filesystems.a: dummy test ! -d $$i || \ { $(MAKE) -C $$i; $(AR) rcs filesystems.a $$i/$$i.o; }; done -ifdef MODULES - -modules: - $(MAKE) CFLAGS="$(CFLAGS) -DMODULE" $(MODULES) - (cd ../modules;for i in $(MODULES); do ln -sf ../fs/$$i .; done) - -else - -modules: - -endif - -ifdef MODULE_FS_SUBDIRS - -modules_fs: - set -e; for i in $(MODULE_FS_SUBDIRS); do \ - test ! -d $$i || \ - { $(MAKE) -C $$i; }; done - - -else - -modules_fs: - -endif +modules: $(MODULE_OBJS) + for i in $(MODULE_FS_SUBDIRS); do $(MAKE) -C $$i modules; done + cd ../modules;for i in $(MODULE_OBJS); do ln -sf ../fs/$$i .; done + cd ../modules;for i in $(MODULE_FS_SUBDIRS); \ + do echo $$i.o; ln -sf ../fs/$$i/$$i.o .; done > ../modules/FS_MODULES depend dep: $(CPP) -M *.c > .depend - set -e; for i in $(SUBDIRS); do \ + set -e; for i in $(FS_SUBDIRS); do \ test ! -d $$i || $(MAKE) -C $$i dep; done + set -e; for i in $(MODULE_FS_SUBDIRS); do \ + test ! -d $$i || $(MAKE) -C $$i CFLAGS="$(CFLAGS) -DMODULE" dep; done dummy: diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index fd5e41cdc..ecdffb85a 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -8,6 +8,15 @@ * * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). */ + +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/fs.h> #include <linux/sched.h> #include <linux/mm.h> @@ -24,16 +33,12 @@ #include <linux/personality.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <linux/config.h> -#ifndef CONFIG_BINFMT_ELF -#include <linux/module.h> -#include "../tools/version.h" -#endif - #include <linux/unistd.h> -typedef int (*sysfun_p)(); +typedef int (*sysfun_p)(int); extern sysfun_p sys_call_table[]; #define SYS(name) (sys_call_table[__NR_##name]) @@ -45,7 +50,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs); static int load_elf_library(int fd); struct linux_binfmt elf_format = { -#ifdef CONFIG_BINFMT_ELF +#ifndef MODULE NULL, NULL, load_elf_binary, load_elf_library, NULL #else NULL, &mod_use_count_, load_elf_binary, load_elf_library, NULL @@ -58,19 +63,19 @@ struct linux_binfmt elf_format = { be in memory */ -static void padzero(int elf_bss){ - unsigned int fpnt, nbyte; +static void padzero(unsigned long elf_bss) +{ + unsigned long fpnt, nbyte; - if(elf_bss & 0xfff) { - - nbyte = (PAGE_SIZE - (elf_bss & 0xfff)) & 0xfff; - if(nbyte){ - verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); - - fpnt = elf_bss; - while(fpnt & 0xfff) put_fs_byte(0, fpnt++); - }; - }; + nbyte = elf_bss & (PAGE_SIZE-1); + if (nbyte) { + nbyte = PAGE_SIZE - nbyte; + verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); + fpnt = elf_bss; + do { + put_fs_byte(0, fpnt++); + } while (--nbyte); + } } unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs) @@ -84,7 +89,7 @@ unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exe mpnt->vm_task = current; mpnt->vm_start = PAGE_MASK & (unsigned long) p; mpnt->vm_end = TASK_SIZE; - mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY; + mpnt->vm_page_prot = PAGE_COPY; #ifdef VM_STACK_FLAGS mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_pte = 0; @@ -93,7 +98,6 @@ unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exe mpnt->vm_flags = VM_GROWSDOWN; # endif #endif - mpnt->vm_share = NULL; mpnt->vm_inode = NULL; mpnt->vm_offset = 0; mpnt->vm_ops = NULL; @@ -204,10 +208,13 @@ static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex, eppnt = elf_phdata; for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) if(eppnt->p_type == PT_LOAD) { + int elf_prot = (eppnt->p_flags & PF_R) ? PROT_READ : 0; + if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; error = do_mmap(file, eppnt->p_vaddr & 0xfffff000, eppnt->p_filesz + (eppnt->p_vaddr & 0xfff), - PROT_READ | PROT_WRITE | PROT_EXEC, + elf_prot, MAP_PRIVATE | MAP_DENYWRITE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0), eppnt->p_offset & 0xfffff000); @@ -315,9 +322,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned int elf_stack; char passed_fileno[6]; -#ifndef CONFIG_BINFMT_ELF MOD_INC_USE_COUNT; -#endif ibcs2_interpreter = 0; status = 0; @@ -326,9 +331,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_ex.e_ident[0] != 0x7f || strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; } @@ -338,9 +341,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || !bprm->inode->i_op->default_file_ops->mmap)){ -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; }; @@ -356,9 +357,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) set_fs(old_fs); if (retval < 0) { kfree (elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return retval; } @@ -371,9 +370,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_exec_fileno < 0) { kfree (elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return elf_exec_fileno; } @@ -420,9 +417,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if(retval < 0) { kfree (elf_phdata); kfree(elf_interpreter); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return retval; }; }; @@ -437,9 +432,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if(retval < 0) { kfree(elf_interpreter); kfree(elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ELIBACC; }; /* Now figure out which format our binary is */ @@ -456,9 +449,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) { kfree(elf_interpreter); kfree(elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ELIBBAD; }; } @@ -483,9 +474,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) kfree(elf_interpreter); } kfree (elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -E2BIG; } } @@ -502,7 +491,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; - bprm->p += change_ldt(0, bprm->page); + bprm->p += setup_arg_pages(0, bprm->page); current->mm->start_stack = bprm->p; /* Now we do a little grungy work by mmaping the ELF image into @@ -537,20 +526,21 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) printk("Unable to load interpreter\n"); kfree(elf_phdata); send_sig(SIGSEGV, current, 0); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return 0; }; }; if(elf_ppnt->p_type == PT_LOAD) { + int elf_prot = (elf_ppnt->p_flags & PF_R) ? PROT_READ : 0; + if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; error = do_mmap(file, elf_ppnt->p_vaddr & 0xfffff000, elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff), - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, + elf_prot, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, elf_ppnt->p_offset & 0xfffff000); #ifdef LOW_ELF_STACK @@ -631,25 +621,20 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) printk("(brk) %x\n" , current->mm->brk); #endif - /* Why this, you ask??? Well SVr4 maps page 0 as read-only, - and some applications "depend" upon this behavior. - Since we do not have the power to recompile these, we - emulate the SVr4 behavior. Sigh. */ - error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); - -#if defined (__i386__) - regs->eip = elf_entry; /* eip, magic happens :-) */ - regs->esp = bprm->p; /* stack pointer */ -#elif defined (__mips__) - regs->cp0_epc = elf_entry; /* eip, magic happens :-) */ - regs->reg29 = bprm->p; /* stack pointer */ -#endif + if( current->personality == PER_SVR4 ) + { + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + and some applications "depend" upon this behavior. + Since we do not have the power to recompile these, we + emulate the SVr4 behavior. Sigh. */ + error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); + } + + start_thread(regs, elf_entry, bprm->p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return 0; } @@ -669,10 +654,7 @@ load_elf_library(int fd){ int error; int i,j, k; -#ifndef CONFIG_BINFMT_ELF MOD_INC_USE_COUNT; -#endif - len = 0; file = current->files->fd[fd]; inode = file->f_inode; @@ -681,18 +663,14 @@ load_elf_library(int fd){ set_fs(KERNEL_DS); if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) { SYS(close)(fd); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -EACCES; } set_fs(USER_DS); if (elf_ex.e_ident[0] != 0x7f || strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; } @@ -700,18 +678,14 @@ load_elf_library(int fd){ if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || (!inode->i_op || !inode->i_op->default_file_ops->mmap)){ -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; }; /* Now read in all of the header information */ if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) { -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; } @@ -730,9 +704,7 @@ load_elf_library(int fd){ if(j != 1) { kfree(elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return -ENOEXEC; }; @@ -752,9 +724,7 @@ load_elf_library(int fd){ SYS(close)(fd); if (error != elf_phdata->p_vaddr & 0xfffff000) { kfree(elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return error; } @@ -767,13 +737,11 @@ load_elf_library(int fd){ PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); kfree(elf_phdata); -#ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; -#endif return 0; } -#ifndef CONFIG_BINFMT_ELF +#ifdef MODULE char kernel_version[] = UTS_RELEASE; int init_module(void) { diff --git a/fs/block_dev.c b/fs/block_dev.c index d19af6fa0..9d54d03c7 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -9,12 +9,15 @@ #include <linux/kernel.h> #include <linux/locks.h> #include <linux/fcntl.h> +#include <linux/mm.h> + #include <asm/segment.h> #include <asm/system.h> extern int *blk_size[]; extern int *blksize_size[]; +#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512) #define NBUF 64 int block_write(struct inode * inode, struct file * filp, char * buf, int count) @@ -24,7 +27,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count) loff_t offset; int chars; int written = 0; - int cluster_list[8]; + int cluster_list[MAX_BUF_PER_PAGE]; struct buffer_head * bhlist[NBUF]; int blocks_per_cluster; unsigned int size; @@ -162,7 +165,7 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count) int blocksize_bits, i; unsigned int blocks, rblocks, left; int bhrequest, uptodate; - int cluster_list[8]; + int cluster_list[MAX_BUF_PER_PAGE]; int blocks_per_cluster; struct buffer_head ** bhb, ** bhe; struct buffer_head * buflist[NBUF]; diff --git a/fs/buffer.c b/fs/buffer.c index 9ee47cee6..c2d76a6db 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -16,7 +16,6 @@ * invalidate changed floppy-disk-caches. */ -#include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/major.h> @@ -34,6 +33,7 @@ static char buffersize_index[9] = {-1, 0, 1, -1, 2, -1, -1, -1, 3}; static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096}; #define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9]) +#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512) static int grow_buffers(int pri, int size); static int shrink_specific_buffers(unsigned int priority, int size); @@ -87,7 +87,7 @@ static union bdflush_param{ trim back the buffers */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{25, 500, 64, 256, 15, 3000, 500, 1884, 2}}; +} bdf_prm = {{25, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; /* The lav constant is set for 1 minute, as long as the update process runs every 5 seconds. If you change the frequency of update, the time @@ -259,14 +259,16 @@ void invalidate_buffers(dev_t dev) for(nlist = 0; nlist < NR_LIST; nlist++) { bh = lru_list[nlist]; - for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; - bh = bh->b_next_free) { + for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bh->b_next_free) { if (bh->b_dev != dev) - continue; + continue; wait_on_buffer(bh); - if (bh->b_dev == dev) - bh->b_flushtime = bh->b_uptodate = - bh->b_dirt = bh->b_req = 0; + if (bh->b_dev != dev) + continue; + if (bh->b_count) + continue; + bh->b_flushtime = bh->b_uptodate = + bh->b_dirt = bh->b_req = 0; } } } @@ -712,7 +714,7 @@ repeat: bh = free_list[isize]; remove_from_free_list(bh); -/* OK, FINALLY we know that this buffer is the only one of it's kind, */ +/* OK, FINALLY we know that this buffer is the only one of its kind, */ /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */ bh->b_count=1; bh->b_dirt=0; @@ -742,20 +744,19 @@ void set_writetime(struct buffer_head * buf, int flag) } -static char buffer_disposition[] = {BUF_CLEAN, BUF_SHARED, BUF_LOCKED, BUF_SHARED, - BUF_DIRTY, BUF_DIRTY, BUF_DIRTY, BUF_DIRTY}; - void refile_buffer(struct buffer_head * buf){ - int i, dispose; - i = 0; + int dispose; if(buf->b_dev == 0xffff) panic("Attempt to refile free buffer\n"); - if(mem_map[MAP_NR((unsigned long) buf->b_data)] != 1) i = 1; - if(buf->b_lock) i |= 2; - if(buf->b_dirt) i |= 4; - dispose = buffer_disposition[i]; - if(buf->b_list == BUF_SHARED && dispose == BUF_CLEAN) - dispose = BUF_UNSHARED; - if(dispose == -1) panic("Bad buffer settings (%d)\n", i); + if (buf->b_dirt) + dispose = BUF_DIRTY; + else if (mem_map[MAP_NR((unsigned long) buf->b_data)] > 1) + dispose = BUF_SHARED; + else if (buf->b_lock) + dispose = BUF_LOCKED; + else if (buf->b_list == BUF_SHARED) + dispose = BUF_UNSHARED; + else + dispose = BUF_CLEAN; if(dispose == BUF_CLEAN) buf->b_lru_time = jiffies; if(dispose != buf->b_list) { if(dispose == BUF_DIRTY || dispose == BUF_UNSHARED) @@ -964,7 +965,7 @@ static void read_buffers(struct buffer_head * bh[], int nrbuf) { int i; int bhnum = 0; - struct buffer_head * bhr[8]; + struct buffer_head * bhr[MAX_BUF_PER_PAGE]; for (i = 0 ; i < nrbuf ; i++) { if (bh[i] && !bh[i]->b_uptodate) @@ -972,30 +973,44 @@ static void read_buffers(struct buffer_head * bh[], int nrbuf) } if (bhnum) ll_rw_block(READ, bhnum, bhr); - for (i = 0 ; i < nrbuf ; i++) { + for (i = nrbuf ; --i >= 0 ; ) { if (bh[i]) { wait_on_buffer(bh[i]); } } } +/* + * This actually gets enough info to try to align the stuff, + * but we don't bother yet.. We'll have to check that nobody + * else uses the buffers etc. + * + * "address" points to the new page we can use to move things + * around.. + */ +static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf, + unsigned long address) +{ + while (nrbuf-- > 0) + brelse(bh[nrbuf]); + return 0; +} + static unsigned long check_aligned(struct buffer_head * first, unsigned long address, dev_t dev, int *b, int size) { - struct buffer_head * bh[8]; + struct buffer_head * bh[MAX_BUF_PER_PAGE]; unsigned long page; unsigned long offset; int block; int nrbuf; + int aligned = 1; - page = (unsigned long) first->b_data; - if (page & ~PAGE_MASK) { - brelse(first); - return 0; - } - mem_map[MAP_NR(page)]++; bh[0] = first; nrbuf = 1; + page = (unsigned long) first->b_data; + if (page & ~PAGE_MASK) + aligned = 0; for (offset = size ; offset < PAGE_SIZE ; offset += size) { block = *++b; if (!block) @@ -1005,8 +1020,11 @@ static unsigned long check_aligned(struct buffer_head * first, unsigned long add goto no_go; bh[nrbuf++] = first; if (page+offset != (unsigned long) first->b_data) - goto no_go; + aligned = 0; } + if (!aligned) + return try_to_align(bh, nrbuf, address); + mem_map[MAP_NR(page)]++; read_buffers(bh,nrbuf); /* make sure they are actually read correctly */ while (nrbuf-- > 0) brelse(bh[nrbuf]); @@ -1016,14 +1034,13 @@ static unsigned long check_aligned(struct buffer_head * first, unsigned long add no_go: while (nrbuf-- > 0) brelse(bh[nrbuf]); - free_page(page); return 0; } static unsigned long try_to_load_aligned(unsigned long address, dev_t dev, int b[], int size) { - struct buffer_head * bh, * tmp, * arr[8]; + struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE]; unsigned long offset; int isize = BUFSIZE_INDEX(size); int * p; @@ -1114,7 +1131,7 @@ static inline unsigned long try_to_share_buffers(unsigned long address, */ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, int no_share) { - struct buffer_head * bh[8]; + struct buffer_head * bh[MAX_BUF_PER_PAGE]; unsigned long where; int i, j; @@ -1134,9 +1151,10 @@ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, in for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, where += size) { if (bh[i]) { if (bh[i]->b_uptodate) - memcpy((void *)where, bh[i]->b_data, size); + memcpy((void *) where, bh[i]->b_data, size); brelse(bh[i]); - } + } else + memset((void *) where, 0, size); } return address; } @@ -1196,6 +1214,9 @@ static int grow_buffers(int pri, int size) return 1; } + +/* =========== Reduce the buffer memory ============= */ + /* * try_to_free() checks if all the buffers on this particular page * are unused, and free's the page if so. @@ -1343,7 +1364,7 @@ static int shrink_specific_buffers(unsigned int priority, int size) if(priority > 3 && nlist == BUF_SHARED) continue; bh = lru_list[nlist]; if(!bh) continue; - i = nr_buffers_type[nlist] >> priority; + i = 2*nr_buffers_type[nlist] >> priority; for ( ; i-- > 0 ; bh = bh->b_next_free) { /* We may have stalled while waiting for I/O to complete. */ if(bh->b_list != nlist) goto repeat1; @@ -1371,6 +1392,8 @@ static int shrink_specific_buffers(unsigned int priority, int size) } +/* ================== Debugging =================== */ + void show_buffers(void) { struct buffer_head * bh; @@ -1410,6 +1433,9 @@ void show_buffers(void) } } + +/* ====================== Cluster patches for ext2 ==================== */ + /* * try_to_reassign() checks if all the buffers on this particular page * are unused, and reassign to a new cluster them if this is true. @@ -1435,7 +1461,7 @@ static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head ** } while (tmp != bh); tmp = bh; - while((unsigned int) tmp->b_data & (PAGE_SIZE - 1)) + while((unsigned long) tmp->b_data & (PAGE_SIZE - 1)) tmp = tmp->b_this_page; /* This is the buffer at the head of the page */ @@ -1496,7 +1522,7 @@ static int reassign_cluster(dev_t dev, */ static unsigned long try_to_generate_cluster(dev_t dev, int block, int size) { - struct buffer_head * bh, * tmp, * arr[8]; + struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE]; int isize = BUFSIZE_INDEX(size); unsigned long offset; unsigned long page; @@ -1541,7 +1567,7 @@ static unsigned long try_to_generate_cluster(dev_t dev, int block, int size) bh->b_this_page = tmp; while (nblock-- > 0) brelse(arr[nblock]); - return 4; + return 4; /* ?? */ not_aligned: while ((tmp = bh) != NULL) { bh = bh->b_this_page; @@ -1577,6 +1603,9 @@ unsigned long generate_cluster(dev_t dev, int b[], int size) return reassign_cluster(dev, b[0], size); } + +/* ===================== Init ======================= */ + /* * This initializes the initial buffer free list. nr_buffers_type is set * to one less the actual number of buffers, as a sop to backwards @@ -1587,23 +1616,25 @@ unsigned long generate_cluster(dev_t dev, int b[], int size) void buffer_init(void) { int i; - int isize = BUFSIZE_INDEX(BLOCK_SIZE); + int isize = BUFSIZE_INDEX(BLOCK_SIZE); + unsigned long total_memory; - if (high_memory >= 4*1024*1024) { - if(high_memory >= 16*1024*1024) + total_memory = MAP_NR(high_memory) << PAGE_SHIFT; + if (total_memory >= 4*1024*1024) { + if(total_memory >= 16*1024*1024) nr_hash = 16381; else nr_hash = 4093; } else { nr_hash = 997; - }; + } hash_table = (struct buffer_head **) vmalloc(nr_hash * sizeof(struct buffer_head *)); - buffer_pages = (struct buffer_head **) vmalloc(MAP_NR(high_memory) * sizeof(struct buffer_head *)); + for (i = 0 ; i < MAP_NR(high_memory) ; i++) buffer_pages[i] = NULL; @@ -1616,6 +1647,9 @@ void buffer_init(void) return; } + +/* ====================== bdflush support =================== */ + /* This is a simple kernel daemon, whose job it is to provide a dynamically * response to dirty buffers. Once this process is activated, we write back * a limited number of buffers to the disks and then go back to sleep again. @@ -1632,6 +1666,7 @@ static void wakeup_bdflush(int wait) { if(!bdflush_running){ printk("Warning - bdflush not running\n"); +cli();while(1); sync_buffers(0,0); return; }; @@ -1724,7 +1759,7 @@ asmlinkage int sync_old_buffers(void) * the tuning parameters. We would want to verify each parameter, however, * to make sure that it is reasonable. */ -asmlinkage int sys_bdflush(int func, int data) +asmlinkage int sys_bdflush(int func, long data) { int i, error; int ndirty; @@ -1747,7 +1782,7 @@ asmlinkage int sys_bdflush(int func, int data) error = verify_area(VERIFY_WRITE, (void *) data, sizeof(int)); if (error) return error; - put_fs_long(bdf_prm.data[i], data); + put_user(bdf_prm.data[i], (int*)data); return 0; }; if (data < bdflush_min[i] || data > bdflush_max[i]) @@ -1818,7 +1853,7 @@ asmlinkage int sys_bdflush(int func, int data) /* If there are still a lot of dirty buffers around, skip the sleep and flush some more */ - if(nr_buffers_type[BUF_DIRTY] < (nr_buffers - nr_buffers_type[BUF_SHARED]) * + if(nr_buffers_type[BUF_DIRTY] <= (nr_buffers - nr_buffers_type[BUF_SHARED]) * bdf_prm.b_un.nfract/100) { if (current->signal & (1 << (SIGKILL-1))) { bdflush_running--; diff --git a/fs/dcache.c b/fs/dcache.c index a40bdf316..b236a5cdb 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -29,7 +29,7 @@ * to be short. */ #define DCACHE_NAME_LEN 15 -#define DCACHE_SIZE 64 +#define DCACHE_SIZE 128 struct hash_list { struct dir_cache_entry * next; diff --git a/fs/devices.c b/fs/devices.c index e79ea07d5..3eb269295 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -66,10 +66,6 @@ int register_chrdev(unsigned int major, const char * name, struct file_operation { if (major == 0) { for (major = MAX_CHRDEV-1; major > 0; major--) { - if (chrdevs[major].fops == fops) - return major; - } - for (major = MAX_CHRDEV-1; major > 0; major--) { if (chrdevs[major].fops == NULL) { chrdevs[major].name = name; chrdevs[major].fops = fops; @@ -91,10 +87,6 @@ int register_blkdev(unsigned int major, const char * name, struct file_operation { if (major == 0) { for (major = MAX_BLKDEV-1; major > 0; major--) { - if (blkdevs[major].fops == fops) - return major; - } - for (major = MAX_BLKDEV-1; major > 0; major--) { if (blkdevs[major].fops == NULL) { blkdevs[major].name = name; blkdevs[major].fops = fops; @@ -42,6 +42,9 @@ #include <asm/system.h> #include <asm/segment.h> +#include <asm/pgtable.h> + +#include <linux/config.h> asmlinkage int sys_exit(int exit_code); asmlinkage int sys_brk(unsigned long); @@ -50,6 +53,8 @@ static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs); static int load_aout_library(int fd); static int aout_core_dump(long signr, struct pt_regs * regs); +extern void dump_thread(struct pt_regs *, struct user *); + /* * Here are the actual binaries that will be accepted: * add more with "register_binfmt()".. @@ -106,14 +111,16 @@ int open_inode(struct inode * inode, int mode) return -EINVAL; f = get_empty_filp(); if (!f) - return -EMFILE; + return -ENFILE; fd = 0; fpp = current->files->fd; for (;;) { if (!*fpp) break; - if (++fd > NR_OPEN) - return -ENFILE; + if (++fd >= NR_OPEN) { + f->f_count--; + return -EMFILE; + } fpp++; } *fpp = f; @@ -164,8 +171,7 @@ static int aout_core_dump(long signr, struct pt_regs * regs) unsigned short fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; - int i; - register int dump_start, dump_size; + unsigned long dump_start, dump_size; struct user dump; if (!current->dumpable) @@ -206,55 +212,22 @@ static int aout_core_dump(long signr, struct pt_regs * regs) if (!file.f_op->write) goto close_coredump; has_dumped = 1; -/* changed the size calculations - should hopefully work better. lbt */ - dump.magic = CMAGIC; - dump.start_code = 0; -#if defined (__i386__) - dump.start_stack = regs->esp & ~(PAGE_SIZE - 1); -#elif defined (__mips__) - dump.start_stack = regs->reg29 & ~(PAGE_SIZE - 1); -#endif - dump.u_tsize = ((unsigned long) current->mm->end_code) >> 12; - dump.u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12; - dump.u_dsize -= dump.u_tsize; - dump.u_ssize = 0; - for(i=0; i<8; i++) dump.u_debugreg[i] = current->debugreg[i]; - if (dump.start_stack < TASK_SIZE) - dump.u_ssize = ((unsigned long) (TASK_SIZE - dump.start_stack)) >> 12; + strncpy(dump.u_comm, current->comm, sizeof(current->comm)); + dump.u_ar0 = (struct pt_regs *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump))); + dump.signal = signr; + dump_thread(regs, &dump); + /* If the size of the dump file exceeds the rlimit, then see what would happen if we wrote the stack, but not the data area. */ if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE > current->rlim[RLIMIT_CORE].rlim_cur) dump.u_dsize = 0; + /* Make sure we have enough room to write the stack and data areas. */ if ((dump.u_ssize+1) * PAGE_SIZE > current->rlim[RLIMIT_CORE].rlim_cur) dump.u_ssize = 0; - strncpy(dump.u_comm, current->comm, sizeof(current->comm)); - dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump))); - dump.signal = signr; - dump.regs = *regs; -#if defined (__i386__) -/* Flag indicating the math stuff is valid. We don't support this for the - soft-float routines yet */ - if (hard_math) { - if ((dump.u_fpvalid = current->used_math) != 0) { - if (last_task_used_math == current) - __asm__("clts ; fnsave %0": :"m" (dump.i387)); - else - memcpy(&dump.i387,¤t->tss.i387.hard,sizeof(dump.i387)); - } - } else { - /* we should dump the emulator state here, but we need to - convert it into standard 387 format first.. */ - dump.u_fpvalid = 0; - } -#elif defined (__mips__) - /* - * Dump the MIPS fpa. - * FIXME: not implemented yet. - */ -#endif + set_fs(KERNEL_DS); /* struct user */ DUMP_WRITE(&dump,sizeof(dump)); @@ -335,9 +308,8 @@ unsigned long * create_tables(char * p,int argc,int envc,int ibcs) mpnt->vm_task = current; mpnt->vm_start = PAGE_MASK & (unsigned long) p; mpnt->vm_end = TASK_SIZE; - mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY; + mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; - mpnt->vm_share = NULL; mpnt->vm_ops = NULL; mpnt->vm_offset = 0; mpnt->vm_inode = NULL; @@ -462,7 +434,7 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page, return p; } -unsigned long change_ldt(unsigned long text_size,unsigned long * page) +unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page) { unsigned long code_limit,data_limit,code_base,data_base; int i; @@ -536,7 +508,6 @@ void flush_old_exec(struct linux_binprm * bprm) int i; int ch; char * name; - struct vm_area_struct * mpnt, *mpnt1; current->dumpable = 1; name = bprm->filename; @@ -548,43 +519,14 @@ void flush_old_exec(struct linux_binprm * bprm) current->comm[i++] = ch; } current->comm[i] = '\0'; - /* Release all of the old mmap stuff. */ - mpnt = current->mm->mmap; - current->mm->mmap = NULL; - while (mpnt) { - mpnt1 = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) - mpnt->vm_ops->close(mpnt); - if (mpnt->vm_inode) - iput(mpnt->vm_inode); - kfree(mpnt); - mpnt = mpnt1; - } - -#if defined (__i386__) - /* Flush the old ldt stuff... */ - if (current->ldt) { - free_page((unsigned long) current->ldt); - current->ldt = NULL; - for (i=1 ; i<NR_TASKS ; i++) { - if (task[i] == current) { - set_ldt_desc(gdt+(i<<1)+ - FIRST_LDT_ENTRY,&default_ldt, 1); - load_ldt(i); - } - } - } + /* Release all of the old mmap stuff. */ + exit_mmap(current); - for (i=0 ; i<8 ; i++) current->debugreg[i] = 0; -#elif defined (__mips__) - /* - * Do MIPS specific magic - */ -#endif + flush_thread(); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || - !permission(bprm->inode,MAY_READ)) + permission(bprm->inode,MAY_READ)) current->dumpable = 0; current->signal = 0; for (i=0 ; i<32 ; i++) { @@ -615,10 +557,6 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs int retval; int sh_bang = 0; -#if defined (__i386__) - if (regs->cs != USER_CS) - return -EINVAL; -#endif bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4; for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ bprm.page[i] = 0; @@ -658,8 +596,9 @@ restart_interp: bprm.e_uid = (i & S_ISUID) ? bprm.inode->i_uid : current->euid; bprm.e_gid = (i & S_ISGID) ? bprm.inode->i_gid : current->egid; } - if (!permission(bprm.inode, MAY_EXEC) || - (!(bprm.inode->i_mode & 0111) && fsuser())) { + if ((retval = permission(bprm.inode, MAY_EXEC)) != 0) + goto exec_error2; + if (!(bprm.inode->i_mode & 0111) && fsuser()) { retval = -EACCES; goto exec_error2; } @@ -779,26 +718,6 @@ exec_error1: return(retval); } -/* - * sys_execve() executes a new program. - */ -asmlinkage int sys_execve(struct pt_regs regs) -{ - int error; - char * filename; - -#if defined (__i386__) - error = getname((char *) regs.ebx, &filename); -#elif defined (__mips__) - error = getname((char *) regs.reg3, &filename); -#endif - if (error) - return error; - error = do_execve(filename, (char **) regs.reg4, (char **) regs.reg5, ®s); - putname(filename); - return error; -} - static void set_brk(unsigned long start, unsigned long end) { start = PAGE_ALIGN(start); @@ -915,19 +834,13 @@ beyond_if: set_brk(current->mm->start_brk, current->mm->brk); - p += change_ldt(ex.a_text,bprm->page); + p += setup_arg_pages(ex.a_text,bprm->page); p -= MAX_ARG_PAGES*PAGE_SIZE; p = (unsigned long)create_tables((char *)p, bprm->argc, bprm->envc, current->personality != PER_LINUX); current->mm->start_stack = p; -#if defined (__i386__) - regs->eip = ex.a_entry; /* eip, magic happens :-) */ - regs->esp = p; /* stack pointer */ -#elif defined (__mips__) - regs->cp0_epc = ex.a_entry; /* eip, magic happens :-) */ - regs->reg29 = p; /* stack pointer */ -#endif + start_thread(regs, ex.a_entry, p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); return 0; diff --git a/fs/ext/dir.c b/fs/ext/dir.c index 10e30fafa..f4ae51d91 100644 --- a/fs/ext/dir.c +++ b/fs/ext/dir.c @@ -20,15 +20,12 @@ #include <linux/ext_fs.h> #include <linux/stat.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - static int ext_dir_read(struct inode * inode, struct file * filp, char * buf, int count) { return -EISDIR; } -static int ext_readdir(struct inode *, struct file *, struct dirent *, int); +static int ext_readdir(struct inode *, struct file *, void *, filldir_t); static struct file_operations ext_dir_operations = { NULL, /* lseek - default */ @@ -65,12 +62,11 @@ struct inode_operations ext_dir_inode_operations = { }; static int ext_readdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { + int error; unsigned int i; - unsigned int ret; off_t offset; - char c; struct buffer_head * bh; struct ext_dir_entry * de; @@ -78,8 +74,8 @@ static int ext_readdir(struct inode * inode, struct file * filp, return -EBADF; if ((filp->f_pos & 7) != 0) return -EBADF; - ret = 0; - while (!ret && filp->f_pos < inode->i_size) { + error = 0; + while (!error && filp->f_pos < inode->i_size) { offset = filp->f_pos & 1023; bh = ext_bread(inode,(filp->f_pos)>>BLOCK_SIZE_BITS,0); if (!bh) { @@ -94,7 +90,7 @@ static int ext_readdir(struct inode * inode, struct file * filp, } offset = i; de = (struct ext_dir_entry *) (offset + bh->b_data); - while (!ret && offset < 1024 && filp->f_pos < inode->i_size) { + while (offset < 1024 && filp->f_pos < inode->i_size) { if (de->rec_len < 8 || de->rec_len % 8 != 0 || de->rec_len < de->name_len + 8 || (de->rec_len + (off_t) filp->f_pos - 1) / 1024 > ((off_t) filp->f_pos / 1024)) { @@ -106,26 +102,16 @@ static int ext_readdir(struct inode * inode, struct file * filp, filp->f_pos = inode->i_size; continue; } - offset += de->rec_len; - filp->f_pos += de->rec_len; if (de->inode) { - for (i = 0; i < de->name_len; i++) - if ((c = de->name[i]) != 0) - put_fs_byte(c,i+dirent->d_name); - else - break; - if (i) { - put_fs_long(de->inode,&dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); - ret = ROUND_UP(NAME_OFFSET(dirent)+i+1); + error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode); + if (error) break; - } } - de = (struct ext_dir_entry *) ((char *) de - + de->rec_len); + offset += de->rec_len; + filp->f_pos += de->rec_len; + ((char *) de) += de->rec_len; } brelse(bh); } - return ret; + return 0; } diff --git a/fs/ext/inode.c b/fs/ext/inode.c index b3ca2e2cf..5601becb7 100644 --- a/fs/ext/inode.c +++ b/fs/ext/inode.c @@ -144,21 +144,19 @@ void ext_write_super (struct super_block *sb) sb->s_dirt = 0; } -void ext_statfs (struct super_block *sb, struct statfs *buf) +void ext_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { - long tmp; - - put_fs_long(EXT_SUPER_MAGIC, &buf->f_type); - put_fs_long(1024, &buf->f_bsize); - put_fs_long(sb->u.ext_sb.s_nzones << sb->u.ext_sb.s_log_zone_size, - &buf->f_blocks); - tmp = ext_count_free_blocks(sb); - put_fs_long(tmp, &buf->f_bfree); - put_fs_long(tmp, &buf->f_bavail); - put_fs_long(sb->u.ext_sb.s_ninodes, &buf->f_files); - put_fs_long(ext_count_free_inodes(sb), &buf->f_ffree); - put_fs_long(EXT_NAME_LEN, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + struct statfs tmp; + + tmp.f_type = EXT_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = sb->u.ext_sb.s_nzones << sb->u.ext_sb.s_log_zone_size; + tmp.f_bfree = ext_count_free_blocks(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = sb->u.ext_sb.s_ninodes; + tmp.f_ffree = ext_count_free_inodes(sb); + tmp.f_namelen = EXT_NAME_LEN; + memcpy_tofs(buf, &tmp, bufsiz); } #define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)]) diff --git a/fs/ext/namei.c b/fs/ext/namei.c index 85a411e94..f9e4b8499 100644 --- a/fs/ext/namei.c +++ b/fs/ext/namei.c @@ -285,6 +285,7 @@ printk ("ext_add_entry : creating next block\n"); de->rec_len = rec_len; } dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; de->name_len = namelen; for (i=0; i < namelen ; i++) de->name[i] = name[i]; @@ -810,8 +811,7 @@ start_up: retval = -EEXIST; if (new_bh) goto end_rename; - retval = -EACCES; - if (!permission(old_inode, MAY_WRITE)) + if ((retval = permission(old_inode, MAY_WRITE)) != 0) goto end_rename; retval = -EINVAL; if (subdir(new_dir, old_inode)) diff --git a/fs/ext2/CHANGES b/fs/ext2/CHANGES index b760d18c7..a2495a41e 100644 --- a/fs/ext2/CHANGES +++ b/fs/ext2/CHANGES @@ -1,5 +1,12 @@ Changes from version 0.5 to version 0.5a ======================================== + - Zero the partial block following the end of the file when a file + is truncated. + - Dates updated in the copyright. + - More checks when the filesystem is mounted: the count of blocks, + fragments, and inodes per group is checked against the block size. + - The buffers used by the error routines are now static variables, to + avoid using space on the kernel stack, as requested by Linus. - Some cleanups in the error messages (some versions of syslog contain a bug which truncates an error message if it contains '\n'). - Check that no data can be written to a file past the 2GB limit. diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 91ef7c8cc..0b2701fe6 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/acl.c * - * Copyright (C) 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) */ /* @@ -30,12 +31,12 @@ int ext2_permission (struct inode * inode, int mask) * Nobody gets write access to an immutable file */ if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) - return 0; + return -EACCES; /* * Special case, access is always granted for root */ if (fsuser()) - return 1; + return 0; /* * If no ACL, checks using the file mode */ @@ -44,7 +45,7 @@ int ext2_permission (struct inode * inode, int mask) else if (in_group_p (inode->i_gid)) mode >>= 3; if (((mode & mask & S_IRWXO) == mask)) - return 1; - else return 0; + else + return -EACCES; } diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index bc6faa7ed..c476fa2b2 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/balloc.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 */ @@ -75,7 +76,7 @@ static void read_block_bitmap (struct super_block * sb, ext2_panic (sb, "read_block_bitmap", "Cannot read block bitmap - " "block_group = %d, block_bitmap = %lu", - block_group, gdp->bg_block_bitmap); + block_group, (unsigned long) gdp->bg_block_bitmap); sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh; } @@ -232,7 +233,7 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); mark_buffer_dirty(bh, 1); - if (sb->s_flags & MS_SYNC) { + if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } @@ -249,8 +250,8 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, * bitmap, and then for any free bit if that fails. */ int ext2_new_block (struct super_block * sb, unsigned long goal, - unsigned long * prealloc_count, - unsigned long * prealloc_block) + u32 * prealloc_count, + u32 * prealloc_block) { struct buffer_head * bh; struct buffer_head * bh2; @@ -441,7 +442,7 @@ got_block: j = tmp; mark_buffer_dirty(bh, 1); - if (sb->s_flags & MS_SYNC) { + if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } @@ -577,6 +578,6 @@ void ext2_check_blocks_bitmap (struct super_block * sb) ext2_error (sb, "ext2_check_blocks_bitmap", "Wrong free blocks count in super block, " "stored = %lu, counted = %lu", - es->s_free_blocks_count, bitmap_count); + (unsigned long) es->s_free_blocks_count, bitmap_count); unlock_super (sb); } diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c index 1084da16d..8b9b5d233 100644 --- a/fs/ext2/bitmap.c +++ b/fs/ext2/bitmap.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/bitmap.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) */ #include <linux/fs.h> diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index c98139bc6..5b8bf9cc7 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/dir.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * @@ -22,16 +23,13 @@ #include <linux/sched.h> #include <linux/stat.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - static int ext2_dir_read (struct inode * inode, struct file * filp, char * buf, int count) { return -EISDIR; } -static int ext2_readdir (struct inode *, struct file *, struct dirent *, int); +static int ext2_readdir (struct inode *, struct file *, void *, filldir_t); static struct file_operations ext2_dir_operations = { NULL, /* lseek - default */ @@ -92,16 +90,17 @@ int ext2_check_dir_entry (char * function, struct inode * dir, if (error_msg != NULL) ext2_error (dir->i_sb, function, "bad directory entry: %s\n" "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", - error_msg, offset, de->inode, de->rec_len, + error_msg, offset, (unsigned long) de->inode, de->rec_len, de->name_len); return error_msg == NULL ? 1 : 0; } static int ext2_readdir (struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { + int error = 0; unsigned long offset, blk; - int i, num, stored, dlen; + int i, num, stored; struct buffer_head * bh, * tmp, * bha[16]; struct ext2_dir_entry * de; struct super_block * sb; @@ -115,7 +114,7 @@ static int ext2_readdir (struct inode * inode, struct file * filp, bh = NULL; offset = filp->f_pos & (sb->s_blocksize - 1); - while (count > 0 && !stored && filp->f_pos < inode->i_size) { + while (!error && !stored && filp->f_pos < inode->i_size) { blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); bh = ext2_bread (inode, blk, 0, &err); if (!bh) { @@ -167,7 +166,7 @@ revalidate: filp->f_version = inode->i_version; } - while (count > 0 && filp->f_pos < inode->i_size + while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ext2_dir_entry *) (bh->b_data + offset); if (!ext2_check_dir_entry ("ext2_readdir", inode, de, @@ -179,18 +178,8 @@ revalidate: brelse (bh); return stored; } + offset += de->rec_len; if (de->inode) { - dlen = ROUND_UP(NAME_OFFSET(dirent) - + de->name_len + 1); - /* Old libc libraries always use a - count of 1. */ - if (count == 1 && !stored) - count = dlen; - if (count < dlen) { - count = 0; - break; - } - /* We might block in the next section * if the data destination is * currently swapped out. So, use a @@ -198,22 +187,13 @@ revalidate: * not the directory has been modified * during the copy operation. */ version = inode->i_version; - i = de->name_len; - memcpy_tofs (dirent->d_name, de->name, i); - put_fs_long (de->inode, &dirent->d_ino); - put_fs_byte (0, dirent->d_name + i); - put_fs_word (i, &dirent->d_reclen); - put_fs_long (dlen, &dirent->d_off); + error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode); + if (error) + break; if (version != inode->i_version) goto revalidate; - dcache_add(inode, de->name, de->name_len, - de->inode); - - stored += dlen; - count -= dlen; - ((char *) dirent) += dlen; + stored ++; } - offset += de->rec_len; filp->f_pos += de->rec_len; } offset = 0; @@ -223,5 +203,5 @@ revalidate: inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - return stored; + return 0; } diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 20628b349..9491942c5 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/file.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * @@ -195,9 +196,37 @@ static int ext2_file_read (struct inode * inode, struct file * filp, left -= chars; read += chars; if (*bhe) { +#if 0 +printk("ext2_file_read() #1:\n"); +printk("ext2_file_read() #1.1: ");print_sp(); +#endif memcpy_tofs (buf, offset + (*bhe)->b_data, chars); +#if 0 +printk("ext2_file_read() #2: buf == %08x\n", offset+(*bhe)->b_data); +if(!buf) + { +#if 0 + printk("Flushing caches...\n"); + sys_cacheflush(0, ~0, 3); +#endif + printk("dumping #1 at %08lx...\n", (unsigned long) buf); + dump16(buf); + printk("dumping #2 at %08lx...\n", (unsigned long) (offset+(*bhe)->b_data)); + dump16(offset+(*bhe)->b_data); +#if 0 + printk("Jumping to 0x0\n"); + __asm__ __volatile__ ("jr\t%0;nop"::"r" (0)); + printk("Freezing ...\n"); + while(1); +#endif + } +/*dump_list(0);*/ +#endif brelse (*bhe); +#if 0 +printk("ext2_file_read() #3:\n"); +#endif buf += chars; } else { while (chars-- > 0) diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 2f79c4749..dd3d992c3 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -27,7 +27,7 @@ #define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) #define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) -static int sync_block (struct inode * inode, unsigned long * block, int wait) +static int sync_block (struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; int tmp; @@ -55,7 +55,7 @@ static int sync_block (struct inode * inode, unsigned long * block, int wait) return 0; } -static int sync_iblock (struct inode * inode, unsigned long * iblock, +static int sync_iblock (struct inode * inode, u32 * iblock, struct buffer_head ** bh, int wait) { int rc, tmp; @@ -94,8 +94,7 @@ static int sync_direct (struct inode * inode, int wait) return err; } -static int sync_indirect (struct inode * inode, unsigned long * iblock, - int wait) +static int sync_indirect (struct inode * inode, u32 * iblock, int wait) { int i; struct buffer_head * ind_bh; @@ -107,7 +106,7 @@ static int sync_indirect (struct inode * inode, unsigned long * iblock, for (i = 0; i < addr_per_block; i++) { rc = sync_block (inode, - ((unsigned long *) ind_bh->b_data) + i, + ((u32 *) ind_bh->b_data) + i, wait); if (rc > 0) break; @@ -118,8 +117,7 @@ static int sync_indirect (struct inode * inode, unsigned long * iblock, return err; } -static int sync_dindirect (struct inode * inode, unsigned long * diblock, - int wait) +static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) { int i; struct buffer_head * dind_bh; @@ -131,7 +129,7 @@ static int sync_dindirect (struct inode * inode, unsigned long * diblock, for (i = 0; i < addr_per_block; i++) { rc = sync_indirect (inode, - ((unsigned long *) dind_bh->b_data) + i, + ((u32 *) dind_bh->b_data) + i, wait); if (rc > 0) break; @@ -142,8 +140,7 @@ static int sync_dindirect (struct inode * inode, unsigned long * diblock, return err; } -static int sync_tindirect (struct inode * inode, unsigned long * tiblock, - int wait) +static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) { int i; struct buffer_head * tind_bh; @@ -155,7 +152,7 @@ static int sync_tindirect (struct inode * inode, unsigned long * tiblock, for (i = 0; i < addr_per_block; i++) { rc = sync_dindirect (inode, - ((unsigned long *) tind_bh->b_data) + i, + ((u32 *) tind_bh->b_data) + i, wait); if (rc > 0) break; diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 69c9e2224..9102d02b2 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/ialloc.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * BSD ufs-inspired inode and directory allocation by * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 @@ -74,7 +75,7 @@ static void read_inode_bitmap (struct super_block * sb, ext2_panic (sb, "read_inode_bitmap", "Cannot read inode bitmap - " "block_group = %lu, inode_bitmap = %lu", - block_group, gdp->bg_inode_bitmap); + block_group, (unsigned long) gdp->bg_inode_bitmap); sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh; } @@ -247,7 +248,7 @@ void ext2_free_inode (struct inode * inode) set_inode_dtime (inode, gdp); } mark_buffer_dirty(bh, 1); - if (sb->s_flags & MS_SYNC) { + if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } @@ -358,7 +359,7 @@ repeat: else { /* - * Try to place the inode in it's parent directory + * Try to place the inode in its parent directory */ i = dir->u.ext2_i.i_block_group; tmp = get_group_desc (sb, i, &bh2); @@ -414,7 +415,7 @@ repeat: goto repeat; } mark_buffer_dirty(bh, 1); - if (sb->s_flags & MS_SYNC) { + if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } @@ -476,7 +477,7 @@ repeat: inode->u.ext2_i.i_block_group = i; inode->i_op = NULL; if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) - inode->i_flags |= MS_SYNC; + inode->i_flags |= MS_SYNCHRONOUS; insert_inode_hash(inode); inc_inode_version (inode, gdp, mode); @@ -549,6 +550,6 @@ void ext2_check_inodes_bitmap (struct super_block * sb) ext2_error (sb, "ext2_check_inodes_bitmap", "Wrong free inodes count in super block, " "stored = %lu, counted = %lu", - es->s_free_inodes_count, bitmap_count); + (unsigned long) es->s_free_inodes_count, bitmap_count); unlock_super (sb); } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 7a3c37b79..127f4a9cd 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/inode.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * @@ -24,6 +25,7 @@ #include <linux/stat.h> #include <linux/string.h> #include <linux/locks.h> +#include <linux/mm.h> void ext2_put_inode (struct inode * inode) { @@ -45,7 +47,7 @@ static int block_bmap (struct buffer_head * bh, int nr) if (!bh) return 0; - tmp = ((unsigned long *) bh->b_data)[nr]; + tmp = ((u32 *) bh->b_data)[nr]; brelse (bh); return tmp; } @@ -99,7 +101,7 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal) "cannot get block %lu", result); return 0; } - memset (bh->b_data, 0, inode->i_sb->s_blocksize); + memset(bh->b_data, 0, inode->i_sb->s_blocksize); bh->b_uptodate = 1; mark_buffer_dirty(bh, 1); brelse (bh); @@ -181,8 +183,8 @@ int ext2_bmap (struct inode * inode, int block) static struct buffer_head * inode_getblk (struct inode * inode, int nr, int create, int new_block, int * err) { + u32 * p; int tmp, goal = 0; - unsigned long * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; @@ -249,7 +251,7 @@ static struct buffer_head * block_getblk (struct inode * inode, int new_block, int * err) { int tmp, goal = 0; - unsigned long * p; + u32 * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; @@ -263,7 +265,7 @@ static struct buffer_head * block_getblk (struct inode * inode, return NULL; } } - p = (unsigned long *) bh->b_data + nr; + p = (u32 *) bh->b_data + nr; repeat: tmp = *p; if (tmp) { @@ -286,8 +288,8 @@ repeat: goal = inode->u.ext2_i.i_next_alloc_goal; if (!goal) { for (tmp = nr - 1; tmp >= 0; tmp--) { - if (((unsigned long *) bh->b_data)[tmp]) { - goal = ((unsigned long *)bh->b_data)[tmp]; + if (((u32 *) bh->b_data)[tmp]) { + goal = ((u32 *)bh->b_data)[tmp]; break; } } @@ -324,7 +326,7 @@ static int block_getcluster (struct inode * inode, struct buffer_head * bh, int nr, int blocksize) { - unsigned long * p; + u32 * p; int firstblock = 0; int result = 0; int i; @@ -337,7 +339,7 @@ static int block_getcluster (struct inode * inode, struct buffer_head * bh, if(nr + 3 > EXT2_ADDR_PER_BLOCK(inode->i_sb)) goto out; for(i=0; i< (PAGE_SIZE / inode->i_sb->s_blocksize); i++) { - p = (unsigned long *) bh->b_data + nr + i; + p = (u32 *) bh->b_data + nr + i; /* All blocks in cluster must already be allocated */ if(*p == 0) goto out; @@ -345,9 +347,9 @@ static int block_getcluster (struct inode * inode, struct buffer_head * bh, /* See if aligned correctly */ if(i==0) firstblock = *p; else if(*p != firstblock + i) goto out; - }; + } - p = (unsigned long *) bh->b_data + nr; + p = (u32 *) bh->b_data + nr; result = generate_cluster(bh->b_dev, (int *) p, blocksize); out: @@ -567,7 +569,7 @@ void ext2_read_inode (struct inode * inode) else if (S_ISFIFO(inode->i_mode)) init_fifo(inode); if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) - inode->i_flags |= MS_SYNC; + inode->i_flags |= MS_SYNCHRONOUS; if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) inode->i_flags |= S_APPEND; if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 447968ef0..e0de8de11 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/ioctl.c * - * Copyright (C) 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) */ #include <asm/segment.h> @@ -13,6 +14,7 @@ #include <linux/ext2_fs.h> #include <linux/ioctl.h> #include <linux/sched.h> +#include <linux/mm.h> int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index f56c5404e..6c0b277e2 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/namei.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 37fae41ad..5cd418364 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/super.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * @@ -26,10 +27,11 @@ #include <linux/string.h> #include <linux/locks.h> +static char error_buf[1024]; + void ext2_error (struct super_block * sb, const char * function, const char * fmt, ...) { - char buf[1024]; va_list args; if (!(sb->s_flags & MS_RDONLY)) { @@ -39,15 +41,15 @@ void ext2_error (struct super_block * sb, const char * function, sb->s_dirt = 1; } va_start (args, fmt); - vsprintf (buf, fmt, args); + vsprintf (error_buf, fmt, args); va_end (args); if (test_opt (sb, ERRORS_PANIC) || (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_PANIC && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO))) panic ("EXT2-fs panic (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf); + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); printk (KERN_CRIT "EXT2-fs error (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf); + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); if (test_opt (sb, ERRORS_RO) || (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_RO && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) { @@ -59,7 +61,6 @@ void ext2_error (struct super_block * sb, const char * function, NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, const char * fmt, ...) { - char buf[1024]; va_list args; if (!(sb->s_flags & MS_RDONLY)) { @@ -69,23 +70,22 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, sb->s_dirt = 1; } va_start (args, fmt); - vsprintf (buf, fmt, args); + vsprintf (error_buf, fmt, args); va_end (args); panic ("EXT2-fs panic (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf); + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); } void ext2_warning (struct super_block * sb, const char * function, const char * fmt, ...) { - char buf[1024]; va_list args; va_start (args, fmt); - vsprintf (buf, fmt, args); + vsprintf (error_buf, fmt, args); va_end (args); printk (KERN_WARNING "EXT2-fs warning (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, buf); + MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); } void ext2_put_super (struct super_block * sb) @@ -360,7 +360,7 @@ static int ext2_check_descriptors (struct super_block * sb) ext2_error (sb, "ext2_check_descriptors", "Block bitmap for group %d" " not in group (block %lu)!", - i, gdp->bg_block_bitmap); + i, (unsigned long) gdp->bg_block_bitmap); return 0; } if (gdp->bg_inode_bitmap < block || @@ -369,7 +369,7 @@ static int ext2_check_descriptors (struct super_block * sb) ext2_error (sb, "ext2_check_descriptors", "Inode bitmap for group %d" " not in group (block %lu)!", - i, gdp->bg_inode_bitmap); + i, (unsigned long) gdp->bg_inode_bitmap); return 0; } if (gdp->bg_inode_table < block || @@ -379,7 +379,7 @@ static int ext2_check_descriptors (struct super_block * sb) ext2_error (sb, "ext2_check_descriptors", "Inode table for group %d" " not in group (block %lu)!", - i, gdp->bg_inode_table); + i, (unsigned long) gdp->bg_inode_table); return 0; } block += EXT2_BLOCKS_PER_GROUP(sb); @@ -553,6 +553,31 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, return NULL; } + if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) { + sb->s_dev = 0; + unlock_super (sb); + brelse (bh); + printk ("EXT2-fs: #blocks per group too big: %lu\n", + sb->u.ext2_sb.s_blocks_per_group); + return NULL; + } + if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) { + sb->s_dev = 0; + unlock_super (sb); + brelse (bh); + printk ("EXT2-fs: #fragments per group too big: %lu\n", + sb->u.ext2_sb.s_frags_per_group); + return NULL; + } + if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) { + sb->s_dev = 0; + unlock_super (sb); + brelse (bh); + printk ("EXT2-fs: #inodes per group too big: %lu\n", + sb->u.ext2_sb.s_inodes_per_group); + return NULL; + } + sb->u.ext2_sb.s_groups_count = (es->s_blocks_count - es->s_first_data_block + EXT2_BLOCKS_PER_GROUP(sb) - 1) / @@ -716,11 +741,11 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) return 0; } -void ext2_statfs (struct super_block * sb, struct statfs * buf) +void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { - long tmp; unsigned long overhead; unsigned long overhead_per_group; + struct statfs tmp; if (test_opt (sb, MINIX_DF)) overhead = 0; @@ -737,19 +762,15 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf) sb->u.ext2_sb.s_groups_count * overhead_per_group; } - put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type); - put_fs_long (sb->s_blocksize, &buf->f_bsize); - put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count - overhead, - &buf->f_blocks); - tmp = ext2_count_free_blocks (sb); - put_fs_long (tmp, &buf->f_bfree); - if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count) - put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count, - &buf->f_bavail); - else - put_fs_long (0, &buf->f_bavail); - put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files); - put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree); - put_fs_long (EXT2_NAME_LEN, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + tmp.f_type = EXT2_SUPER_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = sb->u.ext2_sb.s_es->s_blocks_count - overhead; + tmp.f_bfree = ext2_count_free_blocks (sb); + tmp.f_bavail = tmp.f_bfree - sb->u.ext2_sb.s_es->s_r_blocks_count; + if (tmp.f_bfree < sb->u.ext2_sb.s_es->s_r_blocks_count) + tmp.f_bavail = 0; + tmp.f_files = sb->u.ext2_sb.s_es->s_inodes_count; + tmp.f_ffree = ext2_count_free_inodes (sb); + tmp.f_namelen = EXT2_NAME_LEN; + memcpy_tofs(buf, &tmp, bufsiz); } diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 7d85ed74c..66d4ab860 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/symlink.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index 10a1fd236..94a64f3e0 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -1,9 +1,10 @@ /* * linux/fs/ext2/truncate.c * - * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) * * from * @@ -45,8 +46,8 @@ static int ext2_secrm_seed = 152; /* Random generator base */ static int trunc_direct (struct inode * inode) { + u32 * p; int i, tmp; - unsigned long * p; struct buffer_head * bh; unsigned long block_to_free = 0; unsigned long free_count = 0; @@ -102,12 +103,12 @@ repeat: return retry; } -static int trunc_indirect (struct inode * inode, int offset, unsigned long * p) +static int trunc_indirect (struct inode * inode, int offset, u32 * p) { int i, tmp; struct buffer_head * bh; struct buffer_head * ind_bh; - unsigned long * ind; + u32 * ind; unsigned long block_to_free = 0; unsigned long free_count = 0; int retry = 0; @@ -134,7 +135,7 @@ repeat: i = 0; if (i < indirect_block) goto repeat; - ind = i + (unsigned long *) ind_bh->b_data; + ind = i + (u32 *) ind_bh->b_data; tmp = *ind; if (!tmp) continue; @@ -176,7 +177,7 @@ repeat: } if (free_count > 0) ext2_free_blocks (inode->i_sb, block_to_free, free_count); - ind = (unsigned long *) ind_bh->b_data; + ind = (u32 *) ind_bh->b_data; for (i = 0; i < addr_per_block; i++) if (*(ind++)) break; @@ -199,11 +200,11 @@ repeat: } static int trunc_dindirect (struct inode * inode, int offset, - unsigned long * p) + u32 * p) { int i, tmp; struct buffer_head * dind_bh; - unsigned long * dind; + u32 * dind; int retry = 0; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int blocks = inode->i_sb->s_blocksize / 512; @@ -228,7 +229,7 @@ repeat: i = 0; if (i < dindirect_block) goto repeat; - dind = i + (unsigned long *) dind_bh->b_data; + dind = i + (u32 *) dind_bh->b_data; tmp = *dind; if (!tmp) continue; @@ -236,7 +237,7 @@ repeat: dind); mark_buffer_dirty(dind_bh, 1); } - dind = (unsigned long *) dind_bh->b_data; + dind = (u32 *) dind_bh->b_data; for (i = 0; i < addr_per_block; i++) if (*(dind++)) break; @@ -262,7 +263,7 @@ static int trunc_tindirect (struct inode * inode) { int i, tmp; struct buffer_head * tind_bh; - unsigned long * tind, * p; + u32 * tind, * p; int retry = 0; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int blocks = inode->i_sb->s_blocksize / 512; @@ -289,13 +290,13 @@ repeat: i = 0; if (i < tindirect_block) goto repeat; - tind = i + (unsigned long *) tind_bh->b_data; + tind = i + (u32 *) tind_bh->b_data; retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS + addr_per_block + (i + 1) * addr_per_block * addr_per_block, tind); mark_buffer_dirty(tind_bh, 1); } - tind = (unsigned long *) tind_bh->b_data; + tind = (u32 *) tind_bh->b_data; for (i = 0; i < addr_per_block; i++) if (*(tind++)) break; @@ -320,6 +321,9 @@ repeat: void ext2_truncate (struct inode * inode) { int retry; + struct buffer_head * bh; + int err; + int offset; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) @@ -331,10 +335,10 @@ void ext2_truncate (struct inode * inode) down(&inode->i_sem); retry = trunc_direct(inode); retry |= trunc_indirect (inode, EXT2_IND_BLOCK, - (unsigned long *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]); retry |= trunc_dindirect (inode, EXT2_IND_BLOCK + EXT2_ADDR_PER_BLOCK(inode->i_sb), - (unsigned long *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]); retry |= trunc_tindirect (inode); up(&inode->i_sem); if (!retry) @@ -344,6 +348,23 @@ void ext2_truncate (struct inode * inode) current->counter = 0; schedule (); } + /* + * If the file is not being truncated to a block boundary, the + * contents of the partial block following the end of the file must be + * zeroed in case it ever becomes accessible again because of + * subsequent file growth. + */ + offset = inode->i_size % inode->i_sb->s_blocksize; + if (offset) { + bh = ext2_bread (inode, inode->i_size / inode->i_sb->s_blocksize, + 0, &err); + if (bh) { + memset (bh->b_data + offset, 0, + inode->i_sb->s_blocksize - offset); + mark_buffer_dirty (bh, 0); + brelse (bh); + } + } inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; } diff --git a/fs/fcntl.c b/fs/fcntl.c index d3226eb01..12f7cf489 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -63,7 +63,7 @@ asmlinkage int sys_dup(unsigned int fildes) return dupfd(fildes,0); } -asmlinkage int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; struct task_struct *p; @@ -8,6 +8,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/fcntl.h> +#include <linux/mm.h> static int fifo_open(struct inode * inode,struct file * filp) { diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile index 94ab74d5d..fec1d65f4 100644 --- a/fs/hpfs/Makefile +++ b/fs/hpfs/Makefile @@ -14,10 +14,10 @@ .s.o: $(AS) -o $*.o $< -OBJS= hpfs_fs.o +OBJS= hpfs_fs.o hpfs_caps.o hpfs.o: $(OBJS) - ln -f hpfs_fs.o hpfs.o + $(LD) -r -o hpfs.o $(OBJS) dep: $(CPP) -M *.c > .depend diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h index 3121a415d..53d1ab2d5 100644 --- a/fs/hpfs/hpfs.h +++ b/fs/hpfs/hpfs.h @@ -134,8 +134,9 @@ struct hpfs_spare_block /* The code page info pointed to by the spare block consists of an index - block and blocks containing character maps. The following is pretty - sketchy, but Linux doesn't use code pages so it doesn't matter. */ + block and blocks containing uppercasing tables. I don't know what + these are for (CHKDSK, maybe?) -- OS/2 does not seem to use them + itself. Linux doesn't use them either. */ /* block pointed to by spareblock->code_page_dir */ @@ -174,7 +175,7 @@ struct code_page_data unsigned short ix; /* index */ unsigned short code_page_number; /* code page number */ unsigned short zero1; - unsigned char map[128]; /* map for chars 80..ff */ + unsigned char map[128]; /* upcase table for chars 80..ff */ unsigned short zero2; } code_page[3]; unsigned char incognita[78]; @@ -256,10 +257,11 @@ struct hpfs_dirent { time_t creation_date; /* ctime */ unsigned ea_size; /* total EA length, bytes */ unsigned char zero1; - unsigned char locality; /* 0=unk 1=seq 2=random 3=both */ + unsigned char ix; /* code page index (of filename), see + struct code_page_data */ unsigned char namelen, name[1]; /* file name */ /* dnode_secno down; btree down pointer, if present, - follows name on next word boundary, or maybe it's + follows name on next word boundary, or maybe it precedes next dirent, which is on a word boundary. */ }; diff --git a/fs/hpfs/hpfs_caps.c b/fs/hpfs/hpfs_caps.c new file mode 100644 index 000000000..61331c1d2 --- /dev/null +++ b/fs/hpfs/hpfs_caps.c @@ -0,0 +1,170 @@ +/* Capitalization rules for HPFS */ + +/* In OS/2, HPFS filenames preserve upper and lower case letter distinctions + but filename matching ignores case. That is, creating a file "Foo" + actually creates a file named "Foo" which can be looked up as "Foo", + "foo", or "FOO", among other possibilities. + + Also, HPFS is internationalized -- a table giving the uppercase + equivalent of every character is stored in the filesystem, so that + any national character set may be used. If several different + national character sets are in use, several tables are stored + in the filesystem. + + It would be perfectly reasonable for Linux HPFS to act as a Unix + filesystem and match "Foo" only if asked for "Foo" exactly. But + the sort order of HPFS directories is case-insensitive, so Linux + still has to know the capitalization rules used by OS/2. Because + of this, it turns out to be more natural for us to be case-insensitive + than not. + + Currently the standard character set used by Linux is Latin-1. + Work is underway to permit people to use UTF-8 instead, therefore + all code that depends on the character set is segregated here. + + (It would be wonderful if Linux HPFS could be independent of what + character set is in use on the Linux side, but because of the + necessary case folding this is impossible.) + + There is a map from Latin-1 into code page 850 for every printing + character in Latin-1. The NLS documentation of OS/2 shows that + everybody has 850 available unless they don't have Western latin + chars available at all (so fitting them to Linux without Unicode + is a doomed exercise). + + It is not clear exactly how HPFS.IFS handles the situation when + multiple code pages are in use. Experiments show that + + - tables on the disk give uppercasing rules for the installed code pages + + - each directory entry is tagged with what code page was current + when that name was created + + - doing just CHCP, without changing what's on the disk in any way, + can change what DIR reports, and what name a case-folded match + will match. + + This means, I think, that HPFS.IFS operates in the current code + page, without regard to the uppercasing information recorded in + the tables on the disk. It does record the uppercasing rules + it used, perhaps for CHKDSK, but it does not appear to use them + itself. + + So: Linux, a Latin-1 system, will operate in code page 850. We + recode between 850 and Latin-1 when dealing with the names actually + on the disk. We don't use the uppercasing tables either. + + In a hypothetical UTF-8 implementation, one reasonable way to + proceed that matches OS/2 (for least surprise) is: do case + translation in UTF-8, and recode to/from one of the code pages + available on the mounted filesystem. Reject as invalid any name + containing chars that can't be represented on disk by one of the + code pages OS/2 is using. Recoding from on-disk names to UTF-8 + could use the code page tags, though this is not what OS/2 does. */ + +static const unsigned char tb_cp850_to_latin1[128] = +{ + 199, 252, 233, 226, 228, 224, 229, 231, + 234, 235, 232, 239, 238, 236, 196, 197, + 201, 230, 198, 244, 246, 242, 251, 249, + 255, 214, 220, 248, 163, 216, 215, 159, + 225, 237, 243, 250, 241, 209, 170, 186, + 191, 174, 172, 189, 188, 161, 171, 187, + 155, 156, 157, 144, 151, 193, 194, 192, + 169, 135, 128, 131, 133, 162, 165, 147, + 148, 153, 152, 150, 145, 154, 227, 195, + 132, 130, 137, 136, 134, 129, 138, 164, + 240, 208, 202, 203, 200, 158, 205, 206, + 207, 149, 146, 141, 140, 166, 204, 139, + 211, 223, 212, 210, 245, 213, 181, 254, + 222, 218, 219, 217, 253, 221, 175, 180, + 173, 177, 143, 190, 182, 167, 247, 184, + 176, 168, 183, 185, 179, 178, 142, 160, +}; + +#if 0 +static const unsigned char tb_latin1_to_cp850[128] = +{ + 186, 205, 201, 187, 200, 188, 204, 185, + 203, 202, 206, 223, 220, 219, 254, 242, + 179, 196, 218, 191, 192, 217, 195, 180, + 194, 193, 197, 176, 177, 178, 213, 159, + 255, 173, 189, 156, 207, 190, 221, 245, + 249, 184, 166, 174, 170, 240, 169, 238, + 248, 241, 253, 252, 239, 230, 244, 250, + 247, 251, 167, 175, 172, 171, 243, 168, + 183, 181, 182, 199, 142, 143, 146, 128, + 212, 144, 210, 211, 222, 214, 215, 216, + 209, 165, 227, 224, 226, 229, 153, 158, + 157, 235, 233, 234, 154, 237, 232, 225, + 133, 160, 131, 198, 132, 134, 145, 135, + 138, 130, 136, 137, 141, 161, 140, 139, + 208, 164, 149, 162, 147, 228, 148, 246, + 155, 151, 163, 150, 129, 236, 231, 152, +}; +#endif + +#define A_GRAVE 0300 +#define THORN 0336 +#define MULTIPLY 0327 +#define a_grave 0340 +#define thorn 0376 +#define divide 0367 + +static inline unsigned latin1_upcase (unsigned c) +{ + if (c - 'a' <= 'z' - 'a' + || (c - a_grave <= thorn - a_grave + && c != divide)) + return c - 'a' + 'A'; + else + return c; +} + +static inline unsigned latin1_downcase (unsigned c) +{ + if (c - 'A' <= 'Z' - 'A' + || (c - A_GRAVE <= THORN - A_GRAVE + && c != MULTIPLY)) + return c + 'a' - 'A'; + else + return c; +} + +#if 0 +static inline unsigned latin1_to_cp850 (unsigned c) +{ + if ((signed) c - 128 >= 0) + return tb_latin1_to_cp850[c - 128]; + else + return c; +} +#endif + +static inline unsigned cp850_to_latin1 (unsigned c) +{ + if ((signed) c - 128 >= 0) + return tb_cp850_to_latin1[c - 128]; + else + return c; +} + +unsigned hpfs_char_to_upper_linux (unsigned c) +{ + return latin1_upcase (cp850_to_latin1 (c)); +} + +unsigned linux_char_to_upper_linux (unsigned c) +{ + return latin1_upcase (c); +} + +unsigned hpfs_char_to_lower_linux (unsigned c) +{ + return latin1_downcase (cp850_to_latin1 (c)); +} + +unsigned hpfs_char_to_linux (unsigned c) +{ + return cp850_to_latin1 (c); +} diff --git a/fs/hpfs/hpfs_caps.h b/fs/hpfs/hpfs_caps.h new file mode 100644 index 000000000..c4e49e97d --- /dev/null +++ b/fs/hpfs/hpfs_caps.h @@ -0,0 +1,4 @@ +unsigned hpfs_char_to_linux (unsigned c); +unsigned hpfs_char_to_lower_linux (unsigned c); +unsigned hpfs_char_to_upper_linux (unsigned c); +unsigned linux_char_to_upper_linux (unsigned c); diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index c05cf56ab..ec16d8af3 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -25,6 +25,7 @@ #include <asm/segment.h> #include "hpfs.h" +#include "hpfs_caps.h" /* * HPFS is a mixture of 512-byte blocks and 2048-byte blocks. The 2k blocks @@ -117,9 +118,6 @@ /* notation */ -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - #define little_ushort(x) (*(unsigned short *) &(x)) typedef void nonconst; @@ -127,7 +125,7 @@ typedef void nonconst; static void hpfs_read_inode(struct inode *); static void hpfs_put_super(struct super_block *); -static void hpfs_statfs(struct super_block *, struct statfs *); +static void hpfs_statfs(struct super_block *, struct statfs *, int); static int hpfs_remount_fs(struct super_block *, int *, char *); static const struct super_operations hpfs_sops = @@ -186,7 +184,7 @@ static const struct inode_operations hpfs_file_iops = static int hpfs_dir_read(struct inode *inode, struct file *filp, char *buf, int count); static int hpfs_readdir(struct inode *inode, struct file *filp, - struct dirent *dirent, int count); + void *dirent, filldir_t filldir); static int hpfs_lookup(struct inode *, const char *, int, struct inode **); static const struct file_operations hpfs_dir_ops = @@ -247,8 +245,6 @@ static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, struct quad_buffer_head *qbh); static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp, struct quad_buffer_head *qbh); -static void write_one_dirent(struct dirent *dirent, const unsigned char *name, - unsigned namelen, ino_t ino, int lowercase); static dnode_secno dir_subdno(struct inode *inode, unsigned pos); static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno, int n, @@ -726,12 +722,13 @@ static void hpfs_put_super(struct super_block *s) * directory band -- not exactly right but pretty analogous. */ -static void hpfs_statfs(struct super_block *s, struct statfs *buf) +static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) { + struct statfs tmp; + /* * count the bits in the bitmaps, unless we already have */ - if (s->s_hpfs_n_free == -1) { s->s_hpfs_n_free = count_bitmap(s); s->s_hpfs_n_free_dnodes = @@ -741,15 +738,15 @@ static void hpfs_statfs(struct super_block *s, struct statfs *buf) /* * fill in the user statfs struct */ - - put_fs_long(s->s_magic, &buf->f_type); - put_fs_long(512, &buf->f_bsize); - put_fs_long(s->s_hpfs_fs_size, &buf->f_blocks); - put_fs_long(s->s_hpfs_n_free, &buf->f_bfree); - put_fs_long(s->s_hpfs_n_free, &buf->f_bavail); - put_fs_long(s->s_hpfs_dirband_size, &buf->f_files); - put_fs_long(s->s_hpfs_n_free_dnodes, &buf->f_ffree); - put_fs_long(254, &buf->f_namelen); + tmp.f_type = s->s_magic; + tmp.f_bsize = 512; + tmp.f_blocks = s->s_hpfs_fs_size; + tmp.f_bfree = s->s_hpfs_n_free; + tmp.f_bavail = s->s_hpfs_n_free; + tmp.f_files = s->s_hpfs_dirband_size; + tmp.f_ffree = s->s_hpfs_n_free_dnodes; + tmp.f_namelen = 254; + memcpy_tofs(buf, &tmp, bufsiz); } /* @@ -1209,12 +1206,8 @@ static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2, if (n != 0) do { - unsigned c1 = *s1++; - unsigned c2 = *s2++; - if (c1 - 'a' < 26) - c1 -= 040; - if (c2 - 'a' < 26) - c2 -= 040; + unsigned c1 = linux_char_to_upper_linux (*s1++); + unsigned c2 = hpfs_char_to_upper_linux (*s2++); if ((t = c1 - c2) != 0) return t; } while (--n != 0); @@ -1310,6 +1303,11 @@ static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, * fixed, throw this out and just walk the tree and write records into * the user buffer.) * + * [ we now can handle multiple dirents, although the current libc doesn't + * use that. The way hpfs does this is pretty strange, as we need to do + * the name translation etc before calling "filldir()". This is untested, + * as I don't have any hpfs partitions to test against. Linus ] + * * We keep track of our position in the dnode tree with a sort of * dewey-decimal record of subtree locations. Like so: * @@ -1329,82 +1327,84 @@ static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, * we won't have to repeatedly scan the top levels of the tree. */ -static int hpfs_readdir(struct inode *inode, struct file *filp, - struct dirent *dirent, int likely_story) +/* + * Translate the given name: Blam it to lowercase if the mount option said to. + */ + +static void translate_hpfs_name(const unsigned char * from, int len, char * to, int lowercase) +{ + while (len > 0) { + unsigned t = *from; + len--; + if (lowercase) + t = hpfs_char_to_lower_linux (t); + else + t = hpfs_char_to_linux (t); + *to = t; + from++; + to++; + } +} + +static int hpfs_readdir(struct inode *inode, struct file *filp, void * dirent, + filldir_t filldir) { struct quad_buffer_head qbh; struct hpfs_dirent *de; int namelen, lc; ino_t ino; + char * tempname; + long old_pos; if (inode == 0 || inode->i_sb == 0 || !S_ISDIR(inode->i_mode)) return -EBADF; + tempname = (char *) __get_free_page(GFP_KERNEL); + if (!tempname) + return -ENOMEM; + lc = inode->i_sb->s_hpfs_lowercase; + switch ((long) filp->f_pos) { + case -2: + break; - switch ((off_t) filp->f_pos) { case 0: - write_one_dirent(dirent, ".", 1, inode->i_ino, lc); + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) + break; filp->f_pos = -1; - return ROUND_UP(NAME_OFFSET(dirent) + 2); + /* fall through */ case -1: - write_one_dirent(dirent, "..", 2, - inode->i_hpfs_parent_dir, lc); + if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) + break; filp->f_pos = 1; - return ROUND_UP(NAME_OFFSET(dirent) + 3); - - case -2: - return 0; + /* fall through */ default: - de = map_pos_dirent(inode, &filp->f_pos, &qbh); - if (!de) { - filp->f_pos = -2; - return 0; + for (;;) { + old_pos = filp->f_pos; + de = map_pos_dirent(inode, &filp->f_pos, &qbh); + if (!de) { + filp->f_pos = -2; + break; + } + namelen = de->namelen; + translate_hpfs_name(de->name, namelen, tempname, lc); + if (de->directory) + ino = dir_ino(de->fnode); + else + ino = file_ino(de->fnode); + brelse4(&qbh); + if (filldir(dirent, tempname, namelen, old_pos, ino) < 0) { + filp->f_pos = old_pos; + break; + } } - - namelen = de->namelen; - if (de->directory) - ino = dir_ino(de->fnode); - else - ino = file_ino(de->fnode); - write_one_dirent(dirent, de->name, namelen, ino, lc); - brelse4(&qbh); - - return ROUND_UP(NAME_OFFSET(dirent) + namelen + 1); } -} - -/* - * Send the given name and ino off to the user dirent struct at *dirent. - * Blam it to lowercase if the mount option said to. - * - * Note that Linux d_reclen is the length of the file name, and has nothing - * to do with the length of the dirent record. - */ - -static void write_one_dirent(struct dirent *dirent, const unsigned char *name, - unsigned namelen, ino_t ino, int lowercase) -{ - unsigned n; - - put_fs_long(ino, &dirent->d_ino); - put_fs_word(namelen, &dirent->d_reclen); - - if (lowercase) - for (n = namelen; n != 0;) { - unsigned t = name[--n]; - if (t - 'A' < 26) - t += 040; - put_fs_byte(t, &dirent->d_name[n]); - } - else - memcpy_tofs(dirent->d_name, name, namelen); - - put_fs_byte(0, &dirent->d_name[namelen]); + free_page((unsigned long) tempname); + return 0; } /* diff --git a/fs/inode.c b/fs/inode.c index 7278b850e..f32714aca 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -406,12 +406,18 @@ repeat: goto repeat; } inode->i_count--; + if (inode->i_mmap) { + printk("iput: inode %lu on device %d/%d still has mappings.\n", + inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev)); + inode->i_mmap = NULL; + } nr_free_inodes++; return; } struct inode * get_empty_inode(void) { + static int ino = 0; struct inode * inode, * best; int i; @@ -456,6 +462,8 @@ repeat: inode->i_nlink = 1; inode->i_version = ++event; inode->i_sem.count = 1; + inode->i_ino = ++ino; + inode->i_dev = -1; nr_free_inodes--; if (nr_free_inodes < 0) { printk ("VFS: get_empty_inode: bad free inode count.\n"); diff --git a/fs/ioctl.c b/fs/ioctl.c index 22d0f4d10..8931cd60c 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -7,6 +7,7 @@ #include <asm/segment.h> #include <linux/sched.h> +#include <linux/mm.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/stat.h> @@ -41,11 +42,11 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) (long *) arg); return 0; case FIONREAD: - error = verify_area(VERIFY_WRITE,(void *) arg,4); + error = verify_area(VERIFY_WRITE,(void *) arg,sizeof(int)); if (error) return error; put_fs_long(filp->f_inode->i_size - filp->f_pos, - (long *) arg); + (int *) arg); return 0; } if (filp->f_op && filp->f_op->ioctl) diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile index a780af479..bea1ba27c 100644 --- a/fs/isofs/Makefile +++ b/fs/isofs/Makefile @@ -19,6 +19,9 @@ OBJS= namei.o inode.o file.o dir.o util.o rock.o symlink.o isofs.o: $(OBJS) $(LD) -r -o isofs.o $(OBJS) +modules: isofs.o + ln -sf ../fs/isofs/isofs.o $(TOPDIR)/modules + dep: $(CPP) -M *.c > .depend diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index b1934db04..55fc5b9a8 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -8,6 +8,10 @@ * isofs directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <asm/segment.h> @@ -22,12 +26,10 @@ #include <linux/sched.h> #include <linux/locks.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) +static int isofs_readdir(struct inode *, struct file *, void *, filldir_t); -static int isofs_readdir(struct inode *, struct file *, struct dirent *, int); - -static struct file_operations isofs_dir_operations = { +static struct file_operations isofs_dir_operations = +{ NULL, /* lseek - default */ NULL, /* read */ NULL, /* write - bad */ @@ -42,14 +44,15 @@ static struct file_operations isofs_dir_operations = { /* * directories can handle most operations... */ -struct inode_operations isofs_dir_inode_operations = { +struct inode_operations isofs_dir_inode_operations = +{ &isofs_dir_operations, /* default directory file-ops */ - NULL, /* create */ + NULL, /* create */ isofs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ @@ -60,199 +63,209 @@ struct inode_operations isofs_dir_inode_operations = { NULL /* permission */ }; -static int isofs_readdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) +static int parent_inode_number(struct inode * inode, struct iso_directory_record * de) +{ + int inode_number = inode->i_ino; + + if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) + inode_number = inode->u.isofs_i.i_backlink; + + if (inode_number != -1) + return inode_number; + + /* This should never happen, but who knows. Try to be forgiving */ + return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode)); +} + +static int isofs_name_translate(char * old, int len, char * new) +{ + int i, c; + + for (i = 0; i < len; i++) { + c = old[i]; + if (!c) + break; + if (c >= 'A' && c <= 'Z') + c |= 0x20; /* lower case */ + + /* Drop trailing '.;1' (ISO9660:1988 7.5.1 requires period) */ + if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1') + break; + + /* Drop trailing ';1' */ + if (c == ';' && i == len - 2 && old[i + 1] == '1') + break; + + /* Convert remaining ';' to '.' */ + if (c == ';') + c = '.'; + + new[i] = c; + } + return i; +} + +/* + * This should _really_ be cleaned up some day.. + */ +static int do_isofs_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir, + char * tmpname, struct iso_directory_record * tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); - unsigned int block,offset,i, j; - char c = 0; + unsigned int block, offset; int inode_number; - struct buffer_head * bh; - void * cpnt = NULL; - unsigned int old_offset; - int dlen, rrflag; + struct buffer_head *bh; + int len, rrflag; int high_sierra = 0; - char * dpnt, *dpnt1; - struct iso_directory_record * de; - - dpnt1 = NULL; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - + char *name; + struct iso_directory_record *de; + offset = filp->f_pos & (bufsize - 1); - block = isofs_bmap(inode,filp->f_pos>>bufbits); + block = isofs_bmap(inode, filp->f_pos >> bufbits); - if(!block) return 0; + if (!block) + return 0; - if(!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) - return 0; + if (!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) + return 0; while (filp->f_pos < inode->i_size) { + int de_len, next_offset; #ifdef DEBUG printk("Block, offset, f_pos: %x %x %x\n", block, offset, filp->f_pos); #endif de = (struct iso_directory_record *) (bh->b_data + offset); inode_number = (block << bufbits) + (offset & (bufsize - 1)); - + + de_len = *(unsigned char *) de; + /* If the length byte is zero, we should move on to the next CDROM sector. If we are at the end of the directory, we kick out of the while loop. */ - - if (*((unsigned char *) de) == 0) { + + if (de_len == 0) { brelse(bh); - offset = 0; filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE); - block = isofs_bmap(inode,(filp->f_pos)>>bufbits); - if (!block - || !(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, - inode->i_size))) + offset = 0; + block = isofs_bmap(inode, (filp->f_pos) >> bufbits); + if (!block) + return 0; + bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); + if (!bh) return 0; continue; } /* Make sure that the entire directory record is in the current bh block. - If not, we malloc a buffer, and put the two halves together, - so that we can cleanly read the block */ - - old_offset = offset; - offset += *((unsigned char *) de); - filp->f_pos += *((unsigned char *) de); - - if (offset > bufsize) { - unsigned int frag1; - frag1 = bufsize - old_offset; - cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL); - if (!cpnt) return 0; - memcpy(cpnt, bh->b_data + old_offset, frag1); - de = (struct iso_directory_record *) ((char *)cpnt); + If not, put the two halves together in "tmpde" */ + next_offset = offset + de_len; + if (next_offset > bufsize) { + next_offset &= (bufsize - 1); + memcpy(tmpde, de, bufsize - offset); brelse(bh); - offset = filp->f_pos & (bufsize - 1); - block = isofs_bmap(inode,(filp->f_pos)>> bufbits); - if (!block - || !(bh = breada(inode->i_dev, block, bufsize, - filp->f_pos, inode->i_size))) { - kfree(cpnt); + block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits); + if (!block) return 0; - }; - memcpy((char *)cpnt+frag1, bh->b_data, offset); + bh = breada(inode->i_dev, block, bufsize, filp->f_pos+de_len, inode->i_size); + if (!bh) + return 0; + memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset); + de = tmpde; } - - /* Handle the case of the '.' directory */ + offset = next_offset; - rrflag = 0; - i = 1; + /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { - put_fs_byte('.',dirent->d_name); - inode_number = inode->i_ino; - dpnt = "."; + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) + break; + filp->f_pos += de_len; + continue; } - + /* Handle the case of the '..' directory */ - - else if (de->name_len[0] == 1 && de->name[0] == 1) { - put_fs_byte('.',dirent->d_name); - put_fs_byte('.',dirent->d_name+1); - i = 2; - dpnt = ".."; - if((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) - inode_number = inode->u.isofs_i.i_backlink; - else - inode_number = inode->i_ino; - - /* This should never happen, but who knows. Try to be forgiving */ - if(inode_number == -1) { - inode_number = - isofs_lookup_grandparent(inode, - find_rock_ridge_relocation(de, inode)); - if(inode_number == -1){ /* Should never happen */ - printk("Backlink not properly set.\n"); - goto out; - }; - } + if (de->name_len[0] == 1 && de->name[0] == 1) { + inode_number = parent_inode_number(inode, de); + if (inode_number == -1) + break; + if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0) + break; + filp->f_pos += de_len; + continue; } - + /* Handle everything else. Do name translation if there is no Rock Ridge NM field. */ - - else { - /* Do not report hidden or associated files */ - high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; - if (de->flags[-high_sierra] & 5) { - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - continue; - } - dlen = de->name_len[0]; - dpnt = de->name; - i = dlen; - rrflag = get_rock_ridge_filename(de, &dpnt, &dlen, inode); - if (rrflag) { - if (rrflag == -1) { /* This is a rock ridge reloc dir */ - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - continue; - }; - i = dlen; + + if (inode->i_sb->u.isofs_sb.s_unhide == 'n') { + /* Do not report hidden or associated files */ + high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; + if (de->flags[-high_sierra] & 5) { + filp->f_pos += de_len; + continue; } - else - if(inode->i_sb->u.isofs_sb.s_mapping == 'n') { - dpnt1 = dpnt; - dpnt = kmalloc(dlen, GFP_KERNEL); - if (!dpnt) goto out; - for (i = 0; i < dlen && i < NAME_MAX; i++) { - if (!(c = dpnt1[i])) break; - if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */ - if (c == '.' && i == dlen-3 && de->name[i+1] == ';' && de->name[i+2] == '1') - break; /* Drop trailing '.;1' (ISO9660:1988 7.5.1 requires period) */ - if (c == ';' && i == dlen-2 && de->name[i+1] == '1') - break; /* Drop trailing ';1' */ - if (c == ';') c = '.'; /* Convert remaining ';' to '.' */ - dpnt[i] = c; - } - } - for(j=0; j<i; j++) - put_fs_byte(dpnt[j],j+dirent->d_name); /* And save it */ - if(dpnt1) { - kfree(dpnt); - dpnt = dpnt1; + } + + /* Check Rock Ridge name translation.. */ + len = de->name_len[0]; + name = de->name; + rrflag = get_rock_ridge_filename(de, &name, &len, inode); + if (rrflag) { + /* rrflag == 1 means that we have a new name (kmalloced) */ + if (rrflag == 1) { + rrflag = filldir(dirent, name, len, filp->f_pos, inode_number); + kfree(name); /* this was allocated in get_r_r_filename.. */ + if (rrflag < 0) + break; } - - dcache_add(inode, dpnt, i, inode_number); - }; -#if 0 - printk("Nchar: %d\n",i); -#endif + filp->f_pos += de_len; + continue; + } - if (rrflag) kfree(dpnt); - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - - if (i) { - put_fs_long(inode_number, &dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); - brelse(bh); - return ROUND_UP(NAME_OFFSET(dirent) + i + 1); + if (inode->i_sb->u.isofs_sb.s_mapping == 'n') { + len = isofs_name_translate(name, len, tmpname); + if (filldir(dirent, tmpname, len, filp->f_pos, inode_number) < 0) + break; + filp->f_pos += de_len; + continue; } - } - /* We go here for any condition we cannot handle. We also drop through - to here at the end of the directory. */ - out: - if (cpnt) - kfree(cpnt); + + if (filldir(dirent, name, len, filp->f_pos, inode_number) < 0) + break; + + filp->f_pos += de_len; + continue; + } brelse(bh); return 0; } +/* + * Handle allocation of temporary space for name translation and + * handling split directory entries.. The real work is done by + * "do_isofs_readdir()". + */ +static int isofs_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir) +{ + int result; + char * tmpname; + struct iso_directory_record * tmpde; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; + + tmpname = (char *) __get_free_page(GFP_KERNEL); + if (!tmpname) + return -ENOMEM; + tmpde = (struct iso_directory_record *) (tmpname+256); + result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); + free_page((unsigned long) tmpname); + return result; +} diff --git a/fs/isofs/file.c b/fs/isofs/file.c index ee0877d7b..831bfeaf4 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -8,6 +8,10 @@ * isofs regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index c1754e337..db3200bc6 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -6,6 +6,14 @@ * (C) 1991 Linus Torvalds - minix filesystem */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/stat.h> #include <linux/sched.h> #include <linux/iso_fs.h> @@ -16,6 +24,7 @@ #include <linux/locks.h> #include <linux/malloc.h> #include <linux/errno.h> +#include <linux/cdrom.h> #include <asm/system.h> #include <asm/segment.h> @@ -35,6 +44,7 @@ void isofs_put_super(struct super_block *sb) #endif sb->s_dev = 0; unlock_super(sb); + MOD_DEC_USE_COUNT; return; } @@ -53,8 +63,10 @@ struct iso9660_options{ char map; char rock; char cruft; + char unhide; unsigned char conversion; unsigned int blocksize; + mode_t mode; gid_t gid; uid_t uid; }; @@ -66,8 +78,10 @@ static int parse_options(char *options, struct iso9660_options * popt) popt->map = 'n'; popt->rock = 'y'; popt->cruft = 'n'; - popt->conversion = 'a'; + popt->unhide = 'n'; + popt->conversion = 'b'; /* default: no conversion */ popt->blocksize = 1024; + popt->mode = S_IRUGO; popt->gid = 0; popt->uid = 0; if (!options) return 1; @@ -76,6 +90,10 @@ static int parse_options(char *options, struct iso9660_options * popt) popt->rock = 'n'; continue; }; + if (strncmp(this_char,"unhide",6) == 0) { + popt->unhide = 'y'; + continue; + }; if (strncmp(this_char,"cruft",5) == 0) { popt->cruft = 'y'; continue; @@ -100,6 +118,7 @@ static int parse_options(char *options, struct iso9660_options * popt) } else if (value && (!strcmp(this_char,"block") || + !strcmp(this_char,"mode") || !strcmp(this_char,"uid") || !strcmp(this_char,"gid"))) { char * vpnt = value; @@ -122,6 +141,9 @@ static int parse_options(char *options, struct iso9660_options * popt) case 'g': popt->gid = ivalue; break; + case 'm': + popt->mode = ivalue; + break; } } else return 0; @@ -129,14 +151,53 @@ static int parse_options(char *options, struct iso9660_options * popt) return 1; } + +static unsigned int isofs_get_last_session(int dev) +{ + struct cdrom_multisession ms_info; + unsigned int vol_desc_start; + struct inode inode_fake; + extern struct file_operations * get_blkfops(unsigned int); + int i; + + /* + * look if the driver can tell the multi session redirection value + * <emoenke@gwdg.de> + */ + vol_desc_start=0; + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + inode_fake.i_rdev=dev; + ms_info.addr_format=CDROM_LBA; + set_fs(KERNEL_DS); + i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROMMULTISESSION, + (unsigned long) &ms_info); + set_fs(USER_DS); +#if 0 + printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); + if (i==0) + { + printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no"); + printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); + } +#endif 0 + if ((i==0)&&(ms_info.xa_flag)) vol_desc_start=ms_info.addr.lba; + } + return vol_desc_start; +} + struct super_block *isofs_read_super(struct super_block *s,void *data, int silent) { - struct buffer_head *bh; + struct buffer_head *bh=NULL; int iso_blknum; unsigned int blocksize_bits; int high_sierra; int dev=s->s_dev; + unsigned int vol_desc_start; + struct iso_volume_descriptor *vdp; struct hs_volume_descriptor *hdp; @@ -147,8 +208,11 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, struct iso9660_options opt; + MOD_INC_USE_COUNT; + if (!parse_options((char *) data,&opt)) { s->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } @@ -156,6 +220,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, printk("map = %c\n", opt.map); printk("rock = %c\n", opt.rock); printk("cruft = %c\n", opt.cruft); + printk("unhide = %c\n", opt.unhide); printk("conversion = %c\n", opt.conversion); printk("blocksize = %d\n", opt.blocksize); printk("gid = %d\n", opt.gid); @@ -176,12 +241,18 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */ - for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { + vol_desc_start = isofs_get_last_session(dev); + + for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) { +#if 0 + printk("isofs.inode: iso_blknum=%d\n", iso_blknum); +#endif 0 if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) { s->s_dev=0; printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n", dev, iso_blknum); unlock_super(s); + MOD_DEC_USE_COUNT; return NULL; } @@ -214,11 +285,12 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, brelse(bh); } - if(iso_blknum == 100) { + if(iso_blknum == vol_desc_start + 100) { if (!silent) printk("Unable to identify CD-ROM format.\n"); s->s_dev = 0; unlock_super(s); + MOD_DEC_USE_COUNT; return NULL; }; @@ -288,8 +360,14 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 1 : 0); s->u.isofs_sb.s_conversion = opt.conversion; s->u.isofs_sb.s_cruft = opt.cruft; + s->u.isofs_sb.s_unhide = opt.unhide; s->u.isofs_sb.s_uid = opt.uid; s->u.isofs_sb.s_gid = opt.gid; + /* + * It would be incredibly stupid to allow people to mark every file on the disk + * as suid, so we merely allow them to set the default permissions. + */ + s->u.isofs_sb.s_mode = opt.mode & 0777; s->s_blocksize = opt.blocksize; s->s_blocksize_bits = blocksize_bits; s->s_mounted = iget(s, isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size); @@ -298,28 +376,34 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, if (!(s->s_mounted)) { s->s_dev=0; printk("get root inode failed\n"); + MOD_DEC_USE_COUNT; return NULL; } - if(!check_disk_change(s->s_dev)) return s; + if(!check_disk_change(s->s_dev)) { + return s; + } out: /* Kick out for various error conditions */ brelse(bh); s->s_dev = 0; unlock_super(s); + MOD_DEC_USE_COUNT; return NULL; } -void isofs_statfs (struct super_block *sb, struct statfs *buf) +void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { - put_fs_long(ISOFS_SUPER_MAGIC, &buf->f_type); - put_fs_long(1 << ISOFS_BLOCK_BITS, &buf->f_bsize); - put_fs_long(sb->u.isofs_sb.s_nzones, &buf->f_blocks); - put_fs_long(0, &buf->f_bfree); - put_fs_long(0, &buf->f_bavail); - put_fs_long(sb->u.isofs_sb.s_ninodes, &buf->f_files); - put_fs_long(0, &buf->f_ffree); - put_fs_long(NAME_MAX, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + struct statfs tmp; + + tmp.f_type = ISOFS_SUPER_MAGIC; + tmp.f_bsize = 1 << ISOFS_BLOCK_BITS; + tmp.f_blocks = sb->u.isofs_sb.s_nzones; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = sb->u.isofs_sb.s_ninodes; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + memcpy_tofs(buf, &tmp, bufsiz); } int isofs_bmap(struct inode * inode,int block) @@ -378,9 +462,6 @@ void isofs_read_inode(struct inode * inode) raw_inode = ((struct iso_directory_record *) pnt); } - inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ - inode->i_nlink = 1; - if (raw_inode->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 1; /* Set to 1. We know there are 2, but @@ -389,7 +470,7 @@ void isofs_read_inode(struct inode * inode) easier to give 1 which tells find to do it the hard way. */ } else { - inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ + inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */ inode->i_nlink = 1; inode->i_mode |= S_IFREG; /* If there are no periods in the name, then set the execute permission bit */ @@ -705,3 +786,25 @@ void leak_check_brelse(struct buffer_head * bh){ } #endif + +#ifdef MODULE + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type iso9660_fs_type = { + isofs_read_super, "iso9660", 1, NULL +}; + +int init_module(void) +{ + register_filesystem(&iso9660_fs_type); + return 0; +} + +void cleanup_module(void) +{ + unregister_filesystem(&iso9660_fs_type); +} + +#endif + diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 1473b1b7f..02d01171f 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -6,6 +6,10 @@ * (C) 1991 Linus Torvalds - minix filesystem */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/iso_fs.h> #include <linux/kernel.h> @@ -69,7 +73,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, unsigned int old_offset; unsigned int backlink; int dlen, rrflag, match; - int high_sierra = 0; char * dpnt; struct iso_directory_record * de; char c; @@ -151,16 +154,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, backlink = 0; } - /* Do not report hidden or associated files */ - high_sierra = dir->i_sb->u.isofs_sb.s_high_sierra; - if (de->flags[-high_sierra] & 5) { - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - continue; - } - dlen = de->name_len[0]; dpnt = de->name; /* Now convert the filename in the buffer to lower case */ diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 686c1d910..b902d8f24 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -5,7 +5,10 @@ * * Rock Ridge Extensions to iso9660 */ -#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/stat.h> #include <linux/sched.h> #include <linux/iso_fs.h> @@ -302,7 +305,18 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, { int high, low; high = isonum_733(rr->u.PN.dev_high); low = isonum_733(rr->u.PN.dev_low); - inode->i_rdev = ((high << 8) | (low & 0xff)) & 0xffff; + /* + * The Rock Ridge standard specifies that if sizeof(dev_t) <=4, + * then the high field is unused, and the device number is completely + * stored in the low field. Some writers may ignore this subtlety, + * and as a result we test to see if the entire device number is + * stored in the low field, and use that. + */ + if(MINOR(low) != low && high == 0) { + inode->i_rdev = low; + } else { + inode->i_rdev = MKDEV(high, low); + } }; break; case SIG('T','F'): diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c index fa4a45ba6..d159e424f 100644 --- a/fs/isofs/symlink.c +++ b/fs/isofs/symlink.c @@ -9,6 +9,10 @@ * extensions to iso9660 */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> diff --git a/fs/isofs/util.c b/fs/isofs/util.c index dbeee868d..ba432b391 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -9,6 +9,10 @@ * the bsd386 iso9660 filesystem, by Pace Williamson. */ +#ifdef MODULE +#include <linux/module.h> +#endif + int isonum_711 (char * p) diff --git a/fs/locks.c b/fs/locks.c index d1de73ad0..9b64fa185 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2,23 +2,61 @@ * linux/fs/locks.c * * Provide support for fcntl()'s F_GETLK, F_SETLK, and F_SETLKW calls. - * Doug Evans, 92Aug07, dje@sspiff.uucp. + * Doug Evans (dje@spiff.uucp), August 07, 1992 * - * Deadlock Detection added by Kelly Carmichael, kelly@[142.24.8.65] - * September 17, 1994. - * - * FIXME: one thing isn't handled yet: + * Deadlock detection added. + * FIXME: one thing isn't handled yet: * - mandatory locks (requires lots of changes elsewhere) + * Kelly Carmichael (kelly@[142.24.8.65]), September 17, 1994. * - * Edited by Kai Petzke, wpp@marie.physik.tu-berlin.de - * + * Miscellaneous edits, and a total rewrite of posix_lock_file() code. + * Kai Petzke (wpp@marie.physik.tu-berlin.de), 1994 + * * Converted file_lock_table to a linked list from an array, which eliminates - * the limits on how many active file locks are open - Chad Page - * (pageone@netcom.com), November 27, 1994 + * the limits on how many active file locks are open. + * Chad Page (pageone@netcom.com), November 27, 1994 + * + * Removed dependency on file descriptors. dup()'ed file descriptors now + * get the same locks as the original file descriptors, and a close() on + * any file descriptor removes ALL the locks on the file for the current + * process. Since locks still depend on the process id, locks are inherited + * after an exec() but not after a fork(). This agrees with POSIX, and both + * BSD and SVR4 practice. + * Andy Walker (andy@keo.kvaerner.no), February 14, 1995 + * + * Scrapped free list which is redundant now that we allocate locks + * dynamically with kmalloc()/kfree(). + * Andy Walker (andy@keo.kvaerner.no), February 21, 1995 + * + * Implemented two lock personalities - F_FLOCK and F_POSIX. + * + * F_POSIX locks are created with calls to fcntl() and lockf() through the + * fcntl() system call. They have the semantics described above. + * + * F_FLOCK locks are created with calls to flock(), through the flock() + * system call, which is new. Old C libraries implement flock() via fcntl() + * and will continue to use the old, broken implementation. + * + * F_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated + * with a file pointer (filp). As a result they can be shared by a parent + * process and its children after a fork(). They are removed when the last + * file descriptor referring to the file pointer is closed (unless explicitly + * unlocked). + * + * F_FLOCK locks never deadlock, an existing lock is always removed before + * upgrading from shared to exclusive (or vice versa). When this happens + * any processes blocked by the current lock are woken up and allowed to + * run before the new lock is applied. + * + * NOTE: + * I do not intend to implement mandatory locks unless demand is *HUGE*. + * They are not in BSD, and POSIX.1 does not require them. I have never + * seen any public code that relied on them. As Kelly Carmichael suggests + * above, mandatory locks requires lots of changes elsewhere and I am + * reluctant to start something so drastic for so little gain. + * Andy Walker (andy@keo.kvaerner.no), June 09, 1995 */ -#define DEADLOCK_DETECTION - #include <asm/segment.h> #include <linux/malloc.h> @@ -30,21 +68,53 @@ #define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */ -static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l, - unsigned int fd); -static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); -static int overlap(struct file_lock *fl1, struct file_lock *fl2); -static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd); -static struct file_lock *alloc_lock(struct file_lock **pos, struct file_lock *fl, - unsigned int fd); -static void free_lock(struct file_lock **fl); -#ifdef DEADLOCK_DETECTION -int locks_deadlocked(int my_pid,int blocked_pid); -#endif +static int flock_make_lock(struct file *filp, struct file_lock *fl, + unsigned int cmd); +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l); +static int flock_locks_conflict(struct file_lock *caller_fl, + struct file_lock *sys_fl); +static int posix_locks_conflict(struct file_lock *caller_fl, + struct file_lock *sys_fl); +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); +static int flock_lock_file(struct file *filp, struct file_lock *caller, + unsigned int wait); +static int posix_lock_file(struct file *filp, struct file_lock *caller, + unsigned int wait); +static int posix_locks_deadlock(struct task_struct *my_task, + struct task_struct *blocked_task); +static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2); + +static struct file_lock *locks_alloc_lock(struct file_lock *fl); +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl); +static void locks_delete_lock(struct file_lock **fl, unsigned int wait); +static void locks_insert_block(struct file_lock **block, struct file_lock *fl); static struct file_lock *file_lock_table = NULL; -static struct file_lock *file_lock_free_list = NULL; +/* flock() system call entry point. Apply a FLOCK style locks to + * an open file descriptor. + */ +asmlinkage int sys_flock(unsigned int fd, unsigned int cmd) +{ + struct file_lock file_lock; + struct file *filp; + + if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) + return (-EBADF); + + if (!flock_make_lock(filp, &file_lock, cmd)) + return (-EINVAL); + + if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3)) + return (-EBADF); + + return (flock_lock_file(filp, &file_lock, cmd & LOCK_UN ? 0 : cmd & LOCK_NB ? 0 : 1)); +} + +/* Report the first existing locks that would conflict with l. This implements + * the F_GETLK command of fcntl(). + */ int fcntl_getlk(unsigned int fd, struct flock *l) { int error; @@ -52,59 +122,65 @@ int fcntl_getlk(unsigned int fd, struct flock *l) struct file *filp; struct file_lock *fl,file_lock; - if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) - return -EBADF; - error = verify_area(VERIFY_WRITE,l, sizeof(*l)); + if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) + return (-EBADF); + error = verify_area(VERIFY_WRITE, l, sizeof(*l)); if (error) - return error; + return (error); + memcpy_fromfs(&flock, l, sizeof(flock)); - if (flock.l_type == F_UNLCK) - return -EINVAL; - if (!copy_flock(filp, &file_lock, &flock, fd)) - return -EINVAL; + if ((flock.l_type == F_UNLCK) || (flock.l_type == F_EXLCK) || + (flock.l_type == F_SHLCK)) + return (-EINVAL); + + if (!posix_make_lock(filp, &file_lock, &flock)) + return (-EINVAL); for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (conflict(&file_lock, fl)) { + if (posix_locks_conflict(&file_lock, fl)) { flock.l_pid = fl->fl_owner->pid; flock.l_start = fl->fl_start; flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : fl->fl_end - fl->fl_start + 1; - flock.l_whence = fl->fl_whence; + flock.l_whence = 0; flock.l_type = fl->fl_type; memcpy_tofs(l, &flock, sizeof(flock)); - return 0; + return (0); } } flock.l_type = F_UNLCK; /* no conflict found */ memcpy_tofs(l, &flock, sizeof(flock)); - return 0; + return (0); } -/* - * This function implements both F_SETLK and F_SETLKW. +/* Apply the lock described by l to an open file descriptor. This implements + * both the F_SETLK and F_SETLKW commands of fcntl(). It also emulates flock() + * in a pretty broken way for older C libraries. */ - int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { int error; struct file *filp; - struct file_lock *fl,file_lock; + struct file_lock file_lock; struct flock flock; /* * Get arguments and validate them ... */ - if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) - return -EBADF; + if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) + return (-EBADF); + error = verify_area(VERIFY_READ, l, sizeof(*l)); if (error) - return error; + return (error); + memcpy_fromfs(&flock, l, sizeof(flock)); - if (!copy_flock(filp, &file_lock, &flock, fd)) - return -EINVAL; - switch (file_lock.fl_type) { + if (!posix_make_lock(filp, &file_lock, &flock)) + return (-EINVAL); + + switch (flock.l_type) { case F_RDLCK : if (!(filp->f_mode & 1)) return -EBADF; @@ -114,178 +190,287 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) return -EBADF; break; case F_SHLCK : - if (!(filp->f_mode & 3)) - return -EBADF; - file_lock.fl_type = F_RDLCK; - break; case F_EXLCK : if (!(filp->f_mode & 3)) return -EBADF; - file_lock.fl_type = F_WRLCK; break; case F_UNLCK : break; } - - /* - * Scan for a conflicting lock ... - */ - - if (file_lock.fl_type != F_UNLCK) { -repeat: - for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!conflict(&file_lock, fl)) - continue; - /* - * File is locked by another process. If this is - * F_SETLKW wait for the lock to be released. - */ - if (cmd == F_SETLKW) { - if (current->signal & ~current->blocked) - return -ERESTARTSYS; -#ifdef DEADLOCK_DETECTION - if (locks_deadlocked(file_lock.fl_owner->pid,fl->fl_owner->pid)) return -EDEADLOCK; -#endif - interruptible_sleep_on(&fl->fl_wait); - if (current->signal & ~current->blocked) - return -ERESTARTSYS; - goto repeat; - } - return -EAGAIN; - } - } - - /* - * Lock doesn't conflict with any other lock ... - */ - - return lock_it(filp, &file_lock, fd); -} - -#ifdef DEADLOCK_DETECTION -/* - * This function tests for deadlock condition before putting a process to sleep - * this detection scheme is recursive... we may need some test as to make it - * exit if the function gets stuck due to bad lock data. - */ - -int locks_deadlocked(int my_pid,int blocked_pid) -{ - int ret_val; - struct wait_queue *dlock_wait; - struct file_lock *fl; - for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { - if (fl->fl_owner == NULL) continue; /* not a used lock */ - if (fl->fl_owner->pid != my_pid) continue; - if (fl->fl_wait == NULL) continue; /* no queues */ - dlock_wait = fl->fl_wait; - do { - if (dlock_wait->task != NULL) { - if (dlock_wait->task->pid == blocked_pid) return -EDEADLOCK; - ret_val = locks_deadlocked(dlock_wait->task->pid,blocked_pid); - if (ret_val) return -EDEADLOCK; - } - dlock_wait = dlock_wait->next; - } while (dlock_wait != NULL); - } - return 0; + + return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW)); } -#endif -/* - * This function is called when the file is closed. +/* This function is called when the file is closed. */ - -void fcntl_remove_locks(struct task_struct *task, struct file *filp, - unsigned int fd) +void locks_remove_locks(struct task_struct *task, struct file *filp) { struct file_lock *fl; struct file_lock **before; - /* Find first lock owned by caller ... */ - + /* For POSIX locks we free all locks on this file for the given task. + * For FLOCK we only free locks on this *open* file if it is the last + * close on that file. + */ before = &filp->f_inode->i_flock; - while ((fl = *before) && (task != fl->fl_owner || fd != fl->fl_fd)) - before = &fl->fl_next; - - /* The list is sorted by owner and fd ... */ + while ((fl = *before) != NULL) { + if (((fl->fl_flags == F_POSIX) && (fl->fl_owner == task)) || + ((fl->fl_flags == F_FLOCK) && (fl->fl_file == filp) && + (filp->f_count == 1))) + locks_delete_lock(before, 0); + else + before = &fl->fl_next; + } - while ((fl = *before) && task == fl->fl_owner && fd == fl->fl_fd) - free_lock(before); + return; } -/* - * Verify a "struct flock" and copy it to a "struct file_lock" ... - * Result is a boolean indicating success. +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX + * style lock. */ - -static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l, - unsigned int fd) +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l) { off_t start; if (!filp->f_inode) /* just in case */ - return 0; - if (l->l_type != F_UNLCK && l->l_type != F_RDLCK && l->l_type != F_WRLCK - && l->l_type != F_SHLCK && l->l_type != F_EXLCK) - return 0; + return (0); + + switch (l->l_type) { + case F_RDLCK : + case F_WRLCK : + case F_UNLCK : + fl->fl_type = l->l_type; + break; + case F_SHLCK : + fl->fl_type = F_RDLCK; + break; + case F_EXLCK : + fl->fl_type = F_WRLCK; + break; + default : + return (0); + } + switch (l->l_whence) { - case 0 /*SEEK_SET*/ : start = 0; break; - case 1 /*SEEK_CUR*/ : start = filp->f_pos; break; - case 2 /*SEEK_END*/ : start = filp->f_inode->i_size; break; - default : return 0; + case 0 : /*SEEK_SET*/ + start = 0; + break; + case 1 : /*SEEK_CUR*/ + start = filp->f_pos; + break; + case 2 : /*SEEK_END*/ + start = filp->f_inode->i_size; + break; + default : + return (0); } - if ((start += l->l_start) < 0 || l->l_len < 0) - return 0; - fl->fl_type = l->l_type; + + if (((start += l->l_start) < 0) || (l->l_len < 0)) + return (0); fl->fl_start = start; /* we record the absolute position */ - fl->fl_whence = 0; /* FIXME: do we record {l_start} as passed? */ - if (l->l_len == 0 || (fl->fl_end = start + l->l_len - 1) < 0) + if ((l->l_len == 0) || ((fl->fl_end = start + l->l_len - 1) < 0)) fl->fl_end = OFFSET_MAX; + + fl->fl_flags = F_POSIX; + fl->fl_file = filp; fl->fl_owner = current; - fl->fl_fd = fd; fl->fl_wait = NULL; /* just for cleanliness */ - return 1; + + return (1); } -/* - * Determine if lock {sys_fl} blocks lock {caller_fl} ... +/* Verify a call to flock() and fill in a file_lock structure with an appropriate + * FLOCK lock. */ +static int flock_make_lock(struct file *filp, struct file_lock *fl, + unsigned int cmd) +{ + if (!filp->f_inode) /* just in case */ + return (0); + + switch (cmd & ~LOCK_NB) { + case LOCK_SH : + fl->fl_type = F_RDLCK; + break; + case LOCK_EX : + fl->fl_type = F_WRLCK; + break; + case LOCK_UN : + fl->fl_type = F_UNLCK; + break; + default : + return (0); + } + + fl->fl_flags = F_FLOCK; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_file = filp; + fl->fl_owner = current; + fl->fl_wait = NULL; /* just for cleanliness */ + + return (1); +} -static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific checking + * before calling the locks_conflict(). + */ +static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - if ( caller_fl->fl_owner == sys_fl->fl_owner - && caller_fl->fl_fd == sys_fl->fl_fd) - return 0; - if (!overlap(caller_fl, sys_fl)) - return 0; + /* POSIX locks owned by the same process do not conflict with + * each other. + */ + if ((sys_fl->fl_flags == F_POSIX) && + (caller_fl->fl_owner == sys_fl->fl_owner)) + return (0); + + return (locks_conflict(caller_fl, sys_fl)); +} + +/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking + * before calling the locks_conflict(). + */ +static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + /* FLOCK locks referring to the same filp do not conflict with + * each other. + */ + if ((sys_fl->fl_flags == F_FLOCK) && + (caller_fl->fl_file == sys_fl->fl_file)) + return (0); + + return (locks_conflict(caller_fl, sys_fl)); +} + +/* Determine if lock sys_fl blocks lock caller_fl. Common functionality + * checks for overlapping locks and shared/exclusive status. + */ +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + if (!locks_overlap(caller_fl, sys_fl)) + return (0); + switch (caller_fl->fl_type) { case F_RDLCK : - return sys_fl->fl_type != F_RDLCK; + return (sys_fl->fl_type == F_WRLCK); + case F_WRLCK : - return 1; /* overlapping region not owned by caller */ + return (1); + + default: + printk("locks_conflict(): impossible lock type - %d\n", + caller_fl->fl_type); + break; } - return 0; /* shouldn't get here, but just in case */ + return (0); /* This should never happen */ } -static int overlap(struct file_lock *fl1, struct file_lock *fl2) +/* Check if two locks overlap each other. + */ +static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) { - return fl1->fl_end >= fl2->fl_start && fl2->fl_end >= fl1->fl_start; + return ((fl1->fl_end >= fl2->fl_start) && + (fl2->fl_end >= fl1->fl_start)); } -/* - * Add a lock to a file ... - * Result is 0 for success or -ENOLCK. - * - * We merge adjacent locks whenever possible. - * - * WARNING: We assume the lock doesn't conflict with any other lock. +/* This function tests for deadlock condition before putting a process to sleep. + * The detection scheme is recursive... we may need a test to make it exit if the + * function gets stuck due to bad lock data. 4.4 BSD uses a maximum depth of 50 + * for this. */ - -/* - * Rewritten by Kai Petzke: - * We sort the lock list first by owner, then by the starting address. +static int posix_locks_deadlock(struct task_struct *my_task, + struct task_struct *blocked_task) +{ + struct wait_queue *dlock_wait; + struct file_lock *fl; + + for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { + if (fl->fl_owner == NULL) + continue; /* Should never happen! */ + if (fl->fl_owner != my_task) + continue; + if (fl->fl_wait == NULL) + continue; /* no queues */ + dlock_wait = fl->fl_wait; + do { + if (dlock_wait->task != NULL) { + if (dlock_wait->task == blocked_task) + return (-EDEADLOCK); + if (posix_locks_deadlock(dlock_wait->task, blocked_task)) + return (-EDEADLOCK); + } + dlock_wait = dlock_wait->next; + } while (dlock_wait != fl->fl_wait); + } + return (0); +} + +/* Try to create a FLOCK lock on filp. We rely on FLOCK locks being sorting + * first in an inode's lock list, and always insert new locks at the head + * of the list. + */ +static int flock_lock_file(struct file *filp, struct file_lock *caller, + unsigned int wait) +{ + struct file_lock *fl; + struct file_lock *new_fl; + struct file_lock **before; + int change = 0; + + /* This a compact little algorithm based on us always placing FLOCK + * locks at the front of the list. + */ + before = &filp->f_inode->i_flock; + while ((fl = *before) && (fl->fl_flags == F_FLOCK)) { + if (caller->fl_file == fl->fl_file) { + if (caller->fl_type == fl->fl_type) + return (0); + change = 1; + break; + } + before = &fl->fl_next; + } + /* change means that we are changing the type of an existing lock, or + * or else unlocking it. + */ + if (change) + locks_delete_lock(before, caller->fl_type != F_UNLCK); + if (caller->fl_type == F_UNLCK) + return (0); + if ((new_fl = locks_alloc_lock(caller)) == NULL) + return (-ENOLCK); + repeat: + for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!flock_locks_conflict(new_fl, fl)) + continue; + + if (wait) { + if (current->signal & ~current->blocked) { + locks_delete_lock(&new_fl, 0); + return (-ERESTARTSYS); + } + locks_insert_block(&fl->fl_block, new_fl); + interruptible_sleep_on(&new_fl->fl_wait); + wake_up(&new_fl->fl_wait); + if (current->signal & ~current->blocked) { + locks_delete_lock(&new_fl, 0); + return (-ERESTARTSYS); + } + goto repeat; + } + locks_delete_lock(&new_fl, 0); + return (-EAGAIN); + } + locks_insert_lock(&filp->f_inode->i_flock, new_fl); + return (0); +} + +/* Add a POSIX style lock to a file. + * We merge adjacent locks whenever possible. POSIX locks come after FLOCK + * locks in the list and are sorted by owner task, then by starting address * + * Kai Petzke writes: * To make freeing a lock much faster, we keep a pointer to the lock before the * actual one. But the real gain of the new coding was, that lock_it() and * unlock_it() became one function. @@ -293,46 +478,62 @@ static int overlap(struct file_lock *fl1, struct file_lock *fl2) * To all purists: Yes, I use a few goto's. Just pass on to the next function. */ -static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd) +static int posix_lock_file(struct file *filp, struct file_lock *caller, + unsigned int wait) { struct file_lock *fl; - struct file_lock *left = 0; - struct file_lock *right = 0; + struct file_lock *new_fl; + struct file_lock *left = NULL; + struct file_lock *right = NULL; struct file_lock **before; int added = 0; + if (caller->fl_type != F_UNLCK) { +repeat: + for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!posix_locks_conflict(caller, fl)) + continue; + if (wait) { + if (current->signal & ~current->blocked) + return (-ERESTARTSYS); + if (fl->fl_flags == F_POSIX) + if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner)) + return (-EDEADLOCK); + interruptible_sleep_on(&fl->fl_wait); + if (current->signal & ~current->blocked) + return (-ERESTARTSYS); + goto repeat; + } + return (-EAGAIN); + } + } /* * Find the first old lock with the same owner as the new lock. */ - + before = &filp->f_inode->i_flock; - while ((fl = *before) && - (caller->fl_owner != fl->fl_owner || - caller->fl_fd != fl->fl_fd)) - before = &fl->fl_next; - /* - * Look up all locks of this owner. + /* First skip FLOCK locks and locks owned by other processes. */ + while ((fl = *before) && ((fl->fl_flags == F_FLOCK) || + (caller->fl_owner != fl->fl_owner))) + before = &fl->fl_next; - while ( (fl = *before) - && caller->fl_owner == fl->fl_owner - && caller->fl_fd == fl->fl_fd) { - /* - * Detect adjacent or overlapping regions (if same lock type) + /* Process locks with this owner. + */ + while ((fl = *before) && (caller->fl_owner == fl->fl_owner)) { + /* Detect adjacent or overlapping regions (if same lock type) */ if (caller->fl_type == fl->fl_type) { if (fl->fl_end < caller->fl_start - 1) goto next_lock; - /* - * If the next lock in the list has entirely bigger + /* If the next lock in the list has entirely bigger * addresses than the new one, insert the lock here. */ if (fl->fl_start > caller->fl_end + 1) break; - /* - * If we come here, the new and old lock are of the + /* If we come here, the new and old lock are of the * same type and adjacent or overlapping. Make one * lock yielding from the lower start address of both * locks to the higher end address. @@ -346,15 +547,14 @@ static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd) else caller->fl_end = fl->fl_end; if (added) { - free_lock(before); + locks_delete_lock(before, 0); continue; } caller = fl; added = 1; goto next_lock; } - /* - * Processing for different lock types is a bit more complex. + /* Processing for different lock types is a bit more complex. */ if (fl->fl_end < caller->fl_start) goto next_lock; @@ -364,8 +564,7 @@ static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd) added = 1; if (fl->fl_start < caller->fl_start) left = fl; - /* - * If the next lock in the list has a higher end address than + /* If the next lock in the list has a higher end address than * the new one, insert the new one here. */ if (fl->fl_end > caller->fl_end) { @@ -373,134 +572,162 @@ static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd) break; } if (fl->fl_start >= caller->fl_start) { - /* - * The new lock completely replaces an old one (This may + /* The new lock completely replaces an old one (This may * happen several times). */ if (added) { - free_lock(before); + locks_delete_lock(before, 0); continue; } - /* - * Replace the old lock with the new one. Wake up + /* Replace the old lock with the new one. Wake up * anybody waiting for the old one, as the change in * lock type might satisfy his needs. */ wake_up(&fl->fl_wait); fl->fl_start = caller->fl_start; - fl->fl_end = caller->fl_end; - fl->fl_type = caller->fl_type; + fl->fl_end = caller->fl_end; + fl->fl_type = caller->fl_type; caller = fl; added = 1; } - /* - * Go on to next lock. + /* Go on to next lock. */ -next_lock: + next_lock: before = &(*before)->fl_next; } - if (! added) { - if (caller->fl_type == F_UNLCK) { -/* - * XXX - under iBCS-2, attempting to unlock a not-locked region is - * not considered an error condition, although I'm not sure if this - * should be a default behavior (it makes porting to native Linux easy) - * or a personality option. - * - * Does Xopen/1170 say anything about this? - * - drew@Colorado.EDU - */ -#if 0 - return -EINVAL; -#else - return 0; -#endif - } - if (! (caller = alloc_lock(before, caller, fd))) - return -ENOLCK; + if (!added) { + if (caller->fl_type == F_UNLCK) + return (0); + if ((new_fl = locks_alloc_lock(caller)) == NULL) + return (-ENOLCK); + locks_insert_lock(before, new_fl); + } if (right) { if (left == right) { - /* - * The new lock breaks the old one in two pieces, so we + /* The new lock breaks the old one in two pieces, so we * have to allocate one more lock (in this case, even * F_UNLCK may fail!). */ - if (! (left = alloc_lock(before, right, fd))) { - if (! added) - free_lock(before); - return -ENOLCK; + if ((left = locks_alloc_lock(right)) == NULL) { + if (!added) + locks_delete_lock(before, 0); + return (-ENOLCK); } + locks_insert_lock(before, left); } right->fl_start = caller->fl_end + 1; } if (left) left->fl_end = caller->fl_start - 1; - return 0; + return (0); } -/* - * File_lock() inserts a lock at the position pos of the linked list. - * - * Modified to create a new node if no free entries available - Chad Page - * +/* Allocate memory for a new lock and initialize its fields from + * fl. The lock is not inserted into any lists until locks_insert_lock() + * or locks_insert_block() are called. */ -static struct file_lock *alloc_lock(struct file_lock **pos, - struct file_lock *fl, - unsigned int fd) +static struct file_lock *locks_alloc_lock(struct file_lock *fl) { struct file_lock *tmp; - tmp = file_lock_free_list; - - if (tmp == NULL) - { - /* Okay, let's make a new file_lock structure... */ - tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock), GFP_KERNEL); - tmp -> fl_owner = NULL; - tmp -> fl_next = file_lock_free_list; - tmp -> fl_nextlink = file_lock_table; - file_lock_table = tmp; - } - else - { - /* remove from free list */ - file_lock_free_list = tmp->fl_next; - } - - if (tmp->fl_owner != NULL) - panic("alloc_lock: broken free list\n"); + /* Okay, let's make a new file_lock structure... */ + if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock), + GFP_KERNEL)) == NULL) + return (tmp); + + tmp->fl_nextlink = NULL; + tmp->fl_prevlink = NULL; + tmp->fl_next = NULL; + tmp->fl_block = NULL; + tmp->fl_flags = fl->fl_flags; + tmp->fl_owner = fl->fl_owner; + tmp->fl_file = fl->fl_file; + tmp->fl_wait = NULL; + tmp->fl_type = fl->fl_type; + tmp->fl_start = fl->fl_start; + tmp->fl_end = fl->fl_end; - *tmp = *fl; + return (tmp); +} - tmp->fl_next = *pos; /* insert into file's list */ - *pos = tmp; +/* Insert file lock fl into an inode's lock list at the position indicated + * by pos. At the same time add the lock to the global file lock list. + */ - tmp->fl_owner = current; /* FIXME: needed? */ - tmp->fl_fd = fd; /* FIXME: needed? */ - tmp->fl_wait = NULL; - return tmp; +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) +{ + fl->fl_nextlink = file_lock_table; + fl->fl_prevlink = NULL; + if (file_lock_table != NULL) + file_lock_table->fl_prevlink = fl; + file_lock_table = fl; + fl->fl_next = *pos; /* insert into file's list */ + *pos = fl; + + return; } -/* - * Add a lock to the free list ... +/* Delete a lock and free it. + * First remove our lock from the lock lists. Then remove all the blocked locks + * from our blocked list, waking up the processes that own them. If told to wait, + * then sleep on each of these lock's wait queues. Each blocked process will wake + * up and immediately wake up its own wait queue allowing us to be scheduled again. + * Lastly, wake up our own wait queue before freeing the file_lock structure. */ -static void free_lock(struct file_lock **fl_p) +static void locks_delete_lock(struct file_lock **fl_p, unsigned int wait) { struct file_lock *fl; - + struct file_lock *bfl; + fl = *fl_p; - if (fl->fl_owner == NULL) /* sanity check */ - panic("free_lock: broken lock list\n"); - *fl_p = (*fl_p)->fl_next; - fl->fl_next = file_lock_free_list; /* add to free list */ - file_lock_free_list = fl; - fl->fl_owner = NULL; /* for sanity checks */ + if (fl->fl_nextlink != NULL) + fl->fl_nextlink->fl_prevlink = fl->fl_prevlink; + + if (fl->fl_prevlink != NULL) + fl->fl_prevlink->fl_nextlink = fl->fl_nextlink; + else + file_lock_table = fl->fl_nextlink; + + while ((bfl = fl->fl_block) != NULL) { + fl->fl_block = bfl->fl_block; + bfl->fl_block = NULL; + wake_up(&bfl->fl_wait); + if (wait) + sleep_on(&bfl->fl_wait); + } wake_up(&fl->fl_wait); + kfree(fl); + + return; +} + +/* Add lock fl to the blocked list pointed to by block. + * We search to the end of the existing list and insert the the new + * struct. This ensures processes will be woken up in the order they + * blocked. + * NOTE: nowhere does the documentation insist that processes be woken + * up in this order, but it seems like the reasonable thing to do. + * If the blocked list gets long then this search could get expensive, + * in which case we could consider waking the processes up in reverse + * order, or making the blocked list a doubly linked circular list. + */ +static void locks_insert_block(struct file_lock **block, struct file_lock *fl) +{ + struct file_lock *bfl; + + while ((bfl = *block) != NULL) + block = &bfl->fl_block; + + *block = fl; + fl->fl_block = NULL; + + return; } + diff --git a/fs/minix/Makefile b/fs/minix/Makefile index 20e7f3dae..82250ca47 100644 --- a/fs/minix/Makefile +++ b/fs/minix/Makefile @@ -23,6 +23,8 @@ minix.o: $(OBJS) dep: $(CPP) -M *.c > .depend +modules: minix.o + # # include a dependency file if one exists # diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 64fe09f87..ac97cb84e 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -6,6 +6,10 @@ /* bitmap.c contains the code that handles the inode and block bitmaps */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/minix_fs.h> #include <linux/stat.h> @@ -88,7 +92,7 @@ repeat: j = 8192; for (i=0 ; i<8 ; i++) if ((bh=sb->u.minix_sb.s_zmap[i]) != NULL) - if ((j=find_first_zero_bit(bh->b_data,8192)) < 8192) + if ((j=find_first_zero_bit(bh->b_data, 8192)) < 8192) break; if (i>=8 || !bh || j>=8192) return 0; @@ -171,7 +175,7 @@ struct inode * minix_new_inode(const struct inode * dir) j = 8192; for (i=0 ; i<8 ; i++) if ((bh = inode->i_sb->u.minix_sb.s_imap[i]) != NULL) - if ((j=find_first_zero_bit(bh->b_data,8192)) < 8192) + if ((j=find_first_zero_bit(bh->b_data, 8192)) < 8192) break; if (!bh || j >= 8192) { iput(inode); diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 6ece61971..dccc10469 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -6,22 +6,24 @@ * minix directory handling functions */ -#include <asm/segment.h> +#ifdef MODULE +#include <linux/module.h> +#endif +#include <linux/string.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/minix_fs.h> #include <linux/stat.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) +#include <asm/segment.h> static int minix_dir_read(struct inode * inode, struct file * filp, char * buf, int count) { return -EISDIR; } -static int minix_readdir(struct inode *, struct file *, struct dirent *, int); +static int minix_readdir(struct inode *, struct file *, void *, filldir_t); static struct file_operations minix_dir_operations = { NULL, /* lseek - default */ @@ -58,11 +60,9 @@ struct inode_operations minix_dir_inode_operations = { }; static int minix_readdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { - unsigned int offset,i,ret; - int version; - char c; + unsigned int offset; struct buffer_head * bh; struct minix_dir_entry * de; struct minix_sb_info * info; @@ -72,37 +72,26 @@ static int minix_readdir(struct inode * inode, struct file * filp, info = &inode->i_sb->u.minix_sb; if (filp->f_pos & (info->s_dirsize - 1)) return -EBADF; - ret = 0; - while (!ret && filp->f_pos < inode->i_size) { + while (filp->f_pos < inode->i_size) { offset = filp->f_pos & 1023; bh = minix_bread(inode,(filp->f_pos)>>BLOCK_SIZE_BITS,0); if (!bh) { filp->f_pos += 1024-offset; continue; } - while (!ret && offset < 1024 && filp->f_pos < inode->i_size) { + do { de = (struct minix_dir_entry *) (offset + bh->b_data); - offset += info->s_dirsize; - filp->f_pos += info->s_dirsize; -retry: if (de->inode) { - version = inode->i_version; - for (i = 0; i < info->s_namelen; i++) - if ((c = de->name[i]) != 0) - put_fs_byte(c,i+dirent->d_name); - else - break; - if (i) { - put_fs_long(de->inode,&dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); - if (version != inode->i_version) - goto retry; - ret = ROUND_UP(NAME_OFFSET(dirent)+i+1); + int size = strnlen(de->name, info->s_namelen); + if (filldir(dirent, de->name, size, filp->f_pos, de->inode) < 0) { + brelse(bh); + return 0; } } - } + offset += info->s_dirsize; + filp->f_pos += info->s_dirsize; + } while (offset < 1024 && filp->f_pos < inode->i_size); brelse(bh); } - return ret; + return 0; } diff --git a/fs/minix/file.c b/fs/minix/file.c index 670fb5e75..db3097c37 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -6,6 +6,10 @@ * minix regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 737a5bfcd..436d27191 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -8,6 +8,10 @@ * minix fsync primitive */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 2aeb538ee..595485a35 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -4,6 +4,14 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/sched.h> #include <linux/minix_fs.h> #include <linux/kernel.h> @@ -63,6 +71,7 @@ void minix_put_super(struct super_block *sb) brelse(sb->u.minix_sb.s_zmap[i]); brelse (sb->u.minix_sb.s_sbh); unlock_super(sb); + MOD_DEC_USE_COUNT; return; } @@ -121,12 +130,14 @@ struct super_block *minix_read_super(struct super_block *s,void *data, if (32 != sizeof (struct minix_inode)) panic("bad i-node size"); + MOD_INC_USE_COUNT; lock_super(s); set_blocksize(dev, BLOCK_SIZE); if (!(bh = bread(dev,1,BLOCK_SIZE))) { s->s_dev=0; unlock_super(s); printk("MINIX-fs: unable to read superblock\n"); + MOD_DEC_USE_COUNT; return NULL; } ms = (struct minix_super_block *) bh->b_data; @@ -155,6 +166,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data, brelse(bh); if (!silent) printk("VFS: Can't find a minix filesystem on dev 0x%04x.\n", dev); + MOD_DEC_USE_COUNT; return NULL; } for (i=0;i < MINIX_I_MAP_SLOTS;i++) @@ -181,6 +193,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data, unlock_super(s); brelse(bh); printk("MINIX-fs: bad superblock or unable to read bitmaps\n"); + MOD_DEC_USE_COUNT; return NULL; } set_bit(0,s->u.minix_sb.s_imap[0]->b_data); @@ -194,6 +207,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data, s->s_dev = 0; brelse(bh); printk("MINIX-fs: get root inode failed\n"); + MOD_DEC_USE_COUNT; return NULL; } if (!(s->s_flags & MS_RDONLY)) { @@ -210,22 +224,19 @@ struct super_block *minix_read_super(struct super_block *s,void *data, return s; } -void minix_statfs(struct super_block *sb, struct statfs *buf) +void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - long tmp; - - put_fs_long(MINIX_SUPER_MAGIC, &buf->f_type); - put_fs_long(1024, &buf->f_bsize); - tmp = sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone; - tmp <<= sb->u.minix_sb.s_log_zone_size; - put_fs_long(tmp, &buf->f_blocks); - tmp = minix_count_free_blocks(sb); - put_fs_long(tmp, &buf->f_bfree); - put_fs_long(tmp, &buf->f_bavail); - put_fs_long(sb->u.minix_sb.s_ninodes, &buf->f_files); - put_fs_long(minix_count_free_inodes(sb), &buf->f_ffree); - put_fs_long(sb->u.minix_sb.s_namelen, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + struct statfs tmp; + + tmp.f_type = MINIX_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size; + tmp.f_bfree = minix_count_free_blocks(sb); + tmp.f_bavail = tmp.f_bavail; + tmp.f_files = sb->u.minix_sb.s_ninodes; + tmp.f_ffree = minix_count_free_inodes(sb); + tmp.f_namelen = sb->u.minix_sb.s_namelen; + memcpy_tofs(buf, &tmp, bufsiz); } #define inode_bmap(inode,nr) ((inode)->u.minix_i.i_data[(nr)]) @@ -511,3 +522,25 @@ int minix_sync_inode(struct inode * inode) brelse (bh); return err; } + +#ifdef MODULE + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type minix_fs_type = { + minix_read_super, "minix", 1, NULL +}; + +int init_module(void) +{ + register_filesystem(&minix_fs_type); + return 0; +} + +void cleanup_module(void) +{ + unregister_filesystem(&minix_fs_type); +} + +#endif + diff --git a/fs/minix/namei.c b/fs/minix/namei.c index dd60e4602..995008c92 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -4,6 +4,10 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/minix_fs.h> #include <linux/kernel.h> @@ -23,11 +27,11 @@ static inline int namecompare(int len, int maxlen, const char * name, const char * buffer) { - if (len >= maxlen) + if (len > maxlen) return 0; if (len < maxlen && buffer[len]) return 0; - return !memcmp(name,buffer,len); + return !memcmp(name, buffer, len); } /* @@ -189,6 +193,7 @@ static int minix_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; for (i = 0; i < info->s_namelen ; i++) de->name[i] = (i < namelen) ? name[i] : 0; dir->i_version = ++event; diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c index bbd2b1f56..86dabf936 100644 --- a/fs/minix/symlink.c +++ b/fs/minix/symlink.c @@ -6,6 +6,10 @@ * minix symlink handling code */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 0b127b9b8..503c7caab 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -4,6 +4,10 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/sched.h> #include <linux/minix_fs.h> diff --git a/fs/msdos/Makefile b/fs/msdos/Makefile index 2c690d3ea..af5769dac 100644 --- a/fs/msdos/Makefile +++ b/fs/msdos/Makefile @@ -7,10 +7,6 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -ifndef CONFIG_MSDOS_FS -CFLAGS := $(CFLAGS) -DMODULE -endif - .c.s: $(CC) $(CFLAGS) -S $< .c.o: @@ -18,11 +14,14 @@ endif .s.o: $(AS) -o $*.o $< -OBJS= namei.o inode.o file.o dir.o misc.o fat.o +OBJS= buffer.o namei.o inode.o file.o dir.o misc.o fat.o mmap.o msdos.o: $(OBJS) $(LD) -r -o msdos.o $(OBJS) +modules: msdos.o + ln -sf ../fs/msdos/msdos.o $(TOPDIR)/modules + dep: $(CPP) -M *.c > .depend diff --git a/fs/msdos/buffer.c b/fs/msdos/buffer.c new file mode 100644 index 000000000..13e7aaeb6 --- /dev/null +++ b/fs/msdos/buffer.c @@ -0,0 +1,148 @@ +#ifdef MODULE +#include <linux/module.h> +#endif + +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/fs.h> +#include <linux/msdos_fs.h> + +struct buffer_head *msdos_bread ( + struct super_block *sb, + int block) +{ + struct buffer_head *ret = NULL; + if (sb->s_blocksize == 512){ + ret = bread (sb->s_dev,block,512); + }else{ + struct buffer_head *real = bread (sb->s_dev,block>>1,1024); + if (real != NULL){ + ret = (struct buffer_head *)kmalloc (sizeof(struct buffer_head) + ,GFP_KERNEL); + if (ret != NULL){ + /* #Specification: msdos / strategy / special device / dummy blocks + Many special device (Scsi optical disk for one) use + larger hardware sector size. This allows for higher + capacity. + + Most of the time, the MsDOS file system that sit + on this device is totally unaligned. It use logically + 512 bytes sector size, with logical sector starting + in the middle of a hardware block. The bad news is + that a hardware sector may hold data own by two + different files. This means that the hardware sector + must be read, patch and written almost all the time. + + Needless to say that it kills write performance + on all OS. + + Internally the linux msdos fs is using 512 bytes + logical sector. When accessing such a device, we + allocate dummy buffer cache blocks, that we stuff + with the information of a real one (1k large). + + This strategy is used to hide this difference to + the core of the msdos fs. The slowdown is not + hidden though! + */ + /* + The memset is there only to catch errors. The msdos + fs is only using b_data + */ + memset (ret,0,sizeof(*ret)); + ret->b_data = real->b_data; + if (block & 1) ret->b_data += 512; + ret->b_next = real; + }else{ + brelse (real); + } + } + } + return ret; +} +struct buffer_head *msdos_getblk ( + struct super_block *sb, + int block) +{ + struct buffer_head *ret = NULL; + if (sb->s_blocksize == 512){ + ret = getblk (sb->s_dev,block,512); + }else{ + /* #Specification: msdos / special device / writing + A write is always preceded by a read of the complete block + (large hardware sector size). This defeat write performance. + There is a possibility to optimize this when writing large + chunk by making sure we are filling large block. Volunteer ? + */ + ret = msdos_bread (sb,block); + } + return ret; +} + +void msdos_brelse ( + struct super_block *sb, + struct buffer_head *bh) +{ + if (bh != NULL){ + if (sb->s_blocksize == 512){ + brelse (bh); + }else{ + brelse (bh->b_next); + /* We can free the dummy because a new one is allocated at + each msdos_getblk() and msdos_bread(). + */ + kfree (bh); + } + } +} + +void msdos_mark_buffer_dirty ( + struct super_block *sb, + struct buffer_head *bh, + int dirty_val) +{ + if (sb->s_blocksize != 512){ + bh = bh->b_next; + } + mark_buffer_dirty (bh,dirty_val); +} + +void msdos_set_uptodate ( + struct super_block *sb, + struct buffer_head *bh, + int val) +{ + if (sb->s_blocksize != 512){ + bh = bh->b_next; + } + bh->b_uptodate = val; +} +int msdos_is_uptodate ( + struct super_block *sb, + struct buffer_head *bh) +{ + if (sb->s_blocksize != 512){ + bh = bh->b_next; + } + return bh->b_uptodate; +} + +void msdos_ll_rw_block ( + struct super_block *sb, + int opr, + int nbreq, + struct buffer_head *bh[32]) +{ + if (sb->s_blocksize == 512){ + ll_rw_block(opr,nbreq,bh); + }else{ + struct buffer_head *tmp[32]; + int i; + for (i=0; i<nbreq; i++){ + tmp[i] = bh[i]->b_next; + } + ll_rw_block(opr,nbreq,tmp); + } +} + diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c index 2138b8778..7fd17483e 100644 --- a/fs/msdos/dir.c +++ b/fs/msdos/dir.c @@ -6,6 +6,10 @@ * MS-DOS directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/fs.h> @@ -14,8 +18,7 @@ #include <linux/stat.h> #include <linux/string.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) +#include "msbuffer.h" #define PRINTK(X) @@ -59,33 +62,35 @@ struct inode_operations msdos_dir_inode_operations = { int msdos_readdir( struct inode *inode, struct file *filp, - struct dirent *dirent, /* dirent in user space */ - int count) + void *dirent, + filldir_t filldir) { + struct super_block *sb = inode->i_sb; int ino,i,i2,last; - char c,*walk; + char c; struct buffer_head *bh; struct msdos_dir_entry *de; + unsigned long oldpos = filp->f_pos; - if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; - if (inode->i_ino == MSDOS_ROOT_INO) { + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; /* Fake . and .. for the root directory. */ - if (filp->f_pos == 2) filp->f_pos = 0; - else if (filp->f_pos < 2) { - walk = filp->f_pos++ ? ".." : "."; - for (i = 0; *walk; walk++) - put_fs_byte(*walk,dirent->d_name+i++); - put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino); - put_fs_byte(0,dirent->d_name+i); - put_fs_word(i,&dirent->d_reclen); - return ROUND_UP(NAME_OFFSET(dirent) + i + 1); + if (inode->i_ino == MSDOS_ROOT_INO) { + while (oldpos < 2) { + if (filldir(dirent, "..", oldpos+1, oldpos, MSDOS_ROOT_INO) < 0) + return 0; + oldpos++; + filp->f_pos++; } + if (oldpos == 2) + filp->f_pos = 0; } - if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; + if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) + return -ENOENT; bh = NULL; while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) { if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) { - char bufname[13]; + char bufname[12]; char *ptname = bufname; for (i = last = 0; i < 8; i++) { if (!(c = de->name[i])) break; @@ -103,23 +108,20 @@ int msdos_readdir( if (c != ' ') last = i+1; ptname[i] = c; - i++; + i++; } if ((i = last) != 0) { if (!strcmp(de->name,MSDOS_DOT)) ino = inode->i_ino; else if (!strcmp(de->name,MSDOS_DOTDOT)) - ino = msdos_parent_ino(inode,0); - bufname[i] = '\0'; - put_fs_long(ino,&dirent->d_ino); - memcpy_tofs(dirent->d_name,bufname,i+1); - put_fs_word(i,&dirent->d_reclen); - PRINTK (("readdir avant brelse\n")); - brelse(bh); - PRINTK (("readdir retourne %d\n",i)); - return ROUND_UP(NAME_OFFSET(dirent) + i + 1); + ino = msdos_parent_ino(inode,0); + if (filldir(dirent, bufname, i, oldpos, ino) < 0) { + filp->f_pos = oldpos; + break; + } } } + oldpos = filp->f_pos; } if (bh) brelse(bh); return 0; diff --git a/fs/msdos/fat.c b/fs/msdos/fat.c index 651e58b24..6059ec7bc 100644 --- a/fs/msdos/fat.c +++ b/fs/msdos/fat.c @@ -4,12 +4,17 @@ * Written 1992,1993 by Werner Almesberger */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/msdos_fs.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/stat.h> +#include "msbuffer.h" static struct fat_cache *fat_cache,cache[FAT_CACHE]; @@ -28,16 +33,16 @@ int fat_access(struct super_block *sb,int nr,int new_value) first = nr*3/2; last = first+1; } - if (!(bh = msdos_sread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >> - SECTOR_BITS)))) { + if (!(bh = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >> + SECTOR_BITS),SECTOR_SIZE))) { printk("bread in fat_access failed\n"); return 0; } if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) bh2 = bh; else { - if (!(bh2 = msdos_sread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last - >> SECTOR_BITS)))) { + if (!(bh2 = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last + >> SECTOR_BITS),SECTOR_SIZE))) { brelse(bh); printk("bread in fat_access failed\n"); return 0; @@ -74,16 +79,16 @@ int fat_access(struct super_block *sb,int nr,int new_value) } mark_buffer_dirty(bh, 1); for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) { - if (!(c_bh = msdos_sread(sb->s_dev,MSDOS_SB(sb)-> + if (!(c_bh = bread(sb->s_dev,MSDOS_SB(sb)-> fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)-> - fat_length*copy))) break; + fat_length*copy,SECTOR_SIZE))) break; memcpy(c_bh->b_data,bh->b_data,SECTOR_SIZE); mark_buffer_dirty(c_bh, 1); if (bh != bh2) { - if (!(c_bh2 = msdos_sread(sb->s_dev, + if (!(c_bh2 = bread(sb->s_dev, MSDOS_SB(sb)->fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)->fat_length*copy - +1))) { + +1,SECTOR_SIZE))) { brelse(c_bh); break; } diff --git a/fs/msdos/file.c b/fs/msdos/file.c index fb41fff21..491084a71 100644 --- a/fs/msdos/file.c +++ b/fs/msdos/file.c @@ -6,6 +6,10 @@ * MS-DOS regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> @@ -18,6 +22,8 @@ #include <linux/stat.h> #include <linux/string.h> +#include "msbuffer.h" + #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) @@ -55,6 +61,52 @@ struct inode_operations msdos_file_inode_operations = { NULL, /* permission */ NULL /* smap */ }; +/* #Specification: msdos / special devices / mmap + Mmapping does work because a special mmap is provide in that case. + Note that it is much less efficient than the generic_mmap normally + used since it allocate extra buffer. generic_mmap is used for + normal device (512 bytes hardware sectors). +*/ +static struct file_operations msdos_file_operations_1024 = { + NULL, /* lseek - default */ + msdos_file_read, /* read */ + msdos_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + msdos_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + file_fsync /* fsync */ +}; + +/* #Specification: msdos / special devices / swap file + Swap file can't work on special devices with a large sector + size (1024 bytes hard sector). Those devices have a weird + MsDOS filesystem layout. Generally a single hardware sector + may contain 2 unrelated logical sector. This mean that there is + no easy way to do a mapping between disk sector of a file and virtual + memory. So swap file is difficult (not available right now) + on those devices. Off course, Ext2 does not have this problem. +*/ +struct inode_operations msdos_file_inode_operations_1024 = { + &msdos_file_operations_1024, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + msdos_truncate, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; #define MSDOS_PREFETCH 32 struct msdos_pre { @@ -72,6 +124,7 @@ static void msdos_prefetch ( struct msdos_pre *pre, int nb) /* How many must be prefetch at once */ { + struct super_block *sb = inode->i_sb; struct buffer_head *bhreq[MSDOS_PREFETCH]; /* Buffers not */ /* already read */ int nbreq=0; /* Number of buffers in bhreq */ @@ -85,12 +138,12 @@ static void msdos_prefetch ( bh = getblk(inode->i_dev,sector,SECTOR_SIZE); if (bh == NULL) break; pre->bhlist[pre->nblist++] = bh; - if (!bh->b_uptodate) bhreq[nbreq++] = bh; + if (!msdos_is_uptodate(sb,bh)) bhreq[nbreq++] = bh; }else{ break; } } - if (nbreq > 0) ll_rw_block (READ,nbreq,bhreq); + if (nbreq > 0) msdos_ll_rw_block (sb,READ,nbreq,bhreq); for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL; } @@ -103,6 +156,7 @@ int msdos_file_read( char *buf, int count) { + struct super_block *sb = inode->i_sb; char *start = buf; char *end = buf + count; int i; @@ -172,7 +226,7 @@ int msdos_file_read( } PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count)); wait_on_buffer(bh); - if (!bh->b_uptodate){ + if (!msdos_is_uptodate(sb,bh)){ /* read error ? */ brelse (bh); break; @@ -216,6 +270,7 @@ int msdos_file_write( char *buf, int count) { + struct super_block *sb = inode->i_sb; int sector,offset,size,left,written; int error,carry; char *start,*to,ch; @@ -258,7 +313,7 @@ int msdos_file_write( error = -EIO; break; } - }else if (!(bh = msdos_sread(inode->i_dev,sector))) { + }else if (!(bh = bread(inode->i_dev,sector,SECTOR_SIZE))) { error = -EIO; break; } @@ -292,7 +347,7 @@ int msdos_file_write( inode->i_size = filp->f_pos; inode->i_dirt = 1; } - bh->b_uptodate = 1; + msdos_set_uptodate(sb,bh,1); mark_buffer_dirty(bh, 0); brelse(bh); } diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c index e0577fbef..9302de2a9 100644 --- a/fs/msdos/inode.c +++ b/fs/msdos/inode.c @@ -4,6 +4,14 @@ * Written 1992,1993 by Werner Almesberger */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/msdos_fs.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -11,14 +19,12 @@ #include <linux/string.h> #include <linux/ctype.h> #include <linux/major.h> +#include <linux/blkdev.h> #include <linux/fs.h> #include <linux/stat.h> #include <linux/locks.h> -#ifdef MODULE - #include <linux/module.h> - #include "../../tools/version.h" -#endif +#include "msbuffer.h" #include <asm/segment.h> @@ -40,9 +46,8 @@ void msdos_put_inode(struct inode *inode) clear_inode(inode); if (depend) { if (MSDOS_I(depend)->i_old != inode) { - printk("Invalid link (0x%X): expected 0x%X, got 0x%X\n", - (int) depend,(int) inode,(int) MSDOS_I(depend)-> - i_old); + printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n", + depend, inode, MSDOS_I(depend)->i_old); fs_panic(sb,"..."); return; } @@ -59,9 +64,7 @@ void msdos_put_super(struct super_block *sb) lock_super(sb); sb->s_dev = 0; unlock_super(sb); - #ifdef MODULE - MOD_DEC_USE_COUNT; - #endif + MOD_DEC_USE_COUNT; return; } @@ -79,7 +82,8 @@ static struct super_operations msdos_sops = { static int parse_options(char *options,char *check,char *conversion,uid_t *uid, - gid_t *gid,int *umask,int *debug,int *fat,int *quiet) + gid_t *gid,int *umask,int *debug,int *fat,int *quiet, + int *blksize) { char *this_char,*value; @@ -145,6 +149,14 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, if (value) return 0; *quiet = 1; } + else if (!strcmp(this_char,"blocksize")) { + *blksize = simple_strtoul(value,&value,0); + if (*value) + return 0; + if (*blksize != 512 && *blksize != 1024){ + printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n"); + } + } else return 0; } return 1; @@ -153,7 +165,7 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, /* Read the super block of an MS-DOS FS. */ -struct super_block *msdos_read_super(struct super_block *s,void *data, +struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) { struct buffer_head *bh; @@ -164,28 +176,38 @@ struct super_block *msdos_read_super(struct super_block *s,void *data, uid_t uid; gid_t gid; int umask; + int blksize = 512; + MOD_INC_USE_COUNT; + if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ + blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]; + if (blksize != 512){ + printk ("MSDOS: Hardware sector size is %d\n",blksize); + } + } if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask, - &debug,&fat,&quiet)) { - s->s_dev = 0; + &debug,&fat,&quiet,&blksize) + || (blksize != 512 && blksize != 1024)) { + sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } cache_init(); - lock_super(s); - set_blocksize(s->s_dev, SECTOR_SIZE); - bh = bread(s->s_dev, 0, SECTOR_SIZE); - unlock_super(s); - if (bh == NULL) { - s->s_dev = 0; + lock_super(sb); + /* The first read is always 1024 bytes */ + sb->s_blocksize = 1024; + set_blocksize(sb->s_dev, 1024); + bh = bread(sb->s_dev, 0, 1024); + unlock_super(sb); + if (bh == NULL || !msdos_is_uptodate(sb,bh)) { + brelse (bh); + sb->s_dev = 0; printk("MSDOS bread failed\n"); + MOD_DEC_USE_COUNT; return NULL; } b = (struct msdos_boot_sector *) bh->b_data; - s->s_blocksize = 512; /* Using this small block size solve the */ - /* the misfit with buffer cache and cluster */ - /* because cluster (DOS) are often aligned */ - /* on odd sector */ - s->s_blocksize_bits = 9; /* we cannot handle anything else yet */ + set_blocksize(sb->s_dev, blksize); /* * The DOS3 partition size limit is *not* 32M as many people think. * Instead, it is 64K sectors (with the usual sector size being @@ -206,85 +228,92 @@ struct super_block *msdos_read_super(struct super_block *s,void *data, logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size); sector_mult = logical_sector_size >> SECTOR_BITS; - MSDOS_SB(s)->cluster_size = b->cluster_size*sector_mult; - MSDOS_SB(s)->fats = b->fats; - MSDOS_SB(s)->fat_start = CF_LE_W(b->reserved)*sector_mult; - MSDOS_SB(s)->fat_length = CF_LE_W(b->fat_length)*sector_mult; - MSDOS_SB(s)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W( + MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult; + MSDOS_SB(sb)->fats = b->fats; + MSDOS_SB(sb)->fat_start = CF_LE_W(b->reserved)*sector_mult; + MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult; + MSDOS_SB(sb)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W( b->fat_length))*sector_mult; - MSDOS_SB(s)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries + MSDOS_SB(sb)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries )); - MSDOS_SB(s)->data_start = MSDOS_SB(s)->dir_start+ROUND_TO_MULTIPLE(( - MSDOS_SB(s)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS, + MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE(( + MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS, sector_mult); data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ? CF_LE_W(*((unsigned short *) &b->sectors)) : - CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(s)->data_start; + CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start; error = !b->cluster_size || !sector_mult; if (!error) { - MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/ + MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/ b->cluster_size/sector_mult : 0; - MSDOS_SB(s)->fat_bits = fat ? fat : MSDOS_SB(s)->clusters > + MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters > MSDOS_FAT12 ? 16 : 12; - error = !MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & - (MSDOS_DPS-1)) || MSDOS_SB(s)->clusters+2 > MSDOS_SB(s)-> - fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits || + error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries & + (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > MSDOS_SB(sb)-> + fat_length*SECTOR_SIZE*8/MSDOS_SB(sb)->fat_bits || (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track || !b->heads; } brelse(bh); + /* + This must be done after the brelse because the bh is a dummy + allocated by msdos_bread (see buffer.c) + */ + sb->s_blocksize = blksize; /* Using this small block size solve the */ + /* the misfit with buffer cache and cluster */ + /* because cluster (DOS) are often aligned */ + /* on odd sector */ + sb->s_blocksize_bits = blksize == 512 ? 9 : 10; if (error || debug) { /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c," - "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(s)->fat_bits,check, - conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ? + "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(sb)->fat_bits,check, + conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : ""); printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d," - "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(s)->cluster_size, - MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start,MSDOS_SB(s)-> - fat_length,MSDOS_SB(s)->dir_start,MSDOS_SB(s)->dir_entries, - MSDOS_SB(s)->data_start,CF_LE_W(*(unsigned short *) &b-> - sectors),b->total_sect,logical_sector_size); + "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size, + MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)-> + fat_length,MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries, + MSDOS_SB(sb)->data_start,CF_LE_W(*(unsigned short *) &b-> + sectors),(unsigned long)b->total_sect,logical_sector_size); + printk ("Transaction block size = %d\n",blksize); } if (error) { if (!silent) printk("VFS: Can't find a valid MSDOS filesystem on dev 0x%04x.\n", - s->s_dev); - s->s_dev = 0; + sb->s_dev); + sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } - s->s_magic = MSDOS_SUPER_MAGIC; - MSDOS_SB(s)->name_check = check; - MSDOS_SB(s)->conversion = conversion; + sb->s_magic = MSDOS_SUPER_MAGIC; + MSDOS_SB(sb)->name_check = check; + MSDOS_SB(sb)->conversion = conversion; /* set up enough so that it can read an inode */ - s->s_op = &msdos_sops; - MSDOS_SB(s)->fs_uid = uid; - MSDOS_SB(s)->fs_gid = gid; - MSDOS_SB(s)->fs_umask = umask; - MSDOS_SB(s)->quiet = quiet; - MSDOS_SB(s)->free_clusters = -1; /* don't know yet */ - MSDOS_SB(s)->fat_wait = NULL; - MSDOS_SB(s)->fat_lock = 0; - MSDOS_SB(s)->prev_free = 0; - if (!(s->s_mounted = iget(s,MSDOS_ROOT_INO))) { - s->s_dev = 0; + sb->s_op = &msdos_sops; + MSDOS_SB(sb)->fs_uid = uid; + MSDOS_SB(sb)->fs_gid = gid; + MSDOS_SB(sb)->fs_umask = umask; + MSDOS_SB(sb)->quiet = quiet; + MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */ + MSDOS_SB(sb)->fat_wait = NULL; + MSDOS_SB(sb)->fat_lock = 0; + MSDOS_SB(sb)->prev_free = 0; + if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) { + sb->s_dev = 0; printk("get root inode failed\n"); + MOD_DEC_USE_COUNT; return NULL; } - #ifdef MODULE - MOD_INC_USE_COUNT; - #endif - return s; + return sb; } -void msdos_statfs(struct super_block *sb,struct statfs *buf) +void msdos_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { int free,nr; + struct statfs tmp; - put_fs_long(sb->s_magic,&buf->f_type); - put_fs_long(MSDOS_SB(sb)->cluster_size*SECTOR_SIZE,&buf->f_bsize); - put_fs_long(MSDOS_SB(sb)->clusters,&buf->f_blocks); lock_fat(sb); if (MSDOS_SB(sb)->free_clusters != -1) free = MSDOS_SB(sb)->free_clusters; @@ -295,11 +324,15 @@ void msdos_statfs(struct super_block *sb,struct statfs *buf) MSDOS_SB(sb)->free_clusters = free; } unlock_fat(sb); - put_fs_long(free,&buf->f_bfree); - put_fs_long(free,&buf->f_bavail); - put_fs_long(0,&buf->f_files); - put_fs_long(0,&buf->f_ffree); - put_fs_long(12,&buf->f_namelen); + tmp.f_type = sb->s_magic; + tmp.f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE; + tmp.f_blocks = MSDOS_SB(sb)->clusters; + tmp.f_bfree = free; + tmp.f_bavail = free; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = 12; + memcpy_tofs(buf, &tmp, bufsiz); } @@ -321,6 +354,7 @@ int msdos_bmap(struct inode *inode,int block) void msdos_read_inode(struct inode *inode) { + struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *raw_entry; int nr; @@ -384,7 +418,9 @@ void msdos_read_inode(struct inode *inode) inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG; - inode->i_op = &msdos_file_inode_operations; /* Now can always bmap */ + inode->i_op = sb->s_blocksize == 1024 + ? &msdos_file_inode_operations_1024 + : &msdos_file_inode_operations; MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); inode->i_nlink = 1; inode->i_size = CF_LE_L(raw_entry->size); @@ -404,6 +440,7 @@ void msdos_read_inode(struct inode *inode) void msdos_write_inode(struct inode *inode) { + struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *raw_entry; @@ -482,12 +519,7 @@ int init_module(void) void cleanup_module(void) { - if (MOD_IN_USE) - printk("msdos: device busy, remove delayed\n"); - else - { - unregister_filesystem(&msdos_fs_type); - } + unregister_filesystem(&msdos_fs_type); } #endif diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c index 630198afa..3b3218b32 100644 --- a/fs/msdos/misc.c +++ b/fs/msdos/misc.c @@ -4,6 +4,10 @@ * Written 1992,1993 by Werner Almesberger */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/sched.h> @@ -12,8 +16,10 @@ #include <linux/string.h> #include <linux/stat.h> +#include "msbuffer.h" #define PRINTK(x) +#define Printk(x) printk x /* Well-known binary file extensions */ static char bin_extensions[] = @@ -111,7 +117,8 @@ void unlock_fat(struct super_block *sb) int msdos_add_cluster(struct inode *inode) { - int count,nr,limit,last,current,sector,last_sector; + struct super_block *sb = inode->i_sb; + int count,nr,limit,last,current,sector,last_sector,file_cluster; struct buffer_head *bh; int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size; @@ -124,6 +131,7 @@ int msdos_add_cluster(struct inode *inode) nr = ((count+MSDOS_SB(inode->i_sb)->prev_free) % limit)+2; if (fat_access(inode->i_sb,nr,-1) == 0) break; } + PRINTK (("cnt = %d --",count)); #ifdef DEBUG printk("free cluster: %d\n",nr); #endif @@ -143,14 +151,30 @@ printk("free cluster: %d\n",nr); printk("set to %x\n",fat_access(inode->i_sb,nr,-1)); #endif last = 0; + /* We must locate the last cluster of the file to add this + new one (nr) to the end of the link list (the FAT). + + Here file_cluster will be the number of the last cluster of the + file (before we add nr). + + last is the corresponding cluster number on the disk. We will + use last to plug the nr cluster. We will use file_cluster to + update the cache. + */ + file_cluster = 0; if ((current = MSDOS_I(inode)->i_start) != 0) { cache_lookup(inode,INT_MAX,&last,¤t); - while (current && current != -1) + file_cluster = last; + while (current && current != -1){ + PRINTK ((".")); + file_cluster++; if (!(current = fat_access(inode->i_sb, last = current,-1))) { fs_panic(inode->i_sb,"File without EOF"); return -ENOSPC; } + } + PRINTK ((" -- ")); } #ifdef DEBUG printk("last = %d\n",last); @@ -173,11 +197,17 @@ if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1)); printk("getblk failed\n"); else { memset(bh->b_data,0,SECTOR_SIZE); - bh->b_uptodate = 1; + msdos_set_uptodate(sb,bh,1); mark_buffer_dirty(bh, 1); brelse(bh); } } + if (file_cluster != inode->i_blocks/cluster_size){ + printk ("file_cluster badly computed!!! %d <> %ld\n" + ,file_cluster,inode->i_blocks/cluster_size); + }else{ + cache_add(inode,file_cluster,nr); + } inode->i_blocks += cluster_size; if (S_ISDIR(inode->i_mode)) { if (inode->i_size & (SECTOR_SIZE-1)) { @@ -255,6 +285,7 @@ void date_unix2dos(int unix_date,unsigned short *time, int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, struct msdos_dir_entry **de) { + struct super_block *sb = dir->i_sb; int sector,offset; while (1) { @@ -269,7 +300,7 @@ int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, if (*bh) brelse(*bh); PRINTK (("get_entry sector apres brelse\n")); - if (!(*bh = msdos_sread(dir->i_dev,sector))) { + if (!(*bh = bread(dir->i_dev,sector,SECTOR_SIZE))) { printk("Directory sread (sector %d) failed\n",sector); continue; } @@ -343,7 +374,7 @@ static int raw_scan_sector(struct super_block *sb,int sector,char *name, struct inode *inode; int entry,start,done; - if (!(bh = msdos_sread(sb->s_dev,sector))) return -EIO; + if (!(bh = bread(sb->s_dev,sector,SECTOR_SIZE))) return -EIO; data = (struct msdos_dir_entry *) bh->b_data; for (entry = 0; entry < MSDOS_DPS; entry++) { if (name) RSS_NAME diff --git a/fs/msdos/mmap.c b/fs/msdos/mmap.c index 0e85584e9..97ffa0bb1 100644 --- a/fs/msdos/mmap.c +++ b/fs/msdos/mmap.c @@ -6,6 +6,10 @@ * * msdos mmap handling */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/stat.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -48,6 +52,7 @@ static unsigned long msdos_file_mmap_nopage( if (gap < PAGE_SIZE){ clear = PAGE_SIZE - gap; } + filp.f_reada = 0; filp.f_pos = pos; need_read = PAGE_SIZE - clear; { @@ -71,10 +76,14 @@ static unsigned long msdos_file_mmap_nopage( struct vm_operations_struct msdos_file_mmap = { NULL, /* open */ NULL, /* close */ + NULL, /* unmap */ + NULL, /* protect */ + NULL, /* sync */ + NULL, /* advise */ msdos_file_mmap_nopage, /* nopage */ NULL, /* wppage */ - NULL, /* share */ - NULL, /* unmap */ + NULL, /* swapout */ + NULL, /* swapin */ }; /* @@ -83,7 +92,7 @@ struct vm_operations_struct msdos_file_mmap = { */ int msdos_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) { - if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported now */ + if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */ return -EINVAL; if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) return -EINVAL; @@ -100,3 +109,4 @@ int msdos_mmap(struct inode * inode, struct file * file, struct vm_area_struct * return 0; } + diff --git a/fs/msdos/msbuffer.h b/fs/msdos/msbuffer.h new file mode 100644 index 000000000..ecd736149 --- /dev/null +++ b/fs/msdos/msbuffer.h @@ -0,0 +1,34 @@ +/* buffer.c 13/12/94 20.19.10 */ +struct buffer_head *msdos_bread (struct super_block *sb, int block); +struct buffer_head *msdos_getblk (struct super_block *sb, int block); +void msdos_brelse (struct super_block *sb, struct buffer_head *bh); +void msdos_mark_buffer_dirty (struct super_block *sb, + struct buffer_head *bh, + int dirty_val); +void msdos_set_uptodate (struct super_block *sb, + struct buffer_head *bh, + int val); +int msdos_is_uptodate (struct super_block *sb, struct buffer_head *bh); +void msdos_ll_rw_block (struct super_block *sb, int opr, + int nbreq, struct buffer_head *bh[32]); + +/* These macros exist to avoid modifying all the code */ +/* They should be removed one day I guess */ + +/* The versioning mechanism of the modules system define those macros */ +/* This remove some warnings */ +#ifdef brelse + #undef brelse +#endif +#ifdef bread + #undef bread +#endif +#ifdef getblk + #undef getblk +#endif + +#define brelse(b) msdos_brelse(sb,b) +#define bread(d,b,s) msdos_bread(sb,b) +#define getblk(d,b,s) msdos_getblk(sb,b) +#define mark_buffer_dirty(b,v) msdos_mark_buffer_dirty(sb,b,v) + diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index ad3b9c8bc..ee7a14e36 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -4,6 +4,10 @@ * Written 1992,1993 by Werner Almesberger */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -13,6 +17,8 @@ #include <linux/string.h> #include <linux/stat.h> +#include "msbuffer.h" + #define PRINTK(x) /* MS-DOS "device special files" */ @@ -107,6 +113,7 @@ static int msdos_find(struct inode *dir,const char *name,int len, int msdos_lookup(struct inode *dir,const char *name,int len, struct inode **result) { + struct super_block *sb = dir->i_sb; int ino,res; struct msdos_dir_entry *de; struct buffer_head *bh; @@ -173,6 +180,7 @@ int msdos_lookup(struct inode *dir,const char *name,int len, static int msdos_create_entry(struct inode *dir,char *name,int is_dir, struct inode **result) { + struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; int res,ino; @@ -209,6 +217,7 @@ static int msdos_create_entry(struct inode *dir,char *name,int is_dir, int msdos_create(struct inode *dir,const char *name,int len,int mode, struct inode **result) { + struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; char msdos_name[MSDOS_NAME]; @@ -256,6 +265,7 @@ static void dump_fat(struct super_block *sb,int start) int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) { + struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode,*dot; @@ -313,6 +323,7 @@ mkdir_error: static int msdos_empty(struct inode *dir) { + struct super_block *sb = dir->i_sb; loff_t pos; struct buffer_head *bh; struct msdos_dir_entry *de; @@ -338,6 +349,7 @@ static int msdos_empty(struct inode *dir) int msdos_rmdir(struct inode *dir,const char *name,int len) { + struct super_block *sb = dir->i_sb; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; @@ -379,6 +391,7 @@ static int msdos_unlinkx( int len, int nospc) /* Flag special file ? */ { + struct super_block *sb = dir->i_sb; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; @@ -425,6 +438,7 @@ static int rename_same_dir(struct inode *old_dir,char *old_name, struct inode *new_dir,char *new_name,struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino) { + struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh; struct msdos_dir_entry *new_de; struct inode *new_inode,*old_inode; @@ -476,6 +490,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, struct inode *new_dir,char *new_name,struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino) { + struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk; @@ -594,6 +609,7 @@ rename_done: int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, struct inode *new_dir,const char *new_name,int new_len) { + struct super_block *sb = old_dir->i_sb; char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME]; struct buffer_head *old_bh; struct msdos_dir_entry *old_de; diff --git a/fs/namei.c b/fs/namei.c index e383dff93..e047cde28 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -16,6 +16,7 @@ #include <linux/string.h> #include <linux/fcntl.h> #include <linux/stat.h> +#include <linux/mm.h> #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -31,33 +32,14 @@ static inline int get_max_filename(unsigned long address) if (get_fs() == KERNEL_DS) return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > address) - break; - } -#if defined (__i386__) - if (vma->vm_start > address || !(vma->vm_page_prot & PAGE_USER)) -#elif defined (__mips__) - if (vma->vm_start > address || - vma->vm_start >= 0x80000000 || vma->vm_end >= 0x80000000) -#else -#error "Architecture not supported." -#endif + vma = find_vma(current, address); + if (!vma || vma->vm_start > address || !(vma->vm_flags & VM_READ)) return -EFAULT; - address = vma->vm_end - address; if (address > PAGE_SIZE) return 0; if (vma->vm_next && vma->vm_next->vm_start == vma->vm_end && -#if defined (__i386__) - (vma->vm_next->vm_page_prot & PAGE_USER)) -#elif defined (__mips__) - (vma->vm_start >= 0x80000000 || vma->vm_end >= 0x80000000)) -#else -#error "Architecture not supported." -#endif + (vma->vm_next->vm_flags & VM_READ)) return 0; return address; } @@ -121,14 +103,14 @@ int permission(struct inode * inode,int mask) if (inode->i_op && inode->i_op->permission) return inode->i_op->permission(inode, mask); else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) - return 0; /* Nobody gets write access to an immutable file */ + 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 1; - return 0; + return 0; + return -EACCES; } /* @@ -184,7 +166,6 @@ int lookup(struct inode * dir,const char * name, int len, *result = dir; return 0; } else if ((sb = dir->i_sb) && (dir == sb->s_mounted)) { - sb = dir->i_sb; iput(dir); dir = sb->s_covered; if (!dir) @@ -196,9 +177,9 @@ int lookup(struct inode * dir,const char * name, int len, iput(dir); return -ENOTDIR; } - if (!perm) { + if (perm != 0) { iput(dir); - return -EACCES; + return perm; } if (!len) { *result = dir; @@ -366,9 +347,9 @@ int open_namei(const char * pathname, int flag, int mode, return -EISDIR; } /* thanks to Paul Pluzhnikov for noticing this was missing.. */ - if (!permission(dir,ACC_MODE(flag))) { + if ((error = permission(dir,ACC_MODE(flag))) != 0) { iput(dir); - return -EACCES; + return error; } *res_inode=dir; return 0; @@ -382,8 +363,8 @@ int open_namei(const char * pathname, int flag, int mode, iput(inode); error = -EEXIST; } - } else if (!permission(dir,MAY_WRITE | MAY_EXEC)) - error = -EACCES; + } else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) + ; /* error is already set! */ else if (!dir->i_op || !dir->i_op->create) error = -EACCES; else if (IS_RDONLY(dir)) @@ -409,9 +390,9 @@ int open_namei(const char * pathname, int flag, int mode, iput(inode); return -EISDIR; } - if (!permission(inode,ACC_MODE(flag))) { + if ((error = permission(inode,ACC_MODE(flag))) != 0) { iput(inode); - return -EACCES; + return error; } if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { if (IS_NODEV(inode)) { @@ -474,9 +455,9 @@ int do_mknod(const char * filename, int mode, dev_t dev) iput(dir); return -EROFS; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); - return -EACCES; + return error; } if (!dir->i_op || !dir->i_op->mknod) { iput(dir); @@ -531,9 +512,9 @@ static int do_mkdir(const char * pathname, int mode) iput(dir); return -EROFS; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); - return -EACCES; + return error; } if (!dir->i_op || !dir->i_op->mkdir) { iput(dir); @@ -577,9 +558,9 @@ static int do_rmdir(const char * name) iput(dir); return -EROFS; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); - return -EACCES; + return error; } /* * A subdirectory cannot be removed from an append-only directory @@ -625,9 +606,9 @@ static int do_unlink(const char * name) iput(dir); return -EROFS; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); - return -EACCES; + return error; } /* * A file cannot be removed from an append-only directory @@ -673,9 +654,9 @@ static int do_symlink(const char * oldname, const char * newname) iput(dir); return -EROFS; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); - return -EACCES; + return error; } if (!dir->i_op || !dir->i_op->symlink) { iput(dir); @@ -732,10 +713,10 @@ static int do_link(struct inode * oldinode, const char * newname) iput(oldinode); return -EXDEV; } - if (!permission(dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(dir); iput(oldinode); - return -EACCES; + return error; } /* * A link to an append-only or immutable file cannot be created @@ -786,9 +767,9 @@ static int do_rename(const char * oldname, const char * newname) error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir); if (error) return error; - if (!permission(old_dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(old_dir,MAY_WRITE | MAY_EXEC)) != 0) { iput(old_dir); - return -EACCES; + return error; } if (!old_len || (old_base[0] == '.' && (old_len == 1 || (old_base[1] == '.' && @@ -801,10 +782,10 @@ static int do_rename(const char * oldname, const char * newname) iput(old_dir); return error; } - if (!permission(new_dir,MAY_WRITE | MAY_EXEC)) { + if ((error = permission(new_dir,MAY_WRITE | MAY_EXEC)) != 0){ iput(old_dir); iput(new_dir); - return -EACCES; + return error; } if (!new_len || (new_base[0] == '.' && (new_len == 1 || (new_base[1] == '.' && diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 8610c95b1..eabf9f6f1 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -23,6 +23,8 @@ nfs.o: $(OBJS) dep: $(CPP) -M *.c > .depend +modules: nfs.o + # # include a dependency file if one exists # diff --git a/fs/nfs/cache.c b/fs/nfs/cache.c new file mode 100644 index 000000000..57f3cc411 --- /dev/null +++ b/fs/nfs/cache.c @@ -0,0 +1,63 @@ + +void nfs_bl_cache_invalidate(nfs_cache *nh) +{ + unsigned long flags; + save_flags(flags); + cli(); + if(nh->inuse) + nh->dead=1; + else + { + kfree_s(nh->data); + nh->data=NULL; + nh->free=1; + } +} + +void nfs_bl_cache_revalidate(nfs_cache *nh, struct fattr fa) +{ + nh->fattr=fattr; + nh->time=jiffies; +} + +/* + * Find a block in the cache. We know the cache is block sized in block + * aligned space. + */ + +nfs_cache *nfs_cache_find(struct inode *inode, off_t pos) +{ + nfs_cache *nh=&nfs_cache_slot[0]; + nfs_cache *ffree=NULL; + struct nfs_fattr fattr; + int ct=0; + while(ct<NH_CACHE_SIZE) + { + if(nh->inode_num==inode->i_no && !nh->dead&&!nh->free&&nh->file_pos==pos) + { + if(abs(jiffies-nh->time)<EXPIRE_CACHE) + return nh; + /* + * Revalidate + */ + + if(nfs_proc_getattr(NFS_SERVER(inode), NFS_FH(inode), &fattr)) + { + nfs_bl_cache_invalidate(nh); + continue; /* get attr failed */ + } + if(nh->fattr.modified!=fattr.modified) + { + nfs_bl_cache_invalidate(nh); + continue; /* cache is out of date */ + } + nfs_refresh_inode(inode, fattr); + nh->fattr=fattr; + nfs_bl_cache_revalidate(nh); + return nh; + } + if(nh->free) + ffree=nh; + } + return ffree; +} diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 62a3e0821..bdbe92d0f 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -6,6 +6,10 @@ * nfs directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/errno.h> #include <linux/stat.h> @@ -18,12 +22,9 @@ #include <asm/segment.h> /* for fs functions */ -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - static int nfs_dir_read(struct inode *, struct file *filp, char *buf, int count); -static int nfs_readdir(struct inode *, struct file *, struct dirent *, int); +static int nfs_readdir(struct inode *, struct file *, void *, filldir_t); static int nfs_lookup(struct inode *dir, const char *name, int len, struct inode **result); static int nfs_create(struct inode *dir, const char *name, int len, int mode, @@ -87,7 +88,7 @@ static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf, */ static int nfs_readdir(struct inode *inode, struct file *filp, - struct dirent *dirent, int count) + void *dirent, filldir_t filldir) { static int c_dev = 0; static int c_ino; @@ -95,7 +96,7 @@ static int nfs_readdir(struct inode *inode, struct file *filp, static struct nfs_entry *c_entry = NULL; int result; - int i; + int i, index = 0; struct nfs_entry *entry; if (!inode || !S_ISDIR(inode->i_mode)) { @@ -125,7 +126,7 @@ static int nfs_readdir(struct inode *inode, struct file *filp, return 0; } else - entry = c_entry + i + 1; + entry = c_entry + (index = i + 1); break; } } @@ -144,19 +145,27 @@ static int nfs_readdir(struct inode *inode, struct file *filp, c_dev = inode->i_dev; c_ino = inode->i_ino; c_size = result; - entry = c_entry + 0; + entry = c_entry + (index = 0); } } /* if we found it in the cache or from an nfs call, return results */ - - if (entry) { - i = strlen(entry->name); - memcpy_tofs(dirent->d_name, entry->name, i + 1); - put_fs_long(entry->fileid, &dirent->d_ino); - put_fs_word(i, &dirent->d_reclen); - filp->f_pos = entry->cookie; - return ROUND_UP(NAME_OFFSET(dirent)+i+1); + if (!entry) + return 0; + while (index < c_size) { + int nextpos = entry->cookie; + if (filldir(dirent, entry->name, strlen(entry->name), filp->f_pos, entry->fileid) < 0) + break; + filp->f_pos = nextpos; + /* revalidate the cache if we slept in filldir() */ + if (inode->i_dev != c_dev) + break; + if (inode->i_ino != c_ino) + break; + if (nextpos != entry->cookie) + break; + index++; + entry++; } return 0; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index e71d29483..cf8fe83ce 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -14,6 +14,10 @@ * nfs regular file handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> @@ -119,7 +123,7 @@ static int nfs_file_read(struct inode *inode, struct file *file, char *buf, if ((cache[i].inode_num == inode->i_ino) && (cache[i].file_pos <= pos) && (cache[i].file_pos + cache[i].len >= pos + count) - && (abs(jiffies - cache[i].time) <= EXPIRE_CACHE)) + && (abs(jiffies - cache[i].time) < EXPIRE_CACHE)) break; if (i < READ_CACHE_SIZE) { ++cache[i].in_use; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 17f2cb6f0..94b4b4557 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -4,8 +4,19 @@ * Copyright (C) 1992 Rick Sladkey * * nfs inode and superblock handling functions + * + * Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some + * experimental NFS changes. Modularisation taken straight from SYS5 fs. */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <asm/system.h> #include <asm/segment.h> @@ -18,12 +29,12 @@ #include <linux/errno.h> #include <linux/locks.h> -extern int close_fp(struct file *filp, unsigned int fd); +extern int close_fp(struct file *filp); static int nfs_notify_change(struct inode *, struct iattr *); static void nfs_put_inode(struct inode *); static void nfs_put_super(struct super_block *); -static void nfs_statfs(struct super_block *, struct statfs *); +static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz); static struct super_operations nfs_sops = { NULL, /* read inode */ @@ -43,11 +54,11 @@ static void nfs_put_inode(struct inode * inode) void nfs_put_super(struct super_block *sb) { - /* No locks should be open on this, so 0 should be safe as a fd. */ - close_fp(sb->u.nfs_sb.s_server.file, 0); + close_fp(sb->u.nfs_sb.s_server.file); lock_super(sb); sb->s_dev = 0; unlock_super(sb); + MOD_DEC_USE_COUNT; } /* @@ -67,9 +78,11 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, struct file *filp; dev_t dev = sb->s_dev; + MOD_INC_USE_COUNT; if (!data) { printk("nfs_read_super: missing data argument\n"); sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } fd = data->fd; @@ -80,11 +93,13 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) { printk("nfs_read_super: invalid file descriptor\n"); sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } if (!S_ISSOCK(filp->f_inode->i_mode)) { printk("nfs_read_super: not a socket\n"); sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } filp->f_count++; @@ -121,32 +136,33 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) { sb->s_dev = 0; printk("nfs_read_super: get root inode failed\n"); + MOD_DEC_USE_COUNT; return NULL; } return sb; } -void nfs_statfs(struct super_block *sb, struct statfs *buf) +void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { int error; struct nfs_fsinfo res; + struct statfs tmp; - put_fs_long(NFS_SUPER_MAGIC, &buf->f_type); error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root, &res); if (error) { printk("nfs_statfs: statfs error = %d\n", -error); res.bsize = res.blocks = res.bfree = res.bavail = 0; } - put_fs_long(res.bsize, &buf->f_bsize); - put_fs_long(res.blocks, &buf->f_blocks); - put_fs_long(res.bfree, &buf->f_bfree); - put_fs_long(res.bavail, &buf->f_bavail); - put_fs_long(0, &buf->f_files); - put_fs_long(0, &buf->f_ffree); - /* We should really try to interrogate the remote server to find - it's maximum name length here */ - put_fs_long(NAME_MAX, &buf->f_namelen); + tmp.f_type = NFS_SUPER_MAGIC; + tmp.f_bsize = res.bsize; + tmp.f_blocks = res.blocks; + tmp.f_bfree = res.bfree; + tmp.f_bavail = res.bavail; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + memcpy_tofs(buf, &tmp, bufsiz); } /* @@ -238,3 +254,26 @@ int nfs_notify_change(struct inode *inode, struct iattr *attr) inode->i_dirt = 0; return error; } + +#ifdef MODULE + +/* Every kernel module contains stuff like this. */ + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type nfs_fs_type = { + nfs_read_super, "nfs", 0, NULL +}; + +int init_module(void) +{ + register_filesystem(&nfs_fs_type); + return 0; +} + +void cleanup_module(void) +{ + unregister_filesystem(&nfs_fs_type); +} + +#endif diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c index 811176a69..30854469c 100644 --- a/fs/nfs/mmap.c +++ b/fs/nfs/mmap.c @@ -9,6 +9,10 @@ * Copyright (C) 1993 * */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/stat.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -74,20 +78,25 @@ static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area, } return page; } + struct vm_operations_struct nfs_file_mmap = { NULL, /* open */ NULL, /* close */ + NULL, /* unmap */ + NULL, /* protect */ + NULL, /* sync */ + NULL, /* advise */ nfs_file_mmap_nopage, /* nopage */ NULL, /* wppage */ - NULL, /* share */ - NULL, /* unmap */ + NULL, /* swapout */ + NULL, /* swapin */ }; /* This is used for a general mmap of a nfs file */ int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) { - if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported now */ + if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */ return -EINVAL; if (!inode->i_sb || !S_ISREG(inode->i_mode)) return -EACCES; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index fd01dad99..9aeb66751 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -33,7 +33,10 @@ #define NFS_PROC_DEBUG #endif -#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/param.h> #include <linux/sched.h> #include <linux/mm.h> @@ -898,6 +901,7 @@ static struct { { NFSERR_NOENT, ENOENT }, { NFSERR_IO, errno_NFSERR_IO }, { NFSERR_NXIO, ENXIO }, + { NFSERR_EAGAIN, EAGAIN }, { NFSERR_ACCES, EACCES }, { NFSERR_EXIST, EEXIST }, { NFSERR_NODEV, ENODEV }, diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c index 2455d938a..ea81f55af 100644 --- a/fs/nfs/sock.c +++ b/fs/nfs/sock.c @@ -18,7 +18,10 @@ * */ -#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/nfs_fs.h> #include <linux/errno.h> @@ -34,9 +37,6 @@ * ***FIXME*** should probably put this in nfs_fs.h */ #define NFS_SLACK_SPACE 1024 - -extern struct socket *socki_lookup(struct inode *inode); - #define _S(nr) (1<<((nr)-1)) /* @@ -78,7 +78,7 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end, int file = server->file; inode = file->f_inode; select = file->f_op->select; - sock = socki_lookup(inode); + sock = &inode->u.socket_i; if (!sock) { printk("nfs_rpc_call: socki_lookup failed\n"); return -EBADF; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 4cbe631c6..85c40b495 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -8,6 +8,10 @@ * nfs symlink handling code */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -16,10 +16,11 @@ #include <linux/signal.h> #include <linux/tty.h> #include <linux/time.h> +#include <linux/mm.h> #include <asm/segment.h> -extern void fcntl_remove_locks(struct task_struct *, struct file *, unsigned int fd); +extern void locks_remove_locks(struct task_struct *, struct file *); asmlinkage int sys_ustat(int dev, struct ustat * ubuf) { @@ -41,7 +42,7 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf) iput(inode); return -ENOSYS; } - inode->i_sb->s_op->statfs(inode->i_sb, buf); + inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); iput(inode); return 0; } @@ -61,7 +62,7 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) return -ENOENT; if (!inode->i_sb->s_op->statfs) return -ENOSYS; - inode->i_sb->s_op->statfs(inode->i_sb, buf); + inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); return 0; } @@ -74,10 +75,14 @@ asmlinkage int sys_truncate(const char * path, unsigned int length) error = namei(path,&inode); if (error) return error; - if (S_ISDIR(inode->i_mode) || !permission(inode,MAY_WRITE)) { + if (S_ISDIR(inode->i_mode)) { iput(inode); return -EACCES; } + if ((error = permission(inode,MAY_WRITE)) != 0) { + iput(inode); + return error; + } if (IS_RDONLY(inode)) { iput(inode); return -EROFS; @@ -147,14 +152,67 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) } /* Don't worry, the checks are done in inode_change_ok() */ if (times) { + error = verify_area(VERIFY_READ, times, sizeof(*times)); + if (error) { + iput(inode); + return error; + } actime = get_fs_long((unsigned long *) ×->actime); modtime = get_fs_long((unsigned long *) ×->modtime); newattrs.ia_ctime = CURRENT_TIME; flags = ATTR_ATIME_SET | ATTR_MTIME_SET; } else { - if (!permission(inode,MAY_WRITE)) { + if ((error = permission(inode,MAY_WRITE)) != 0) { iput(inode); - return -EACCES; + return error; + } + actime = modtime = newattrs.ia_ctime = CURRENT_TIME; + } + newattrs.ia_atime = actime; + newattrs.ia_mtime = modtime; + newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags; + inode->i_dirt = 1; + error = notify_change(inode, &newattrs); + iput(inode); + return error; +} + +/* If times==NULL, set access and modification to current time, + * must be owner or have write permission. + * Else, update from *times, must be owner or super user. + */ +asmlinkage int sys_utimes(char * filename, struct timeval * utimes) +{ + struct inode * inode; + long actime,modtime; + int error; + unsigned int flags = 0; + struct iattr newattrs; + + error = namei(filename,&inode); + if (error) + return error; + if (IS_RDONLY(inode)) { + iput(inode); + return -EROFS; + } + /* Don't worry, the checks are done in inode_change_ok() */ + if (utimes) { + struct timeval times[2]; + error = verify_area(VERIFY_READ, utimes, sizeof(times)); + if (error) { + iput(inode); + return error; + } + memcpy_fromfs(×, utimes, sizeof(times)); + actime = times[0].tv_sec; + modtime = times[1].tv_sec; + newattrs.ia_ctime = CURRENT_TIME; + flags = ATTR_ATIME_SET | ATTR_MTIME_SET; + } else { + if ((error = permission(inode,MAY_WRITE)) != 0) { + iput(inode); + return error; } actime = modtime = newattrs.ia_ctime = CURRENT_TIME; } @@ -185,8 +243,7 @@ asmlinkage int sys_access(const char * filename, int mode) current->fsgid = current->gid; res = namei(filename,&inode); if (!res) { - if (!permission(inode, mode)) - res = -EACCES; + res = permission(inode, mode); iput(inode); } current->fsuid = old_fsuid; @@ -206,9 +263,9 @@ asmlinkage int sys_chdir(const char * filename) iput(inode); return -ENOTDIR; } - if (!permission(inode,MAY_EXEC)) { + if ((error = permission(inode,MAY_EXEC)) != 0) { iput(inode); - return -EACCES; + return error; } iput(current->fs->pwd); current->fs->pwd = inode; @@ -219,6 +276,7 @@ asmlinkage int sys_fchdir(unsigned int fd) { struct inode * inode; struct file * file; + int error; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) return -EBADF; @@ -226,8 +284,8 @@ asmlinkage int sys_fchdir(unsigned int fd) return -ENOENT; if (!S_ISDIR(inode->i_mode)) return -ENOTDIR; - if (!permission(inode,MAY_EXEC)) - return -EACCES; + if ((error = permission(inode,MAY_EXEC)) != 0) + return error; iput(current->fs->pwd); current->fs->pwd = inode; inode->i_count++; @@ -267,6 +325,8 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) return -ENOENT; if (IS_RDONLY(inode)) return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); @@ -289,6 +349,10 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode) iput(inode); return -EROFS; } + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + iput(inode); + return -EPERM; + } if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); @@ -312,6 +376,8 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) return -ENOENT; if (IS_RDONLY(inode)) return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) @@ -352,6 +418,10 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) iput(inode); return -EROFS; } + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + iput(inode); + return -EPERM; + } if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) @@ -401,10 +471,10 @@ int do_open(const char * filename,int flags,int mode) struct file * f; int flag,error,fd; - for(fd=0 ; fd<NR_OPEN ; fd++) + for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++) if (!current->files->fd[fd]) break; - if (fd>=NR_OPEN) + if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur) return -EMFILE; FD_CLR(fd,¤t->files->close_on_exec); f = get_empty_filp(); @@ -418,8 +488,11 @@ int do_open(const char * filename,int flags,int mode) if (flag & (O_TRUNC | O_CREAT)) flag |= 2; error = open_namei(filename,flag,mode,&inode,NULL); - if (!error && (f->f_mode & 2)) + if (!error && (f->f_mode & 2)) { error = get_write_access(inode); + if (error) + iput(inode); + } if (error) { current->files->fd[fd]=NULL; f->f_count--; @@ -464,7 +537,7 @@ asmlinkage int sys_creat(const char * pathname, int mode) return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } -int close_fp(struct file *filp, unsigned int fd) +int close_fp(struct file *filp) { struct inode *inode; @@ -474,7 +547,7 @@ int close_fp(struct file *filp, unsigned int fd) } inode = filp->f_inode; if (inode) - fcntl_remove_locks(current, filp, fd); + locks_remove_locks(current, filp); if (filp->f_count > 1) { filp->f_count--; return 0; @@ -498,7 +571,7 @@ asmlinkage int sys_close(unsigned int fd) if (!(filp = current->files->fd[fd])) return -EBADF; current->files->fd[fd] = NULL; - return (close_fp (filp, fd)); + return (close_fp (filp)); } /* @@ -12,6 +12,7 @@ #include <linux/signal.h> #include <linux/fcntl.h> #include <linux/termios.h> +#include <linux/mm.h> /* We don't use the head/tail construction any more. Now we use the start/len*/ @@ -120,11 +121,6 @@ static int pipe_lseek(struct inode * inode, struct file * file, off_t offset, in return -ESPIPE; } -static int pipe_readdir(struct inode * inode, struct file * file, struct dirent * de, int count) -{ - return -ENOTDIR; -} - static int bad_pipe_rw(struct inode * inode, struct file * filp, char * buf, int count) { return -EBADF; @@ -137,9 +133,9 @@ static int pipe_ioctl(struct inode *pino, struct file * filp, switch (cmd) { case FIONREAD: - error = verify_area(VERIFY_WRITE, (void *) arg,4); + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); if (!error) - put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg); + put_fs_long(PIPE_SIZE(*pino),(int *) arg); return error; default: return -EINVAL; @@ -270,7 +266,7 @@ struct file_operations connecting_fifo_fops = { pipe_lseek, connect_read, bad_pipe_rw, - pipe_readdir, + NULL, /* no readdir */ connect_select, pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ @@ -283,7 +279,7 @@ struct file_operations read_fifo_fops = { pipe_lseek, pipe_read, bad_pipe_rw, - pipe_readdir, + NULL, /* no readdir */ fifo_select, pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ @@ -296,7 +292,7 @@ struct file_operations write_fifo_fops = { pipe_lseek, bad_pipe_rw, pipe_write, - pipe_readdir, + NULL, /* no readdir */ fifo_select, pipe_ioctl, NULL, /* mmap */ @@ -309,7 +305,7 @@ struct file_operations rdwr_fifo_fops = { pipe_lseek, pipe_read, pipe_write, - pipe_readdir, + NULL, /* no readdir */ fifo_select, pipe_ioctl, NULL, /* mmap */ @@ -322,7 +318,7 @@ struct file_operations read_pipe_fops = { pipe_lseek, pipe_read, bad_pipe_rw, - pipe_readdir, + NULL, /* no readdir */ pipe_select, pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ @@ -335,7 +331,7 @@ struct file_operations write_pipe_fops = { pipe_lseek, bad_pipe_rw, pipe_write, - pipe_readdir, + NULL, /* no readdir */ pipe_select, pipe_ioctl, NULL, /* mmap */ @@ -348,7 +344,7 @@ struct file_operations rdwr_pipe_fops = { pipe_lseek, pipe_read, pipe_write, - pipe_readdir, + NULL, /* no readdir */ pipe_select, pipe_ioctl, NULL, /* mmap */ @@ -375,43 +371,39 @@ struct inode_operations pipe_inode_operations = { NULL /* permission */ }; -asmlinkage int sys_pipe(unsigned long * fildes) +int do_pipe(int *fd) { struct inode * inode; - struct file * f[2]; - int fd[2]; + struct file *f[2]; int i,j; - j = verify_area(VERIFY_WRITE,fildes,8); - if (j) - return j; + inode = get_pipe_inode(); + if (!inode) + return -ENFILE; + for(j=0 ; j<2 ; j++) if (!(f[j] = get_empty_filp())) break; - if (j==1) - f[0]->f_count--; - if (j<2) + if (j < 2) { + iput(inode); + if (j) + f[0]->f_count--; return -ENFILE; + } j=0; - for(i=0;j<2 && i<NR_OPEN;i++) + for(i=0;j<2 && i<NR_OPEN && i<current->rlim[RLIMIT_NOFILE].rlim_cur;i++) if (!current->files->fd[i]) { current->files->fd[ fd[j]=i ] = f[j]; j++; } - if (j==1) - current->files->fd[fd[0]]=NULL; if (j<2) { + iput(inode); f[0]->f_count--; f[1]->f_count--; + if (j) + current->files->fd[fd[0]] = NULL; return -EMFILE; } - if (!(inode=get_pipe_inode())) { - current->files->fd[fd[0]] = NULL; - current->files->fd[fd[1]] = NULL; - f[0]->f_count--; - f[1]->f_count--; - return -ENFILE; - } f[0]->f_inode = f[1]->f_inode = inode; f[0]->f_pos = f[1]->f_pos = 0; f[0]->f_flags = O_RDONLY; @@ -420,7 +412,5 @@ asmlinkage int sys_pipe(unsigned long * fildes) f[1]->f_flags = O_WRONLY; f[1]->f_op = &write_pipe_fops; f[1]->f_mode = 2; /* write */ - put_fs_long(fd[0],0+fildes); - put_fs_long(fd[1],1+fildes); return 0; } diff --git a/fs/proc/array.c b/fs/proc/array.c index 6fd7bccbe..dad91d707 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -13,6 +13,18 @@ * bad '!' which forced address recalculation for * EVERY character on the current page. * <middelin@polyware.iaf.nl> + * + * Danny ter Haar : Some minor additions for cpuinfo + * <danny@ow.nl> + * + * Alessandro Rubini : profile extension. + * <rubini@ipvvis.unipv.it> + * + * Jeff Tranter : added BogoMips field to cpuinfo + * <Jeff_Tranter@Mitel.COM> + * + * Bruno Haible : remove 4K limit for the maps file + * <haible@ma2s2.mathematik.uni-karlsruhe.de> */ #include <linux/types.h> @@ -26,8 +38,12 @@ #include <linux/string.h> #include <linux/mman.h> #include <linux/proc_fs.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/mm.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <asm/io.h> #define LOAD_INT(x) ((x) >> FSHIFT) @@ -37,6 +53,7 @@ int get_malloc(char * buffer); #endif + static int read_core(struct inode * inode, struct file * file,char * buf, int count) { unsigned long p = file->f_pos; @@ -91,6 +108,66 @@ struct inode_operations proc_kcore_inode_operations = { &proc_kcore_operations, }; + +#ifdef CONFIG_PROFILE + +extern unsigned long prof_len; +extern unsigned long * prof_buffer; +/* + * This function accesses profiling information. The returned data is + * binary: the sampling step and the actual contents of the profile + * buffer. Use of the program readprofile is recommended in order to + * get meaningful info out of these data. + */ +static int read_profile(struct inode *inode, struct file *file, char *buf, int count) +{ + unsigned long p = file->f_pos; + int read; + char * pnt; + unsigned long sample_step = 1 << CONFIG_PROFILE_SHIFT; + + if (count < 0) + return -EINVAL; + if (p >= (prof_len+1)*sizeof(unsigned long)) + return 0; + if (count > (prof_len+1)*sizeof(unsigned long) - p) + count = (prof_len+1)*sizeof(unsigned long) - p; + read = 0; + + while (p < sizeof(unsigned long) && count > 0) { + put_fs_byte(*((char *)(&sample_step)+p),buf); + buf++; p++; count--; read++; + } + pnt = (char *)prof_buffer + p - sizeof(unsigned long); + memcpy_tofs(buf,(void *)pnt,count); + read += count; + file->f_pos += read; + return read; +} + +/* Writing to /proc/profile resets the counters */ +static int write_profile(struct inode * inode, struct file * file, char * buf, int count) +{ + int i=prof_len; + + while (i--) + prof_buffer[i]=0UL; + return count; +} + +static struct file_operations proc_profile_operations = { + NULL, /* lseek */ + read_profile, + write_profile, +}; + +struct inode_operations proc_profile_inode_operations = { + &proc_profile_operations, +}; + +#endif /* CONFIG_PROFILE */ + + static int get_loadavg(char * buffer) { int a, b, c; @@ -98,10 +175,11 @@ static int get_loadavg(char * buffer) a = avenrun[0] + (FIXED_1/200); b = avenrun[1] + (FIXED_1/200); c = avenrun[2] + (FIXED_1/200); - return sprintf(buffer,"%d.%02d %d.%02d %d.%02d\n", + return sprintf(buffer,"%d.%02d %d.%02d %d.%02d %d/%d\n", LOAD_INT(a), LOAD_FRAC(a), LOAD_INT(b), LOAD_FRAC(b), - LOAD_INT(c), LOAD_FRAC(c)); + LOAD_INT(c), LOAD_FRAC(c), + nr_running, nr_tasks); } static int get_kstat(char * buffer) @@ -148,11 +226,29 @@ static int get_uptime(char * buffer) uptime = jiffies; idle = task[0]->utime + task[0]->stime; + + /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but + that would overflow about every five days at HZ == 100. + Therefore the identity a = (a / b) * b + a % b is used so that it is + calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100. + The part in front of the '+' always evaluates as 0 (mod 100). All divisions + in the above formulas are truncating. For HZ being a power of 10, the + calculations simplify to the version in the #else part (if the printf + format is adapted to the same number of digits as zeroes in HZ. + */ +#if HZ!=100 + return sprintf(buffer,"%lu.%02lu %lu.%02lu\n", + uptime / HZ, + (((uptime % HZ) * 100) / HZ) % 100, + idle / HZ, + (((idle % HZ) * 100) / HZ) % 100); +#else return sprintf(buffer,"%lu.%02lu %lu.%02lu\n", uptime / HZ, uptime % HZ, idle / HZ, idle % HZ); +#endif } static int get_meminfo(char * buffer) @@ -188,23 +284,34 @@ static struct task_struct ** get_task(pid_t pid) return NULL; } -static unsigned long get_phys_addr(struct task_struct ** p, unsigned long ptr) +static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) { - unsigned long page; + pgd_t *page_dir; + pmd_t *page_middle; + pte_t pte; - if (!p || !*p || ptr >= TASK_SIZE) + if (!p || ptr >= TASK_SIZE) + return 0; + page_dir = pgd_offset(p,ptr); + if (pgd_none(*page_dir)) + return 0; + if (pgd_bad(*page_dir)) { + printk("bad page directory entry %08lx\n", pgd_val(*page_dir)); + pgd_clear(page_dir); + return 0; + } + page_middle = pmd_offset(page_dir,ptr); + if (pmd_none(*page_middle)) return 0; - page = *PAGE_DIR_OFFSET((*p)->tss.cr3,ptr); - if (!(page & PAGE_PRESENT)) + if (pmd_bad(*page_middle)) { + printk("bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_clear(page_middle); return 0; - page &= PAGE_MASK; - page += PAGE_PTR(ptr); - page = *(unsigned long *) page; - if (!(page & PAGE_PRESENT)) + } + pte = *pte_offset(page_middle,ptr); + if (!pte_present(pte)) return 0; - page &= PAGE_MASK; - page += ptr & ~PAGE_MASK; - return page; + return pte_page(pte) + (ptr & ~PAGE_MASK); } static int get_array(struct task_struct ** p, unsigned long start, unsigned long end, char * buffer) @@ -216,7 +323,7 @@ static int get_array(struct task_struct ** p, unsigned long start, unsigned long if (start >= end) return result; for (;;) { - addr = get_phys_addr(p, start); + addr = get_phys_addr(*p, start); if (!addr) goto ready; do { @@ -260,6 +367,7 @@ static int get_arg(int pid, char * buffer) static unsigned long get_wchan(struct task_struct *p) { +#ifdef __i386__ unsigned long ebp, eip; unsigned long stack_page; int count = 0; @@ -279,6 +387,7 @@ static unsigned long get_wchan(struct task_struct *p) return eip; ebp = *(unsigned long *) ebp; } while (count++ < 16); +#endif return 0; } @@ -310,7 +419,7 @@ static int get_stat(int pid, char * buffer) } wchan = get_wchan(*p); for(i=0; i<32; ++i) { - switch((int) (*p)->sigaction[i].sa_handler) { + switch((unsigned long) (*p)->sigaction[i].sa_handler) { case 1: sigignore |= bit; break; case 0: break; default: sigcatch |= bit; @@ -321,7 +430,7 @@ static int get_stat(int pid, char * buffer) else tty_pgrp = -1; return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ -%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %u %lu %lu %lu %lu %lu %lu \ +%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu \ %lu %lu %lu %lu\n", pid, (*p)->comm, @@ -361,83 +470,181 @@ static int get_stat(int pid, char * buffer) sigcatch, wchan); } + +static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, + int * pages, int * shared, int * dirty, int * total) +{ + pte_t * pte; + unsigned long end; + + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + printk("statm_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_clear(pmd); + return; + } + pte = pte_offset(pmd, address); + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + pte_t page = *pte; + + address += PAGE_SIZE; + pte++; + if (pte_none(page)) + continue; + ++*total; + if (!pte_present(page)) + continue; + ++*pages; + if (pte_dirty(page)) + ++*dirty; + if (pte_page(page) >= high_memory) + continue; + if (mem_map[MAP_NR(pte_page(page))] > 1) + ++*shared; + } while (address < end); +} + +static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned long size, + int * pages, int * shared, int * dirty, int * total) +{ + pmd_t * pmd; + unsigned long end; + + if (pgd_none(*pgd)) + return; + if (pgd_bad(*pgd)) { + printk("statm_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_clear(pgd); + return; + } + pmd = pmd_offset(pgd, address); + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + statm_pte_range(pmd, address, end - address, pages, shared, dirty, total); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long end, + int * pages, int * shared, int * dirty, int * total) +{ + while (address < end) { + statm_pmd_range(pgd, address, end - address, pages, shared, dirty, total); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + pgd++; + } +} static int get_statm(int pid, char * buffer) { struct task_struct ** p = get_task(pid); - int i, tpag; int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0; - unsigned long ptbl, *buf, *pte, *pagedir, map_nr; if (!p || !*p) return 0; - tpag = (*p)->mm->end_code / PAGE_SIZE; if ((*p)->state != TASK_ZOMBIE) { - pagedir = (unsigned long *) (*p)->tss.cr3; - for (i = 0; i < 0x300; ++i) { - if ((ptbl = pagedir[i]) == 0) { - tpag -= PTRS_PER_PAGE; - continue; - } - buf = (unsigned long *)(ptbl & PAGE_MASK); - for (pte = buf; pte < (buf + PTRS_PER_PAGE); ++pte) { - if (*pte != 0) { - ++size; - if (*pte & 1) { - ++resident; - if (tpag > 0) - ++trs; - else - ++drs; - if (i >= 15 && i < 0x2f0) { - ++lrs; - if (*pte & 0x40) - ++dt; - else - --drs; - } - map_nr = MAP_NR(*pte); - if (map_nr < (high_memory / PAGE_SIZE) && mem_map[map_nr] > 1) - ++share; + struct vm_area_struct * vma = (*p)->mm->mmap; + + while (vma) { + pgd_t *pgd = pgd_offset(*p, vma->vm_start); + int pages = 0, shared = 0, dirty = 0, total = 0; + + statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total); + resident += pages; + share += shared; + dt += dirty; + size += total; + if (vma->vm_flags & VM_EXECUTABLE) + trs += pages; /* text */ + else if (vma->vm_flags & VM_GROWSDOWN) + drs += pages; /* stack */ + else if (vma->vm_end > 0x60000000) + lrs += pages; /* library */ + else + drs += pages; + vma = vma->vm_next; } - } - --tpag; - } - } } return sprintf(buffer,"%d %d %d %d %d %d %d\n", size, resident, share, trs, lrs, drs, dt); } -static int get_maps(int pid, char *buf) +/* + * The way we support synthetic files > 4K + * - without storing their contents in some buffer and + * - without walking through the entire synthetic file until we reach the + * position of the requested data + * is to cleverly encode the current position in the file's f_pos field. + * There is no requirement that a read() call which returns `count' bytes + * of data increases f_pos by exactly `count'. + * + * This idea is Linus' one. Bruno implemented it. + */ + +/* + * For the /proc/<pid>/maps file, we use fixed length records, each containing + * a single line. + */ +#define MAPS_LINE_LENGTH 1024 +#define MAPS_LINE_SHIFT 10 +/* + * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH + * + (index into the line) + */ +#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %02x:%02x %lu\n" +#define MAPS_LINE_MAX 49 /* sum of 8 1 8 1 4 1 8 1 2 1 2 1 10 1 */ + +static int read_maps (int pid, struct file * file, char * buf, int count) { - int sz = 0; - struct task_struct **p = get_task(pid); - struct vm_area_struct *map; + struct task_struct ** p = get_task(pid); + char * destptr; + loff_t lineno; + int column; + struct vm_area_struct * map; + int i; if (!p || !*p) + return -EINVAL; + + if (count == 0) return 0; - for(map = (*p)->mm->mmap; map != NULL; map = map->vm_next) { - char str[7], *cp = str; + /* decode f_pos */ + lineno = file->f_pos >> MAPS_LINE_SHIFT; + column = file->f_pos & (MAPS_LINE_LENGTH-1); + + /* quickly go to line lineno */ + for (map = (*p)->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) + continue; + + destptr = buf; + + for ( ; map ; ) { + /* produce the next line */ + char line[MAPS_LINE_MAX+1]; + char str[5], *cp = str; int flags; - int end = sz + 80; /* Length of line */ dev_t dev; unsigned long ino; + int len; flags = map->vm_flags; *cp++ = flags & VM_READ ? 'r' : '-'; *cp++ = flags & VM_WRITE ? 'w' : '-'; *cp++ = flags & VM_EXEC ? 'x' : '-'; - *cp++ = flags & VM_SHARED ? 's' : 'p'; + *cp++ = flags & VM_MAYSHARE ? 's' : 'p'; *cp++ = 0; - - if (end >= PAGE_SIZE) { - sprintf(buf+sz, "...\n"); - break; - } - + if (map->vm_inode != NULL) { dev = map->vm_inode->i_dev; ino = map->vm_inode->i_ino; @@ -446,16 +653,44 @@ static int get_maps(int pid, char *buf) ino = 0; } - sz += sprintf(buf+sz, "%08lx-%08lx %s %08lx %02x:%02x %lu\n", + len = sprintf(line, MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_offset, MAJOR(dev),MINOR(dev), ino); - if (sz > end) { - printk("get_maps: end(%d) < sz(%d)\n", end, sz); - break; + + if (column >= len) { + column = 0; /* continue with next line at column 0 */ + lineno++; + map = map->vm_next; + continue; + } + + i = len-column; + if (i > count) + i = count; + memcpy_tofs(destptr, line+column, i); + destptr += i; count -= i; + column += i; + if (column >= len) { + column = 0; /* next time: next line at column 0 */ + lineno++; + map = map->vm_next; } + + /* done? */ + if (count == 0) + break; + + /* By writing to user space, we might have slept. + * Stop the loop, to avoid a race condition. + */ + if (*p != current) + break; } - - return sz; + + /* encode f_pos */ + file->f_pos = (lineno << MAPS_LINE_SHIFT) + column; + + return destptr-buf; } extern int get_module_list(char *); @@ -464,6 +699,8 @@ extern int get_filesystem_list(char *); extern int get_ksyms_list(char *); extern int get_irq_list(char *); extern int get_dma_list(char *); +extern int get_cpuinfo(char *); +extern int get_pci_list(char*); static int get_root_array(char * page, int type) { @@ -477,6 +714,14 @@ static int get_root_array(char * page, int type) case PROC_MEMINFO: return get_meminfo(page); +#ifdef CONFIG_PCI + case PROC_PCI: + return get_pci_list(page); +#endif + + case PROC_CPUINFO: + return get_cpuinfo(page); + case PROC_VERSION: return get_version(page); @@ -505,6 +750,9 @@ static int get_root_array(char * page, int type) case PROC_DMA: return get_dma_list(page); + + case PROC_IOPORTS: + return get_ioport_list(page); } return -EBADF; } @@ -520,8 +768,6 @@ static int get_process_array(char * page, int pid, int type) return get_stat(pid, page); case PROC_PID_STATM: return get_statm(pid, page); - case PROC_PID_MAPS: - return get_maps(pid, page); } return -EBADF; } @@ -596,3 +842,49 @@ struct inode_operations proc_array_inode_operations = { NULL, /* truncate */ NULL /* permission */ }; + +static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count) +{ + unsigned int pid = inode->i_ino >> 16; + unsigned int type = inode->i_ino & 0x0000ffff; + + if (count < 0) + return -EINVAL; + + switch (type) { + case PROC_PID_MAPS: + return read_maps(pid, file, buf, count); + } + return -EINVAL; +} + +static struct file_operations proc_arraylong_operations = { + NULL, /* array_lseek */ + arraylong_read, + NULL, /* array_write */ + NULL, /* array_readdir */ + NULL, /* array_select */ + NULL, /* array_ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +struct inode_operations proc_arraylong_inode_operations = { + &proc_arraylong_operations, /* default base directory file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; diff --git a/fs/proc/base.c b/fs/proc/base.c index 3dcf0189b..25dd82fe9 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -13,7 +13,7 @@ #include <linux/proc_fs.h> #include <linux/stat.h> -static int proc_readbase(struct inode *, struct file *, struct dirent *, int); +static int proc_readbase(struct inode *, struct file *, void *, filldir_t); static int proc_lookupbase(struct inode *,const char *,int,struct inode **); static struct file_operations proc_base_operations = { @@ -121,11 +121,11 @@ static int proc_lookupbase(struct inode * dir,const char * name, int len, } static int proc_readbase(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { struct proc_dir_entry * de; unsigned int pid, ino; - int i,j; + int i; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; @@ -136,20 +136,14 @@ static int proc_readbase(struct inode * inode, struct file * filp, break; if (!pid || i >= NR_TASKS) return 0; - if (((unsigned) filp->f_pos) < NR_BASE_DIRENTRY) { + while (((unsigned) filp->f_pos) < NR_BASE_DIRENTRY) { de = base_dir + filp->f_pos; - filp->f_pos++; - i = de->namelen; ino = de->low_ino; if (ino != 1) ino |= (pid << 16); - put_fs_long(ino, &dirent->d_ino); - put_fs_word(i,&dirent->d_reclen); - put_fs_byte(0,i+dirent->d_name); - j = i; - while (i--) - put_fs_byte(de->name[i], i+dirent->d_name); - return j; + if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino) < 0) + break; + filp->f_pos++; } return 0; } diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 954540871..03a20b441 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -13,7 +13,7 @@ #include <linux/proc_fs.h> #include <linux/stat.h> -static int proc_readfd(struct inode *, struct file *, struct dirent *, int); +static int proc_readfd(struct inode *, struct file *, void *, filldir_t); static int proc_lookupfd(struct inode *,const char *,int,struct inode **); static struct file_operations proc_fd_operations = { @@ -62,11 +62,10 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len, ino = dir->i_ino; pid = ino >> 16; ino &= 0x0000ffff; - ino -= 7; if (!dir) return -ENOENT; sb = dir->i_sb; - if (!pid || ino || !S_ISDIR(dir->i_mode)) { + if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } @@ -76,7 +75,7 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len, *result = dir; return 0; } - if (!(*result = iget(sb,(pid << 16)+2))) { + if (!(*result = iget(sb,(pid << 16)+PROC_PID_INO))) { iput(dir); return -ENOENT; } @@ -108,73 +107,67 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len, if (fd >= NR_OPEN || !p->files->fd[fd] || !p->files->fd[fd]->f_inode) return -ENOENT; - ino = (pid << 16) + 0x100 + fd; + ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; if (!(*result = iget(sb,ino))) return -ENOENT; return 0; } +#define NUMBUF 10 + static int proc_readfd(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { + char buf[NUMBUF]; + int task_nr; struct task_struct * p; unsigned int fd, pid, ino; - int i,j; + unsigned long i,j; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; ino = inode->i_ino; pid = ino >> 16; ino &= 0x0000ffff; - ino -= 7; - if (ino) + if (ino != PROC_PID_FD) return 0; - while (1) { - fd = filp->f_pos; - filp->f_pos++; - if (fd < 2) { - i = j = fd+1; - if (!fd) - fd = inode->i_ino; - else - fd = (inode->i_ino & 0xffff0000) | 2; - put_fs_long(fd, &dirent->d_ino); - put_fs_word(i, &dirent->d_reclen); - put_fs_byte(0, i+dirent->d_name); - while (i--) - put_fs_byte('.', i+dirent->d_name); - return j; - } - fd -= 2; - for (i = 1 ; i < NR_TASKS ; i++) - if ((p = task[i]) && p->pid == pid) - break; - if (i >= NR_TASKS) + + for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) { + unsigned long ino = inode->i_ino; + if (fd) + ino = (ino & 0xffff0000) | PROC_PID_INO; + if (filldir(dirent, "..", fd+1, fd, ino) < 0) + return 0; + } + + task_nr = 1; + for (;;) { + if ((p = task[task_nr]) && p->pid == pid) + break; + if (++task_nr >= NR_TASKS) return 0; - if (fd >= NR_OPEN) - break; + } + for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) { if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode) - continue; + continue; - j = 10; - i = 1; - while (fd >= j) { - j *= 10; - i++; - } - j = i; - ino = (pid << 16) + 0x100 + fd; - - put_fs_long(ino, &dirent->d_ino); - put_fs_word(i, &dirent->d_reclen); - put_fs_byte(0, i+dirent->d_name); - while (i--) { - put_fs_byte('0'+(fd % 10), i+dirent->d_name); - fd /= 10; - } - return j; + j = NUMBUF; + i = fd; + do { + j--; + buf[j] = '0' + (i % 10); + i /= 10; + } while (i); + + ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; + if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) + break; + + /* filldir() migth have slept, so we must re-validate "p" */ + if (p != task[task_nr] || p->pid != pid) + break; } return 0; } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 0d0848b33..98368730e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -12,10 +12,13 @@ #include <linux/stat.h> #include <linux/locks.h> #include <linux/limits.h> +#include <linux/config.h> #include <asm/system.h> #include <asm/segment.h> +extern unsigned long prof_len; + void proc_put_inode(struct inode *inode) { if (inode->i_nlink) @@ -41,6 +44,37 @@ static struct super_operations proc_sops = { NULL }; + +static int parse_options(char *options,uid_t *uid,gid_t *gid) +{ + char *this_char,*value; + + *uid = current->uid; + *gid = current->gid; + 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 0; + *uid = simple_strtoul(value,&value,0); + if (*value) + return 0; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 0; + *gid = simple_strtoul(value,&value,0); + if (*value) + return 0; + } + else return 0; + } + return 1; +} + + struct super_block *proc_read_super(struct super_block *s,void *data, int silent) { @@ -55,20 +89,23 @@ struct super_block *proc_read_super(struct super_block *s,void *data, printk("get root inode failed\n"); return NULL; } + parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid); return s; } -void proc_statfs(struct super_block *sb, struct statfs *buf) +void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - put_fs_long(PROC_SUPER_MAGIC, &buf->f_type); - put_fs_long(PAGE_SIZE/sizeof(long), &buf->f_bsize); - put_fs_long(0, &buf->f_blocks); - put_fs_long(0, &buf->f_bfree); - put_fs_long(0, &buf->f_bavail); - put_fs_long(0, &buf->f_files); - put_fs_long(0, &buf->f_ffree); - put_fs_long(NAME_MAX, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + struct statfs tmp; + + tmp.f_type = PROC_SUPER_MAGIC; + tmp.f_bsize = PAGE_SIZE/sizeof(long); + 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; + memcpy_tofs(buf, &tmp, bufsiz); } void proc_read_inode(struct inode * inode) @@ -104,7 +141,24 @@ void proc_read_inode(struct inode * inode) return; } - /* files within /proc/net */ +#ifdef CONFIG_IP_ACCT + /* this file may be opened R/W by root to reset the accounting */ + if (ino == PROC_NET_IPACCT) { + inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; + inode->i_op = &proc_net_inode_operations; + return; + } +#endif +#ifdef CONFIG_IP_FIREWALL + /* these files may be opened R/W by root to reset the counters */ + if ((ino == PROC_NET_IPFWFWD) || (ino == PROC_NET_IPFWBLK)) { + inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; + inode->i_op = &proc_net_inode_operations; + return; + } +#endif + + /* other files within /proc/net */ if ((ino >= PROC_NET_UNIX) && (ino < PROC_NET_LAST)) { inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_net_inode_operations; @@ -114,7 +168,7 @@ void proc_read_inode(struct inode * inode) if (!pid) { switch (ino) { case PROC_KMSG: - inode->i_mode = S_IFREG | S_IRUGO; + inode->i_mode = S_IFREG | S_IRUSR; inode->i_op = &proc_kmsg_inode_operations; break; case PROC_NET: @@ -127,6 +181,13 @@ void proc_read_inode(struct inode * inode) inode->i_op = &proc_kcore_inode_operations; inode->i_size = high_memory + PAGE_SIZE; break; +#ifdef CONFIG_PROFILE + case PROC_PROFILE: + inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; + inode->i_op = &proc_profile_inode_operations; + inode->i_size = (1+prof_len) * sizeof(unsigned long); + break; +#endif default: inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_array_inode_operations; @@ -160,13 +221,19 @@ void proc_read_inode(struct inode * inode) inode->i_nlink = 2; return; case PROC_PID_ENVIRON: + inode->i_mode = S_IFREG | S_IRUSR; + inode->i_op = &proc_array_inode_operations; + return; case PROC_PID_CMDLINE: case PROC_PID_STAT: case PROC_PID_STATM: - case PROC_PID_MAPS: inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_array_inode_operations; return; + case PROC_PID_MAPS: + inode->i_mode = S_IFIFO | S_IRUGO; + inode->i_op = &proc_arraylong_inode_operations; + return; } switch (ino >> 8) { case PROC_PID_FD_DIR: diff --git a/fs/proc/link.c b/fs/proc/link.c index 769014f46..2f940a275 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -11,6 +11,7 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/mm.h> #include <linux/proc_fs.h> #include <linux/stat.h> @@ -103,16 +104,16 @@ static int proc_follow_link(struct inode * dir, struct inode * inode, unsigned int pid, ino; struct task_struct * p; struct inode * new_inode; - int i; + int i, error; *res_inode = NULL; if (dir) iput(dir); if (!inode) return -ENOENT; - if (!permission(inode, MAY_EXEC)) { + if ((error = permission(inode, MAY_EXEC)) != 0){ iput(inode); - return -EACCES; + return error; } ino = inode->i_ino; pid = ino >> 16; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index ae043bb0a..f44bd7fbc 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -13,6 +13,7 @@ #include <asm/page.h> #include <asm/segment.h> #include <asm/io.h> +#include <asm/pgtable.h> /* * mem_write isn't really a good idea right now. It needs @@ -24,42 +25,56 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int count) { - unsigned long addr, pid, cr3; + pgd_t *page_dir; + pmd_t *page_middle; + pte_t pte; + char * page; + struct task_struct * tsk; + unsigned long addr, pid; char *tmp; - unsigned long pte, page; int i; if (count < 0) return -EINVAL; pid = inode->i_ino; pid >>= 16; - cr3 = 0; + tsk = NULL; for (i = 1 ; i < NR_TASKS ; i++) if (task[i] && task[i]->pid == pid) { - cr3 = task[i]->tss.cr3; + tsk = task[i]; break; } - if (!cr3) + if (!tsk) return -EACCES; addr = file->f_pos; tmp = buf; while (count > 0) { if (current->signal & ~current->blocked) break; - pte = *PAGE_DIR_OFFSET(cr3,addr); - if (!(pte & PAGE_PRESENT)) + page_dir = pgd_offset(tsk,addr); + if (pgd_none(*page_dir)) break; - pte &= PAGE_MASK; - pte += PAGE_PTR(addr); - page = *(unsigned long *) pte; - if (!(page & 1)) + if (pgd_bad(*page_dir)) { + printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_clear(page_dir); break; - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; + } + page_middle = pmd_offset(page_dir,addr); + if (pmd_none(*page_middle)) + break; + if (pmd_bad(*page_middle)) { + printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_clear(page_middle); + break; + } + pte = *pte_offset(page_middle,addr); + if (!pte_present(pte)) + break; + page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - memcpy_tofs(tmp,(void *) page,i); + memcpy_tofs(tmp, page, i); addr += i; tmp += i; count -= i; @@ -72,9 +87,13 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou static int mem_write(struct inode * inode, struct file * file,char * buf, int count) { - unsigned long addr, pid, cr3; + pgd_t *page_dir; + pmd_t *page_middle; + pte_t pte; + char * page; + struct task_struct * tsk; + unsigned long addr, pid; char *tmp; - unsigned long pte, page; int i; if (count < 0) @@ -82,36 +101,44 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co addr = file->f_pos; pid = inode->i_ino; pid >>= 16; - cr3 = 0; + tsk = NULL; for (i = 1 ; i < NR_TASKS ; i++) if (task[i] && task[i]->pid == pid) { - cr3 = task[i]->tss.cr3; + tsk = task[i]; break; } - if (!cr3) + if (!tsk) return -EACCES; tmp = buf; while (count > 0) { if (current->signal & ~current->blocked) break; - pte = *PAGE_DIR_OFFSET(cr3,addr); - if (!(pte & PAGE_PRESENT)) + page_dir = pgd_offset(tsk,addr); + if (pgd_none(*page_dir)) + break; + if (pgd_bad(*page_dir)) { + printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_clear(page_dir); + break; + } + page_middle = pmd_offset(page_dir,addr); + if (pmd_none(*page_middle)) break; - pte &= PAGE_MASK; - pte += PAGE_PTR(addr); - page = *(unsigned long *) pte; - if (!(page & PAGE_PRESENT)) + if (pmd_bad(*page_middle)) { + printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_clear(page_middle); break; - if (!(page & 2)) { - do_wp_page(0,addr,current,0); - continue; } - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; + pte = *pte_offset(page_middle,addr); + if (!pte_present(pte)) + break; + if (!pte_write(pte)) + break; + page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - memcpy_fromfs((void *) page,tmp,i); + memcpy_fromfs(page, tmp, i); addr += i; tmp += i; count -= i; @@ -140,92 +167,108 @@ static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int } } -int -mem_mmap(struct inode * inode, struct file * file, +/* + * This isn't really reliable by any means.. + */ +int mem_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) { - unsigned long *src_table, *dest_table, stmp, dtmp, cr3; - struct vm_area_struct *src_vma = 0; - int i; - - /* Get the source's task information */ + struct task_struct *tsk; + pgd_t *src_dir, *dest_dir; + pmd_t *src_middle, *dest_middle; + pte_t *src_table, *dest_table; + unsigned long stmp, dtmp; + struct vm_area_struct *src_vma = NULL; + int i; - cr3 = 0; - for (i = 1 ; i < NR_TASKS ; i++) - if (task[i] && task[i]->pid == (inode->i_ino >> 16)) { - cr3 = task[i]->tss.cr3; - src_vma = task[i]->mm->mmap; - break; - } + /* Get the source's task information */ - if (!cr3) - return -EACCES; + tsk = NULL; + for (i = 1 ; i < NR_TASKS ; i++) + if (task[i] && task[i]->pid == (inode->i_ino >> 16)) { + tsk = task[i]; + break; + } -/* Ensure that we have a valid source area. (Has to be mmap'ed and - have valid page information.) We can't map shared memory at the - moment because working out the vm_area_struct & nattach stuff isn't - worth it. */ + if (!tsk) + return -EACCES; - stmp = vma->vm_offset; - while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) { - while (src_vma && stmp > src_vma->vm_end) - src_vma = src_vma->vm_next; - if (!src_vma || (src_vma->vm_flags & VM_SHM)) - return -EINVAL; + /* Ensure that we have a valid source area. (Has to be mmap'ed and + have valid page information.) We can't map shared memory at the + moment because working out the vm_area_struct & nattach stuff isn't + worth it. */ - src_table = PAGE_DIR_OFFSET(cr3, stmp); - if (!*src_table) - return -EINVAL; - src_table = (unsigned long *)((*src_table & PAGE_MASK) + PAGE_PTR(stmp)); - if (!*src_table) - return -EINVAL; + src_vma = tsk->mm->mmap; + stmp = vma->vm_offset; + while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) { + while (src_vma && stmp > src_vma->vm_end) + src_vma = src_vma->vm_next; + if (!src_vma || (src_vma->vm_flags & VM_SHM)) + return -EINVAL; - if (stmp < src_vma->vm_start) { - if (!(src_vma->vm_flags & VM_GROWSDOWN)) - return -EINVAL; - if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur) - return -EINVAL; - } - stmp += PAGE_SIZE; - } + src_dir = pgd_offset(tsk, stmp); + if (pgd_none(*src_dir)) + return -EINVAL; + if (pgd_bad(*src_dir)) { + printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir)); + return -EINVAL; + } + src_middle = pmd_offset(src_dir, stmp); + if (pmd_none(*src_middle)) + return -EINVAL; + if (pmd_bad(*src_middle)) { + printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle)); + return -EINVAL; + } + src_table = pte_offset(src_middle, stmp); + if (pte_none(*src_table)) + return -EINVAL; - src_vma = task[i]->mm->mmap; - stmp = vma->vm_offset; - dtmp = vma->vm_start; + if (stmp < src_vma->vm_start) { + if (!(src_vma->vm_flags & VM_GROWSDOWN)) + return -EINVAL; + if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur) + return -EINVAL; + } + stmp += PAGE_SIZE; + } - while (dtmp < vma->vm_end) { - while (src_vma && stmp > src_vma->vm_end) - src_vma = src_vma->vm_next; + src_vma = tsk->mm->mmap; + stmp = vma->vm_offset; + dtmp = vma->vm_start; - src_table = PAGE_DIR_OFFSET(cr3, stmp); - src_table = (unsigned long *)((*src_table & PAGE_MASK) + PAGE_PTR(stmp)); + while (dtmp < vma->vm_end) { + while (src_vma && stmp > src_vma->vm_end) + src_vma = src_vma->vm_next; - dest_table = PAGE_DIR_OFFSET(current->tss.cr3, dtmp); + src_dir = pgd_offset(tsk, stmp); + src_middle = pmd_offset(src_dir, stmp); + src_table = pte_offset(src_middle, stmp); - if (!*dest_table) { - *dest_table = get_free_page(GFP_KERNEL); - if (!*dest_table) { oom(current); *dest_table=BAD_PAGE; } - else *dest_table |= PAGE_TABLE; - } - - dest_table = (unsigned long *)((*dest_table & PAGE_MASK) + PAGE_PTR(dtmp)); + dest_dir = pgd_offset(current, dtmp); + dest_middle = pmd_alloc(dest_dir, dtmp); + if (!dest_middle) + return -ENOMEM; + dest_table = pte_alloc(dest_middle, dtmp); + if (!dest_table) + return -ENOMEM; - if (!(*src_table & PAGE_PRESENT)) - do_no_page(src_vma, stmp, PAGE_PRESENT); + if (!pte_present(*src_table)) + do_no_page(src_vma, stmp, 1); - if ((vma->vm_flags & VM_WRITE) && !(*src_table & PAGE_RW)) - do_wp_page(src_vma, stmp, PAGE_RW | PAGE_PRESENT); + if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table)) + do_wp_page(src_vma, stmp, 1); - *src_table |= PAGE_DIRTY; - *dest_table = *src_table; - mem_map[MAP_NR(*src_table)]++; + *src_table = pte_mkdirty(*src_table); + *dest_table = *src_table; + mem_map[MAP_NR(pte_page(*src_table))]++; - stmp += PAGE_SIZE; - dtmp += PAGE_SIZE; - } + stmp += PAGE_SIZE; + dtmp += PAGE_SIZE; + } - invalidate(); - return 0; + invalidate(); + return 0; } static struct file_operations proc_mem_operations = { diff --git a/fs/proc/net.c b/fs/proc/net.c index 601f590d3..7ea4d0634 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -19,6 +19,8 @@ * Dusted off the code and added IPX. Fixed the 4K limit. * Erik Schoenfelder (schoenfr@ibr.cs.tu-bs.de) * /proc/net/snmp. + * Alan Cox (gw4pts@gw4pts.ampr.org) 1/95 + * Added Appletalk slots * * proc net directory handling functions */ @@ -30,12 +32,15 @@ #include <linux/sched.h> #include <linux/proc_fs.h> #include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/config.h> +#include <linux/mm.h> /* forward references */ static int proc_readnet(struct inode * inode, struct file * file, char * buf, int count); static int proc_readnetdir(struct inode *, struct file *, - struct dirent *, int); + void *, filldir_t filldir); static int proc_lookupnet(struct inode *,const char *,int,struct inode **); /* the get_*_info() functions are in the net code, and are configured @@ -50,10 +55,24 @@ extern int rarp_get_info(char *, char **, off_t, int); extern int dev_get_info(char *, char **, off_t, int); extern int rt_get_info(char *, char **, off_t, int); extern int snmp_get_info(char *, char **, off_t, int); +extern int afinet_get_info(char *, char **, off_t, int); +#if defined(CONFIG_WAVELAN) +extern int wavelan_get_info(char *, char **, off_t, int); +#endif /* defined(CONFIG_WAVELAN) */ +#ifdef CONFIG_IP_ACCT +extern int ip_acct_procinfo(char *, char **, off_t, int, int); +#endif /* CONFIG_IP_ACCT */ +#ifdef CONFIG_IP_FIREWALL +extern int ip_fw_blk_procinfo(char *, char **, off_t, int, int); +extern int ip_fw_fwd_procinfo(char *, char **, off_t, int, int); +#endif /* CONFIG_IP_FIREWALL */ +extern int ip_msqhst_procinfo(char *, char **, off_t, int); +extern int ip_mc_procinfo(char *, char **, off_t, int); #endif /* CONFIG_INET */ #ifdef CONFIG_IPX extern int ipx_get_info(char *, char **, off_t, int); extern int ipx_rt_get_info(char *, char **, off_t, int); +extern int ipx_get_interface_info(char *, char **, off_t , int); #endif /* CONFIG_IPX */ #ifdef CONFIG_AX25 extern int ax25_get_info(char *, char **, off_t, int); @@ -64,6 +83,11 @@ extern int nr_nodes_get_info(char *, char **, off_t, int); extern int nr_neigh_get_info(char *, char **, off_t, int); #endif /* CONFIG_NETROM */ #endif /* CONFIG_AX25 */ +#ifdef CONFIG_ATALK +extern int atalk_get_info(char *, char **, off_t, int); +extern int atalk_rt_get_info(char *, char **, off_t, int); +extern int atalk_if_get_info(char *, char **, off_t, int); +#endif static struct file_operations proc_net_operations = { @@ -112,13 +136,31 @@ static struct proc_dir_entry net_dir[] = { { PROC_NET_TCP, 3, "tcp" }, { PROC_NET_UDP, 3, "udp" }, { PROC_NET_SNMP, 4, "snmp" }, + { PROC_NET_SOCKSTAT, 8, "sockstat" }, #ifdef CONFIG_INET_RARP { PROC_NET_RARP, 4, "rarp"}, #endif +#ifdef CONFIG_IP_MULTICAST + { PROC_NET_IGMP, 4, "igmp"}, +#endif +#ifdef CONFIG_IP_FIREWALL + { PROC_NET_IPFWFWD, 10, "ip_forward"}, + { PROC_NET_IPFWBLK, 8, "ip_block"}, +#endif +#ifdef CONFIG_IP_MASQUERADE + { PROC_NET_IPMSQHST, 13, "ip_masquerade"}, +#endif +#ifdef CONFIG_IP_ACCT + { PROC_NET_IPACCT, 7, "ip_acct"}, +#endif +#if defined(CONFIG_WAVELAN) + { PROC_NET_WAVELAN, 7, "wavelan" }, +#endif /* defined(CONFIG_WAVELAN) */ #endif /* CONFIG_INET */ #ifdef CONFIG_IPX { PROC_NET_IPX_ROUTE, 9, "ipx_route" }, { PROC_NET_IPX, 3, "ipx" }, + { PROC_NET_IPX_INTERFACE, 13, "ipx_interface" }, #endif /* CONFIG_IPX */ #ifdef CONFIG_AX25 { PROC_NET_AX25_ROUTE, 10, "ax25_route" }, @@ -129,6 +171,11 @@ static struct proc_dir_entry net_dir[] = { { PROC_NET_NR, 2, "nr" }, #endif /* CONFIG_NETROM */ #endif /* CONFIG_AX25 */ +#ifdef CONFIG_ATALK + { PROC_NET_ATALK, 9, "appletalk" }, + { PROC_NET_AT_ROUTE, 11,"atalk_route" }, + { PROC_NET_ATIF, 11,"atalk_iface" }, +#endif /* CONFIG_ATALK */ { 0, 0, NULL } }; @@ -155,31 +202,24 @@ static int proc_lookupnet(struct inode * dir,const char * name, int len, return -ENOENT; return 0; } + iput(dir); return -ENOENT; } static int proc_readnetdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { struct proc_dir_entry * de; unsigned int ino; - int i,j; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; ino = inode->i_ino; - if (((unsigned) filp->f_pos) < NR_NET_DIRENTRY) { + while (((unsigned) filp->f_pos) < NR_NET_DIRENTRY) { de = net_dir + filp->f_pos; + if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino) < 0) + break; filp->f_pos++; - i = de->namelen; - ino = de->low_ino; - put_fs_long(ino, &dirent->d_ino); - put_fs_word(i,&dirent->d_reclen); - put_fs_byte(0,i+dirent->d_name); - j = i; - while (i--) - put_fs_byte(de->name[i], i+dirent->d_name); - return j; } return 0; } @@ -216,6 +256,9 @@ static int proc_readnet(struct inode * inode, struct file * file, length = unix_get_info(page,&start,file->f_pos,thistime); break; #ifdef CONFIG_INET + case PROC_NET_SOCKSTAT: + length = afinet_get_info(page,&start,file->f_pos,thistime); + break; case PROC_NET_ARP: length = arp_get_info(page,&start,file->f_pos,thistime); break; @@ -237,13 +280,47 @@ static int proc_readnet(struct inode * inode, struct file * file, case PROC_NET_SNMP: length = snmp_get_info(page, &start, file->f_pos,thistime); break; +#ifdef CONFIG_IP_MULTICAST + case PROC_NET_IGMP: + length = ip_mc_procinfo(page, &start, file->f_pos,thistime); + break; +#endif +#ifdef CONFIG_IP_FIREWALL + case PROC_NET_IPFWFWD: + length = ip_fw_fwd_procinfo(page, &start, file->f_pos, + thistime, (file->f_flags & O_ACCMODE) == O_RDWR); + break; + case PROC_NET_IPFWBLK: + length = ip_fw_blk_procinfo(page, &start, file->f_pos, + thistime, (file->f_flags & O_ACCMODE) == O_RDWR); + break; +#endif +#ifdef CONFIG_IP_ACCT + case PROC_NET_IPACCT: + length = ip_acct_procinfo(page, &start, file->f_pos, + thistime, (file->f_flags & O_ACCMODE) == O_RDWR); + break; +#endif +#ifdef CONFIG_IP_MASQUERADE + case PROC_NET_IPMSQHST: + length = ip_msqhst_procinfo(page, &start, file->f_pos,thistime); + break; +#endif #ifdef CONFIG_INET_RARP case PROC_NET_RARP: length = rarp_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_INET_RARP */ +#if defined(CONFIG_WAVELAN) + case PROC_NET_WAVELAN: + length = wavelan_get_info(page, &start, file->f_pos, thistime); + break; +#endif /* defined(CONFIG_WAVELAN) */ #endif /* CONFIG_INET */ #ifdef CONFIG_IPX + case PROC_NET_IPX_INTERFACE: + length = ipx_get_interface_info(page, &start, file->f_pos, thistime); + break; case PROC_NET_IPX_ROUTE: length = ipx_rt_get_info(page,&start,file->f_pos,thistime); break; @@ -251,6 +328,17 @@ static int proc_readnet(struct inode * inode, struct file * file, length = ipx_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_IPX */ +#ifdef CONFIG_ATALK + case PROC_NET_ATALK: + length = atalk_get_info(page, &start, file->f_pos, thistime); + break; + case PROC_NET_AT_ROUTE: + length = atalk_rt_get_info(page, &start, file->f_pos, thistime); + break; + case PROC_NET_ATIF: + length = atalk_if_get_info(page, &start, file->f_pos, thistime); + break; +#endif /* CONFIG_ATALK */ #ifdef CONFIG_AX25 case PROC_NET_AX25_ROUTE: length = ax25_rt_get_info(page,&start,file->f_pos,thistime); diff --git a/fs/proc/root.c b/fs/proc/root.c index 97cf2ff25..26b14816c 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -14,7 +14,7 @@ #include <linux/stat.h> #include <linux/config.h> -static int proc_readroot(struct inode *, struct file *, struct dirent *, int); +static int proc_readroot(struct inode *, struct file *, void *, filldir_t); static int proc_lookuproot(struct inode *,const char *,int,struct inode **); static struct file_operations proc_root_operations = { @@ -59,6 +59,10 @@ static struct proc_dir_entry root_dir[] = { { PROC_MEMINFO, 7, "meminfo" }, { PROC_KMSG, 4, "kmsg" }, { PROC_VERSION, 7, "version" }, +#ifdef CONFIG_PCI + { PROC_PCI, 3, "pci" }, +#endif + { PROC_CPUINFO, 7, "cpuinfo" }, { PROC_SELF, 4, "self" }, /* will change inode # */ { PROC_NET, 3, "net" }, #ifdef CONFIG_DEBUG_MALLOC @@ -72,6 +76,10 @@ static struct proc_dir_entry root_dir[] = { { PROC_FILESYSTEMS, 11,"filesystems" }, { PROC_KSYMS, 5, "ksyms" }, { PROC_DMA, 3, "dma" }, + { PROC_IOPORTS, 7, "ioports"}, +#ifdef CONFIG_PROFILE + { PROC_PROFILE, 7, "profile"}, +#endif }; #define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0]))) @@ -133,52 +141,44 @@ static int proc_lookuproot(struct inode * dir,const char * name, int len, return 0; } +#define NUMBUF 10 + static int proc_readroot(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) + void * dirent, filldir_t filldir) { - struct task_struct * p; + char buf[NUMBUF]; unsigned int nr,pid; - int i,j; + unsigned long i,j; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; -repeat: + nr = filp->f_pos; - if (nr < NR_ROOT_DIRENTRY) { + while (nr < NR_ROOT_DIRENTRY) { struct proc_dir_entry * de = root_dir + nr; + if (filldir(dirent, de->name, de->namelen, nr, de->low_ino) < 0) + return 0; filp->f_pos++; - i = de->namelen; - put_fs_long(de->low_ino, &dirent->d_ino); - put_fs_word(i,&dirent->d_reclen); - put_fs_byte(0,i+dirent->d_name); - j = i; - while (i--) - put_fs_byte(de->name[i], i+dirent->d_name); - return j; - } - nr -= NR_ROOT_DIRENTRY; - if (nr >= NR_TASKS) - return 0; - filp->f_pos++; - p = task[nr]; - if (!p || !(pid = p->pid)) - goto repeat; - if (pid & 0xffff0000) - goto repeat; - j = 10; - i = 1; - while (pid >= j) { - j *= 10; - i++; + nr++; } - j = i; - put_fs_long((pid << 16)+2, &dirent->d_ino); - put_fs_word(i, &dirent->d_reclen); - put_fs_byte(0, i+dirent->d_name); - while (i--) { - put_fs_byte('0'+(pid % 10), i+dirent->d_name); - pid /= 10; + + for (nr -= NR_ROOT_DIRENTRY; nr < NR_TASKS; nr++, filp->f_pos++) { + struct task_struct * p = task[nr]; + + if (!p || !(pid = p->pid)) + continue; + + j = NUMBUF; + i = pid; + do { + j--; + buf[j] = '0' + (i % 10); + i /= 10; + } while (i); + + if (filldir(dirent, buf+j, NUMBUF-j, nr+NR_ROOT_DIRENTRY, (pid << 16)+2) < 0) + break; } - return j; + return 0; } diff --git a/fs/read_write.c b/fs/read_write.c index 5f457b9cb..1cd19d57a 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -9,40 +9,10 @@ #include <linux/stat.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/mm.h> #include <asm/segment.h> -/* - * Count is now a supported feature, but currently only the ext2fs - * uses it. A count value of 1 is supported for compatibility with - * earlier libraries, but larger values are supported: count should - * indicate the total buffer space available for filling with dirents. - * The d_off entry in the dirents will then indicate the offset from - * each dirent to the next, and the return value will indicate the - * number of bytes written. All dirents will be written at - * word-aligned addresses. [sct Oct 1994] - */ -asmlinkage int sys_readdir(unsigned int fd, struct dirent * dirent, unsigned int count) -{ - int error; - struct file * file; - struct inode * inode; - - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || - !(inode = file->f_inode)) - return -EBADF; - error = -ENOTDIR; - if (file->f_op && file->f_op->readdir) { - int size = count; - if (count == 1) - size = sizeof(*dirent); - error = verify_area(VERIFY_WRITE, dirent, size); - if (!error) - error = file->f_op->readdir(inode,file,dirent,count); - } - return error; -} - asmlinkage int sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { struct file * file; diff --git a/fs/readdir.c b/fs/readdir.c new file mode 100644 index 000000000..9a4160ac6 --- /dev/null +++ b/fs/readdir.c @@ -0,0 +1,145 @@ +/* + * linux/fs/readdir.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/stat.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/segment.h> + +/* + * Traditional linux readdir() handling.. + * + * "count=1" is a special case, meaning that the buffer is one + * dirent-structure in size and that the code can't handle more + * anyway. Thus the special "fillonedir()" function for that + * case (the low-level handlers don't need to care about this). + */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) + +struct old_linux_dirent { + unsigned long d_ino; + unsigned long d_offset; + unsigned short d_namlen; + char d_name[1]; +}; + +struct readdir_callback { + struct old_linux_dirent * dirent; + int count; +}; + +static int fillonedir(void * __buf, char * name, int namlen, off_t offset, ino_t ino) +{ + struct readdir_callback * buf = (struct readdir_callback *) __buf; + struct old_linux_dirent * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_fs_long(ino, &dirent->d_ino); + put_fs_long(offset, &dirent->d_offset); + put_fs_word(namlen, &dirent->d_namlen); + memcpy_tofs(dirent->d_name, name, namlen); + put_fs_byte(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) +{ + int error; + struct file * file; + struct readdir_callback buf; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!file->f_op || !file->f_op->readdir) + return -ENOTDIR; + error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent)); + if (error) + return error; + buf.count = 0; + buf.dirent = dirent; + error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir); + if (error < 0) + return error; + return buf.count; +} + +/* + * New, all-improved, singing, dancing, iBCS2-compliant getdents() + * interface. + */ +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[1]; +}; + +struct getdents_callback { + struct linux_dirent * current; + struct linux_dirent * previous; + int count; + int error; +}; + +static int filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino) +{ + struct linux_dirent * dirent; + struct getdents_callback * buf = (struct getdents_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current; + buf->previous = dirent; + put_fs_long(ino, &dirent->d_ino); + put_fs_word(reclen, &dirent->d_reclen); + memcpy_tofs(dirent->d_name, name, namlen); + put_fs_byte(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent * lastdirent; + struct getdents_callback buf; + int error; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!file->f_op || !file->f_op->readdir) + return -ENOTDIR; + error = verify_area(VERIFY_WRITE, dirent, count); + if (error) + return error; + buf.current = (struct linux_dirent *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + error = file->f_op->readdir(file->f_inode, file, &buf, filldir); + if (error < 0) + return error; + lastdirent = buf.previous; + if (!lastdirent) + return buf.error; + put_user(file->f_pos, &lastdirent->d_off); + return count - buf.count; +} diff --git a/fs/select.c b/fs/select.c index e87ae07eb..0b277f9d6 100644 --- a/fs/select.c +++ b/fs/select.c @@ -20,6 +20,7 @@ #include <linux/signal.h> #include <linux/errno.h> #include <linux/personality.h> +#include <linux/mm.h> #include <asm/segment.h> #include <asm/system.h> @@ -76,7 +77,7 @@ static int check(int flag, select_table * wait, struct file * file) if ((fops = file->f_op) && (select = fops->select)) return select(inode, file, flag, wait) || (wait && select(inode, file, flag, NULL)); - if (S_ISREG(inode->i_mode)) + if (flag != SEL_EX) return 1; return 0; } @@ -164,10 +165,10 @@ static int __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdse if (error) return error; while (nr > 0) { - *fdset = get_fs_long(fs_pointer); + *fdset = get_user(fs_pointer); fdset++; fs_pointer++; - nr -= 32; + nr -= 8 * sizeof(unsigned long); } return 0; } @@ -177,10 +178,10 @@ static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fds if (!fs_pointer) return; while (nr > 0) { - put_fs_long(*fdset, fs_pointer); + put_user(*fdset, fs_pointer); fdset++; fs_pointer++; - nr -= 32; + nr -= 8 * sizeof(unsigned long); } } @@ -198,29 +199,18 @@ __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp)) * Update: ERESTARTSYS breaks at least the xview clock binary, so * I'm trying ERESTARTNOHAND which restart only when you want to. */ -asmlinkage int sys_select( unsigned long *buffer ) +asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { -/* Perform the select(nd, in, out, ex, tv) system call. */ int i; - fd_set res_in, in, *inp; - fd_set res_out, out, *outp; - fd_set res_ex, ex, *exp; - int n; - struct timeval *tvp; + fd_set res_in, in; + fd_set res_out, out; + fd_set res_ex, ex; unsigned long timeout; - i = verify_area(VERIFY_READ, buffer, 20); - if (i) - return i; - n = get_fs_long(buffer++); if (n < 0) return -EINVAL; if (n > NR_OPEN) n = NR_OPEN; - inp = (fd_set *) get_fs_long(buffer++); - outp = (fd_set *) get_fs_long(buffer++); - exp = (fd_set *) get_fs_long(buffer++); - tvp = (struct timeval *) get_fs_long(buffer); if ((i = get_fd_set(n, inp, &in)) || (i = get_fd_set(n, outp, &out)) || (i = get_fd_set(n, exp, &ex))) return i; @@ -236,11 +226,10 @@ asmlinkage int sys_select( unsigned long *buffer ) } current->timeout = timeout; i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex); - if (current->timeout > jiffies) - timeout = current->timeout - jiffies; - else - timeout = 0; + timeout = current->timeout - jiffies - 1; current->timeout = 0; + if ((long) timeout < 0) + timeout = 0; if (tvp && !(current->personality & STICKY_TIMEOUTS)) { put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); timeout %= HZ; @@ -256,3 +245,28 @@ asmlinkage int sys_select( unsigned long *buffer ) set_fd_set(n, exp, &res_ex); return i; } + +/* + * Perform the select(nd, in, out, ex, tv) system call. + * Linux/i386 didn't use to be able to handle 5 system call + * parameters, so the old select used a memory block for + * parameter passing.. + */ +asmlinkage int old_select(unsigned long *buffer) +{ + int n; + fd_set *inp; + fd_set *outp; + fd_set *exp; + struct timeval *tvp; + + n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long)); + if (n) + return n; + n = get_user(buffer); + inp = (fd_set *) get_user(buffer+1); + outp = (fd_set *) get_user(buffer+2); + exp = (fd_set *) get_user(buffer+3); + tvp = (struct timeval *) get_user(buffer+4); + return sys_select(n, inp, outp, exp, tvp); +} @@ -10,6 +10,8 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/kernel.h> +#include <linux/mm.h> + #include <asm/segment.h> static void cp_old_stat(struct inode * inode, struct old_stat * statbuf) diff --git a/fs/super.c b/fs/super.c index 9ead32a3e..a428a7922 100644 --- a/fs/super.c +++ b/fs/super.c @@ -17,6 +17,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/locks.h> +#include <linux/mm.h> #include <asm/system.h> #include <asm/segment.h> @@ -286,6 +287,7 @@ static struct super_block * read_super(dev_t dev,char *name,int flags, s->s_covered = NULL; s->s_rd_only = 0; s->s_dirt = 0; + s->s_type = type; return s; } @@ -388,7 +390,7 @@ asmlinkage int sys_umount(char * name) return -EACCES; } } else { - if (!inode || !inode->i_sb || inode != inode->i_sb->s_mounted) { + if (!inode->i_sb || inode != inode->i_sb->s_mounted) { iput(inode); return -EINVAL; } @@ -516,15 +518,9 @@ static int copy_mount_options (const void * data, unsigned long *where) if (!data) return 0; - for (vma = current->mm->mmap ; ; ) { - if (!vma || - (unsigned long) data < vma->vm_start) { - return -EFAULT; - } - if ((unsigned long) data < vma->vm_end) - break; - vma = vma->vm_next; - } + vma = find_vma(current, (unsigned long) data); + if (!vma || (unsigned long) data < vma->vm_start) + return -EFAULT; i = vma->vm_end - (unsigned long) data; if (PAGE_SIZE <= (unsigned long) i) i = PAGE_SIZE-1; @@ -583,6 +579,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, if (!fstype) return -ENODEV; t = fstype->name; + fops = NULL; if (fstype->requires_dev) { retval = namei(dev_name,&inode); if (retval) @@ -600,23 +597,28 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, iput(inode); return -ENXIO; } + fops = get_blkfops(MAJOR(dev)); + if (!fops) { + iput(inode); + return -ENOTBLK; + } + if (fops->open) { + struct file dummy; /* allows read-write or read-only flag */ + memset(&dummy, 0, sizeof(dummy)); + dummy.f_inode = inode; + dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; + retval = fops->open(inode, &dummy); + if (retval) { + iput(inode); + return retval; + } + } + } else { if (!(dev = get_unnamed_dev())) return -EMFILE; inode = NULL; } - fops = get_blkfops(MAJOR(dev)); - if (fops && fops->open) { - struct file dummy; /* allows read-write or read-only flag */ - memset(&dummy, 0, sizeof(dummy)); - dummy.f_inode = inode; - dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; - retval = fops->open(inode, &dummy); - if (retval) { - iput(inode); - return retval; - } - } page = 0; if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { flags = new_flags & ~MS_MGC_MSK; diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile index d4a6ecbd4..4ef1cddcd 100644 --- a/fs/sysv/Makefile +++ b/fs/sysv/Makefile @@ -20,6 +20,9 @@ OBJS= ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \ sysv.o: $(OBJS) $(LD) -r -o sysv.o $(OBJS) +modules: sysv.o + ln -sf ../fs/sysv/sysv.o $(TOPDIR)/modules + dep: $(CPP) -M *.c > .depend diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c index f0fb850be..eddbb0bea 100644 --- a/fs/sysv/balloc.c +++ b/fs/sysv/balloc.c @@ -19,6 +19,10 @@ * This file contains code for allocating/freeing blocks. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/kernel.h> #include <linux/fs.h> #include <linux/sysv_fs.h> diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index a4b019228..cfd85993e 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -13,6 +13,10 @@ * SystemV/Coherent directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> @@ -20,15 +24,12 @@ #include <linux/sysv_fs.h> #include <linux/stat.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - static int sysv_dir_read(struct inode * inode, struct file * filp, char * buf, int count) { return -EISDIR; } -static int sysv_readdir(struct inode *, struct file *, struct dirent *, int); +static int sysv_readdir(struct inode *, struct file *, void *, filldir_t); static struct file_operations sysv_dir_operations = { NULL, /* lseek - default */ @@ -64,15 +65,14 @@ struct inode_operations sysv_dir_inode_operations = { NULL /* permission */ }; -static int sysv_readdir1 (struct inode * inode, struct file * filp, - struct dirent * dirent) +static int sysv_readdir(struct inode * inode, struct file * filp, + void * dirent, filldir_t filldir) { struct super_block * sb; unsigned int offset,i; - char c; struct buffer_head * bh; char* bh_data; - struct sysv_dir_entry * de; + struct sysv_dir_entry * de, sde; if (!inode || !(sb = inode->i_sb) || !S_ISDIR(inode->i_mode)) return -EBADF; @@ -88,57 +88,26 @@ static int sysv_readdir1 (struct inode * inode, struct file * filp, bh_data = bh->b_data; while (offset < sb->sv_block_size && filp->f_pos < inode->i_size) { de = (struct sysv_dir_entry *) (offset + bh_data); - offset += SYSV_DIRSIZE; - filp->f_pos += SYSV_DIRSIZE; if (de->inode) { - struct sysv_dir_entry sde; - /* Copy the directory entry first, because the directory - * might be modified while we sleep in put_fs_byte... + * might be modified while we sleep in filldir()... */ memcpy(&sde, de, sizeof(struct sysv_dir_entry)); - for (i = 0; i < SYSV_NAMELEN; i++) - if ((c = sde.name[i]) != 0) - put_fs_byte(c,i+dirent->d_name); - else - break; - if (i) { - if (sde.inode > inode->i_sb->sv_ninodes) - printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n", - inode->i_dev, inode->i_ino, (off_t) filp->f_pos - SYSV_DIRSIZE, sde.inode); - put_fs_long(sde.inode,&dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); + if (sde.inode > inode->i_sb->sv_ninodes) + printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n", + inode->i_dev, inode->i_ino, (off_t) filp->f_pos, sde.inode); + + i = strnlen(sde.name, SYSV_NAMELEN); + if (filldir(dirent, sde.name, i, filp->f_pos, sde.inode) < 0) { brelse(bh); - return ROUND_UP(NAME_OFFSET(dirent)+i+1); + return 0; } } + offset += SYSV_DIRSIZE; + filp->f_pos += SYSV_DIRSIZE; } brelse(bh); } return 0; } - -static int sysv_readdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) -{ - int retval, stored; - - /* compatibility */ - if (count==1) - return sysv_readdir1(inode,filp,dirent); - - stored = 0; - while (count >= sizeof(struct dirent)) { - retval = sysv_readdir1(inode,filp,dirent); - if (retval < 0) - return retval; - if (!retval) - return stored; - dirent = (struct dirent *)((char *) dirent + retval); - stored += retval; - count -= retval; - } - return stored; -} diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 27f82d51a..f098af6b6 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -13,6 +13,10 @@ * SystemV/Coherent regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/kernel.h> @@ -189,8 +193,10 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou if (!read) return -EIO; filp->f_reada = 1; - if (!IS_RDONLY(inode)) + if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } return read; } diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index 9e105077d..af1218fca 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -14,6 +14,10 @@ * SystemV/Coherent fsync primitive */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/stat.h> diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index f87009100..98883c22c 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -19,6 +19,10 @@ * This file contains code for allocating/freeing inodes. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/kernel.h> #include <linux/fs.h> diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 0b61ae7c7..4aa0932cb 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -20,6 +20,14 @@ * the superblock. */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/sched.h> #include <linux/kernel.h> #include <linux/fs.h> @@ -220,6 +228,7 @@ static struct super_block * detected_sysv4 (struct super_block *sb, struct buffe sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; + sb->sv_sb_state = &sbd->s_state; sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; @@ -277,6 +286,7 @@ static struct super_block * detected_sysv2 (struct super_block *sb, struct buffe sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; + sb->sv_sb_state = &sbd->s_state; sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; @@ -348,6 +358,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, panic("Coherent FS: bad super-block size"); if (64 != sizeof (struct sysv_inode)) panic("sysv fs: bad i-node size"); + MOD_INC_USE_COUNT; lock_super(sb); set_blocksize(dev,BLOCK_SIZE); @@ -390,6 +401,8 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, unlock_super(sb); if (!silent) printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device %d/%d\n",MAJOR(dev),MINOR(dev)); + failed: + MOD_DEC_USE_COUNT; return NULL; ok: @@ -413,7 +426,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_dev = 0; unlock_super(sb); printk("SysV FS: cannot read superblock in 1024 byte mode\n"); - return NULL; + goto failed; } } else { /* Switch to another block size. Unfortunately, we have to @@ -459,7 +472,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_dev = 0; unlock_super(sb); printk("SysV FS: cannot read superblock in 512 byte mode\n"); - return NULL; + goto failed; } } sb->sv_ninodes = (sb->sv_firstdatazone - sb->sv_firstinodezone) << sb->sv_inodes_per_block_bits; @@ -475,8 +488,8 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_mounted = iget(sb,SYSV_ROOT_INO); unlock_super(sb); if (!sb->s_mounted) { - sysv_put_super(sb); printk("SysV FS: get root inode failed\n"); + sysv_put_super(sb); return NULL; } sb->s_dirt = 1; @@ -491,8 +504,15 @@ void sysv_write_super (struct super_block *sb) lock_super(sb); if (sb->sv_bh1->b_dirt || sb->sv_bh2->b_dirt) { /* If we are going to write out the super block, - then attach current time stamp. */ + then attach current time stamp. + But if the filesystem was marked clean, keep it clean. */ unsigned long time = CURRENT_TIME; + unsigned long old_time = *sb->sv_sb_time; + if (sb->sv_convert) + old_time = from_coh_ulong(old_time); + if (sb->sv_type == FSTYPE_SYSV4) + if (*sb->sv_sb_state == 0x7c269d38 - old_time) + *sb->sv_sb_state = 0x7c269d38 - time; if (sb->sv_convert) time = to_coh_ulong(time); *sb->sv_sb_time = time; @@ -513,22 +533,22 @@ void sysv_put_super(struct super_block *sb) set_blocksize(sb->s_dev,BLOCK_SIZE); sb->s_dev = 0; unlock_super(sb); + MOD_DEC_USE_COUNT; } -void sysv_statfs(struct super_block *sb, struct statfs *buf) +void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - long tmp; - - put_fs_long(sb->s_magic, &buf->f_type); /* type of filesystem */ - put_fs_long(sb->sv_block_size, &buf->f_bsize); /* block size */ - put_fs_long(sb->sv_ndatazones, &buf->f_blocks); /* total data blocks in file system */ - tmp = sysv_count_free_blocks(sb); - put_fs_long(tmp, &buf->f_bfree); /* free blocks in fs */ - put_fs_long(tmp, &buf->f_bavail); /* free blocks available to non-superuser */ - put_fs_long(sb->sv_ninodes, &buf->f_files); /* total file nodes in file system */ - put_fs_long(sysv_count_free_inodes(sb), &buf->f_ffree); /* free file nodes in fs */ - put_fs_long(SYSV_NAMELEN, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ /* file system id */ + struct statfs tmp; + + tmp.f_type = sb->s_magic; + tmp.f_bsize = sb->sv_block_size; + tmp.f_blocks = sb->sv_ndatazones; + tmp.f_bfree = sysv_count_free_blocks(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = sb->sv_ninodes; + tmp.f_ffree = sysv_count_free_inodes(sb); + tmp.f_namelen = SYSV_NAMELEN; + memcpy_tofs(buf, &tmp, bufsiz); } @@ -949,3 +969,34 @@ int sysv_sync_inode(struct inode * inode) return err; } +#ifdef MODULE + +/* Every kernel module contains stuff like this. */ + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type sysv_fs_type[3] = { + {sysv_read_super, "xenix", 1, NULL}, + {sysv_read_super, "sysv", 1, NULL}, + {sysv_read_super, "coherent", 1, NULL} +}; + +int init_module(void) +{ + int i; + + for (i = 0; i < 3; i++) + register_filesystem(&sysv_fs_type[i]); + + return 0; +} + +void cleanup_module(void) +{ + int i; + + for (i = 0; i < 3; i++) + unregister_filesystem(&sysv_fs_type[i]); +} + +#endif diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index c9fd77158..8d245d38e 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -11,6 +11,10 @@ * Copyright (C) 1993 Bruno Haible */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/kernel.h> #include <linux/fs.h> @@ -184,6 +188,7 @@ static int sysv_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; for (i = 0; i < SYSV_NAMELEN ; i++) de->name[i] = (i < namelen) ? name[i] : 0; mark_buffer_dirty(bh, 1); diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c index d392816bc..c4e7d5bf5 100644 --- a/fs/sysv/symlink.c +++ b/fs/sysv/symlink.c @@ -13,6 +13,10 @@ * SystemV/Coherent symlink handling code */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index 21451e6dd..6de370ebd 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -11,6 +11,10 @@ * Copyright (C) 1993 Bruno Haible */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/fs.h> #include <linux/sysv_fs.h> diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index cfba11e63..8bdf98f97 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -7,10 +7,6 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -ifndef CONFIG_UMSDOS_FS -CFLAGS := $(CFLAGS) -DMODULE -endif - .c.s: $(CC) $(CFLAGS) -S $< .c.o: @@ -24,6 +20,9 @@ OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\ umsdos.o: $(OBJS) $(LD) -r -o umsdos.o $(OBJS) +modules: umsdos.o + ln -sf ../fs/umsdos/umsdos.o $(TOPDIR)/modules + clean: rm -f core *.o *.a *.s diff --git a/fs/umsdos/README b/fs/umsdos/README index 4ce8b4148..320dac6ca 100644 --- a/fs/umsdos/README +++ b/fs/umsdos/README @@ -16,7 +16,7 @@ It gives you: There is plenty of documentation on it in the source. A formated document made from those comments is available from -sunsite.unc.edu:/pub/Linux/ALPHA/umsdos +sunsite.unc.edu:/pub/Linux/system/Filesystems/umsdos. Mostly... @@ -53,9 +53,9 @@ Now, how to get those --linux-.---. $5 per directory. Add any applicable taxes. \end joke_section -A utility umssync creates those and maintain them. It is available -from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz. -A compiled version is available in umsdos-0.3a.bin.tar.gz. +A utility umssync creates those. The kernel maintain them. It is available +from the same directory above (sunsite) in the file umsdos_progs-0.7.tar.gz. +A compiled version is available in umsdos_progs-0.7.bin.tar.gz. So in our example, after mounting mnt, we do @@ -80,5 +80,17 @@ Umsdos won't notice new files, but will signal removed file (it won't crash). Using umssync in /etc/rc will make sure the DOS directory is in sync with the --linux-.---. +It is a good idea to put the following command in your RC file just +after the "mount -a": + + mount -a + /sbin/umssync -i+ -c+ -r99 /umsdos_mount_point + + (You put one for each umsdos mount point in the fstab) + +This will insure nice operation. A umsdos.fsck is in the making, +so you will be allowed to managed umsdos partition in the same way +other filesystem are, using the generic fsck front end. + Hope this helps! diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index d1102b4ce..7fb060c7a 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -1,3 +1,7 @@ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/system.h> #include <linux/signal.h> @@ -12,44 +16,35 @@ extern unsigned long high_memory; -static int check_one_table(unsigned long * page_dir) +static int check_one_table(struct pde * page_dir) { - unsigned long pg_table = *page_dir; - - if (!pg_table) + if (pgd_none(*page_dir)) return 0; - if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) { + if (pgd_bad(*page_dir)) return 1; - } return 0; } /* - * This function frees up all page tables of a process when it exits. + * This function checks all page tables of "current" */ void check_page_tables(void) { - unsigned long pg_dir; + struct pgd * pg_dir; static int err = 0; int stack_level = (long)(&pg_dir)-current->kernel_stack_page; if (stack_level < 1500) printk ("** %d ** ",stack_level); - pg_dir = current->tss.cr3; - if (mem_map[MAP_NR(pg_dir)] > 1) { - return; - } - if (err == 0){ - unsigned long *page_dir = (unsigned long *) pg_dir; - unsigned long *base = page_dir; + pg_dir = PAGE_DIR_OFFSET(current, 0); + if (err == 0) { int i; for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){ int notok = check_one_table(page_dir); if (notok){ err++; - printk ("|%d| ",page_dir-base); + printk ("|%d:%08lx| ",i, page_dir->pgd); } } - if (err) printk ("Erreur MM %d\n",err); + if (err) printk ("\nErreur MM %d\n",err); } } - diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 2a668d102..990260a92 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -7,6 +7,10 @@ * Extended MS-DOS directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -32,10 +36,14 @@ int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf, { return -EISDIR; } +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+3) & ~3) + /* Read count directory entries from directory filp Return a negative value from linux/errno.h. - Return > 0 if success (the length of the file name). + Return > 0 if success (The amount of byte written in + dirent round_up to a word size (32 bits). This function is used by the normal readdir VFS entry point and by some function who try to find out info on a file from a pure MSDOS @@ -72,7 +80,7 @@ static int umsdos_readdir_x( put_fs_byte(0,dirent->d_name+3); put_fs_word (3,&dirent->d_reclen); if (u_entry != NULL) u_entry->flags = 0; - ret = 3; + ret = ROUND_UP(NAME_OFFSET(dirent) + 3 + 1); filp->f_pos++; }else if (filp->f_pos < 2 || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){ @@ -191,7 +199,7 @@ static int umsdos_readdir_x( dirent->d_reclen = entry.name_len; if (u_entry != NULL) *u_entry = entry; } - ret = entry.name_len; + ret = ROUND_UP(NAME_OFFSET(dirent) + entry.name_len + 1); iput (inode); break; } @@ -219,7 +227,7 @@ static int umsdos_readdir_x( /* Read count directory entries from directory filp Return a negative value from linux/errno.h. - Return > 0 if success (the length of the file name). + Return > 0 if success (the amount of byte written to dirent) */ static int UMSDOS_readdir( struct inode *dir, /* Point to a description of the super block */ @@ -644,7 +652,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) if (*pt == '/') *pt++ = '\0'; if (dir->u.umsdos_i.i_emd_dir == 0){ /* This is a DOS directory */ - ret = msdos_lookup(dir,start,len,result); + ret = umsdos_rlookup_x(dir,start,len,result,1); }else{ ret = umsdos_lookup_x(dir,start,len,result,1); } diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index e4d6a9470..b1ec47bf6 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -5,6 +5,10 @@ * * Extended MS-DOS directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/types.h> #include <linux/fcntl.h> #include <linux/kernel.h> diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index d292ea3c2..61eacaac6 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -7,6 +7,10 @@ * Extended MS-DOS regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> @@ -33,8 +37,10 @@ static int UMSDOS_file_read( { /* We have to set the access time because msdos don't care */ int ret = msdos_file_read(inode,filp,buf,count); - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + if (!IS_RDONLY(inode)){ + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } return ret; } /* @@ -58,16 +64,8 @@ static void UMSDOS_truncate(struct inode *inode) inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_dirt = 1; } -/* - See inode.c - - Some entry point are filled dynamically with function pointers - from the msdos file_operations and file_inode_operations. - - The idea is to have the code as independent as possible from - the msdos file system. -*/ +/* Function for normal file system (512 bytes hardware sector size) */ struct file_operations umsdos_file_operations = { NULL, /* lseek - default */ UMSDOS_file_read, /* read */ @@ -94,10 +92,41 @@ struct inode_operations umsdos_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + msdos_bmap, /* bmap */ UMSDOS_truncate,/* truncate */ NULL, /* permission */ msdos_smap /* smap */ }; +/* For other with larger and unaligned file system */ +struct file_operations umsdos_file_operations_no_bmap = { + NULL, /* lseek - default */ + UMSDOS_file_read, /* read */ + UMSDOS_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + msdos_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + file_fsync /* fsync */ +}; +struct inode_operations umsdos_file_inode_operations_no_bmap = { + &umsdos_file_operations_no_bmap, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + UMSDOS_truncate,/* truncate */ + NULL, /* permission */ + NULL, /* smap */ +}; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 40f7feb68..22be740cc 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -6,6 +6,14 @@ * */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/kernel.h> @@ -13,15 +21,9 @@ #include <linux/errno.h> #include <asm/segment.h> #include <linux/string.h> -#include <linux/ctype.h> #include <linux/stat.h> #include <linux/umsdos_fs.h> -#ifdef MODULE - #include <linux/module.h> - #include "../../tools/version.h" -#endif - struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */ /* directory. See UMSDOS_readdir_x() */ @@ -45,6 +47,9 @@ void UMSDOS_put_inode(struct inode *inode) PRINTK (("put inode %x owner %x pos %d dir %x\n",inode ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos ,inode->u.umsdos_i.i_emd_dir)); + if (inode != NULL && inode == pseudo_root){ + printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n"); + } msdos_put_inode(inode); } @@ -52,15 +57,13 @@ void UMSDOS_put_inode(struct inode *inode) void UMSDOS_put_super(struct super_block *sb) { msdos_put_super(sb); - #ifdef MODULE - MOD_DEC_USE_COUNT; - #endif + MOD_DEC_USE_COUNT; } -void UMSDOS_statfs(struct super_block *sb,struct statfs *buf) +void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { - msdos_statfs(sb,buf); + msdos_statfs(sb,buf,bufsiz); } @@ -152,18 +155,11 @@ void umsdos_patch_inode ( if (!umsdos_isinit(inode)){ inode->u.umsdos_i.i_emd_dir = 0; if (S_ISREG(inode->i_mode)){ - static char is_init = 0; - if (!is_init){ - /* - I don't want to change the msdos file system code - so I get the address of some subroutine dynamically - once. - */ - umsdos_file_inode_operations.bmap = inode->i_op->bmap; + if (inode->i_op->bmap != NULL){ inode->i_op = &umsdos_file_inode_operations; - is_init = 1; + }else{ + inode->i_op = &umsdos_file_inode_operations_no_bmap; } - inode->i_op = &umsdos_file_inode_operations; }else if (S_ISDIR(inode->i_mode)){ if (dir != NULL){ umsdos_setup_dir_inode(inode); @@ -408,8 +404,10 @@ struct super_block *UMSDOS_read_super( which do not have an EMD file. They behave like normal msdos directory, with all limitation of msdos. */ - struct super_block *sb = msdos_read_super(s,data,silent); - printk ("UMSDOS Alpha 0.5a (compatibility level %d.%d, fast msdos)\n" + struct super_block *sb; + MOD_INC_USE_COUNT; + sb = msdos_read_super(s,data,silent); + printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n" ,UMSDOS_VERSION,UMSDOS_RELEASE); if (sb != NULL){ sb->s_op = &umsdos_sops; @@ -420,8 +418,8 @@ struct super_block *UMSDOS_read_super( /* #Specification: pseudo root / mount When a umsdos fs is mounted, a special handling is done if it is the root partition. We check for the presence - of the file /linux/etc/init or /linux/etc/rc. - If one is there, we do a chroot("/linux"). + of the file /linux/etc/init or /linux/etc/rc or + /linux/sbin/init. If one is there, we do a chroot("/linux"). We check both because (see init/main.c) the kernel try to exec init at different place and if it fails @@ -454,32 +452,48 @@ struct super_block *UMSDOS_read_super( ,UMSDOS_PSDROOT_LEN,&pseudo)==0 && S_ISDIR(pseudo->i_mode)){ struct inode *etc = NULL; - struct inode *rc = NULL; + struct inode *sbin = NULL; + int pseudo_ok = 0; Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME)); if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0 && S_ISDIR(etc->i_mode)){ - struct inode *init; + struct inode *init = NULL; + struct inode *rc = NULL; Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME)); if ((umsdos_real_lookup (etc,"init",4,&init)==0 && S_ISREG(init->i_mode)) || (umsdos_real_lookup (etc,"rc",2,&rc)==0 && S_ISREG(rc->i_mode))){ - umsdos_setup_dir_inode (pseudo); - Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME)); - pseudo_root = pseudo; - pseudo->i_count++; - pseudo = NULL; + pseudo_ok = 1; } iput (init); iput (rc); } + if (!pseudo_ok + && umsdos_real_lookup (pseudo,"sbin",4,&sbin)==0 + && S_ISDIR(sbin->i_mode)){ + struct inode *init = NULL; + Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME)); + if (umsdos_real_lookup (sbin,"init",4,&init)==0 + && S_ISREG(init->i_mode)){ + pseudo_ok = 1; + } + iput (init); + } + if (pseudo_ok){ + umsdos_setup_dir_inode (pseudo); + Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME)); + pseudo_root = pseudo; + pseudo->i_count++; + pseudo = NULL; + } + iput (sbin); iput (etc); } iput (pseudo); } - #ifdef MODULE - MOD_INC_USE_COUNT; - #endif + } else { + MOD_DEC_USE_COUNT; } return sb; } @@ -501,12 +515,7 @@ int init_module(void) void cleanup_module(void) { - if (MOD_IN_USE) - printk("Umsdos: file system in use, remove delayed\n"); - else - { - unregister_filesystem(&umsdos_fs_type); - } + unregister_filesystem(&umsdos_fs_type); } #endif diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 972571796..d766ef939 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -5,6 +5,10 @@ * * Extended MS-DOS ioctl directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -160,6 +164,21 @@ int UMSDOS_ioctl_dir ( umsdos_parse (data.umsdos_dirent.name ,data.umsdos_dirent.name_len,&info); ret = umsdos_newentry (dir,&info); + }else if (cmd == UMSDOS_RENAME_DOS){ + /* #Specification: ioctl / UMSDOS_RENAME_DOS + A file or directory is rename in a DOS directory + (not moved across directory). The source name + is in the dos_dirent.name field and the destination + is in umsdos_dirent.name field. + + This ioctl allows umssync to rename a mangle file + name before syncing it back in the EMD. + */ + dir->i_count += 2; + ret = msdos_rename (dir + ,data.dos_dirent.d_name,data.dos_dirent.d_reclen + ,dir + ,data.umsdos_dirent.name,data.umsdos_dirent.name_len); }else if (cmd == UMSDOS_UNLINK_EMD){ /* #Specification: ioctl / UMSDOS_UNLINK_EMD The umsdos_dirent field of the struct umsdos_ioctl is used diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c index 1f59447e9..a7649a39a 100644 --- a/fs/umsdos/mangle.c +++ b/fs/umsdos/mangle.c @@ -6,6 +6,10 @@ * Control the mangling of file name to fit msdos name space. * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID) */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/ctype.h> #include <linux/string.h> @@ -222,13 +226,15 @@ int umsdos_parse ( with a special character as the first character of the extension will be mangled. This solve the following problem: - + + # touch FILE # FILE is invalid for DOS, so mangling is applied # file.{_1 is created in the DOS directory touch file.{_1 # To UMSDOS file point to a single DOS entry. # So file.{_1 has to be mangled. + # */ static char special[]={ SPECIAL_MANGLING,'\0' @@ -270,7 +276,9 @@ int umsdos_parse ( Control character are converted to #. Space are converted to #. The following character are also converted to #. + # " * + , / : ; < = > ? [ \ ] | ~ + # Sometime, the problem is not in MsDOS itself but in command.com. @@ -294,7 +302,7 @@ int umsdos_parse ( */ }else{ /* Conforming MSDOS file name */ - strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */ + strncpy (info->fake.fname,fname,len); info->msdos_reject = 0; base_len = firstpt != NULL ? (int)(firstpt - fname) : len; } @@ -316,10 +324,12 @@ int umsdos_parse ( Here is the list of DOS pseudo devices: + # "prn","con","aux","nul", "lpt1","lpt2","lpt3","lpt4", "com1","com2","com3","com4", "clock$" + # and some standard ones for common DOS programs diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 567039e14..42820bb98 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -6,6 +6,10 @@ * * Maintain and access the --linux alternate directory file. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -315,7 +319,7 @@ static int umsdos_rename_f( int flags) /* 0 == copy flags from old_name */ /* != 0, this is the value of flags */ { - int ret = EPERM; + int ret = -EPERM; struct umsdos_info old_info; int old_ret = umsdos_parse (old_name,old_len,&old_info); struct umsdos_info new_info; @@ -330,58 +334,79 @@ chkstk(); chkstk(); PRINTK (("ret %d ",ret)); if (ret == 0){ - PRINTK (("new newentry ")); - umsdos_ren_init(&new_info,&old_info,flags); - ret = umsdos_newentry (new_dir,&new_info); -chkstk(); - PRINTK (("ret %d %d ",ret,new_info.fake.len)); - if (ret == 0){ - PRINTK (("msdos_rename ")); - old_dir->i_count++; - new_dir->i_count++; /* Both inode are needed later */ - ret = msdos_rename (old_dir - ,old_info.fake.fname,old_info.fake.len - ,new_dir - ,new_info.fake.fname,new_info.fake.len); -chkstk(); - PRINTK (("after m_rename ret %d ",ret)); - if (ret != 0){ - umsdos_delentry (new_dir,&new_info - ,S_ISDIR(new_info.entry.mode)); -chkstk(); - }else{ - ret = umsdos_delentry (old_dir,&old_info - ,S_ISDIR(old_info.entry.mode)); + /* check sticky bit on old_dir */ + if ( !(old_dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == old_info.entry.uid || + current->fsuid == old_dir->i_uid ) { + /* Does new_name already exist? */ + PRINTK(("new findentry ")); + ret = umsdos_findentry(new_dir,&new_info,0); + if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */ + !(new_dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == new_info.entry.uid || + current->fsuid == new_dir->i_uid ) { + PRINTK (("new newentry ")); + umsdos_ren_init(&new_info,&old_info,flags); + ret = umsdos_newentry (new_dir,&new_info); chkstk(); + PRINTK (("ret %d %d ",ret,new_info.fake.len)); if (ret == 0){ - /* - This UMSDOS_lookup does not look very useful. - It makes sure that the inode of the file will - be correctly setup (umsdos_patch_inode()) in - case it is already in use. - - Not very efficient ... - */ - struct inode *inode; - new_dir->i_count++; - PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags)); - ret = UMSDOS_lookup (new_dir,new_name,new_len - ,&inode); + PRINTK (("msdos_rename ")); + old_dir->i_count++; + new_dir->i_count++; /* Both inode are needed later */ + ret = msdos_rename (old_dir + ,old_info.fake.fname,old_info.fake.len + ,new_dir + ,new_info.fake.fname,new_info.fake.len); chkstk(); + PRINTK (("after m_rename ret %d ",ret)); if (ret != 0){ - printk ("UMSDOS: partial rename for file %s\n" - ,new_info.entry.name); + umsdos_delentry (new_dir,&new_info + ,S_ISDIR(new_info.entry.mode)); +chkstk(); }else{ - /* - Update f_pos so notify_change will succeed - if the file was already in use. - */ - umsdos_set_dirinfo (inode,new_dir,new_info.f_pos); + ret = umsdos_delentry (old_dir,&old_info + ,S_ISDIR(old_info.entry.mode)); chkstk(); - iput (inode); + if (ret == 0){ + /* + This UMSDOS_lookup does not look very useful. + It makes sure that the inode of the file will + be correctly setup (umsdos_patch_inode()) in + case it is already in use. + + Not very efficient ... + */ + struct inode *inode; + new_dir->i_count++; + PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags)); + ret = UMSDOS_lookup (new_dir,new_name,new_len + ,&inode); +chkstk(); + if (ret != 0){ + printk ("UMSDOS: partial rename for file %s\n" + ,new_info.entry.name); + }else{ + /* + Update f_pos so notify_change will succeed + if the file was already in use. + */ + umsdos_set_dirinfo (inode,new_dir,new_info.f_pos); +chkstk(); + iput (inode); + } + } } } + }else{ + /* sticky bit set on new_dir */ + PRINTK(("sticky set on new ")); + ret = -EPERM; } + }else{ + /* sticky bit set on old_dir */ + PRINTK(("sticky set on old ")); + ret = -EPERM; } } umsdos_unlockcreate(old_dir); @@ -483,6 +508,7 @@ int UMSDOS_link ( Given a file /foo/file + # ln /foo/file /tmp/file2 become internally @@ -490,6 +516,7 @@ int UMSDOS_link ( mv /foo/file /foo/-LINK1 ln -s /foo/-LINK1 /foo/file ln -s /foo/-LINK1 /tmp/file2 + # Using this strategy, we can operate on /foo/file or /foo/file2. We can remove one and keep the other, like a normal Unix hard link. @@ -502,6 +529,7 @@ int UMSDOS_link ( The strategy for hard link introduces a side effect that may or may not be acceptable. Here is the sequence + # mkdir subdir1 touch subdir1/file mkdir subdir2 @@ -509,6 +537,7 @@ int UMSDOS_link ( rm subdir1/file rmdir subdir1 rmdir: subdir1: Directory not empty + # This happen because there is an invisible file (--link) in subdir1 which is referenced by subdir2/file. @@ -519,12 +548,14 @@ int UMSDOS_link ( Another weakness of hard link come from the fact that it is based on hidden symbolic links. Here is an example. + # mkdir /subdir1 touch /subdir1/file mkdir /subdir2 ln /subdir1/file subdir2/file mv /subdir1 subdir3 ls -l /subdir2/file + # Since /subdir2/file is a hidden symbolic link to /subdir1/..hlinkNNN, accessing it will fail since @@ -765,6 +796,7 @@ int UMSDOS_rmdir( but you rapidly get iput() all around. Here is an exemple of what I am trying to avoid. + # if (a){ ... if(b){ @@ -783,10 +815,12 @@ int UMSDOS_rmdir( } // Was iput finally done ? return status; + # Here is the style I am using. Still sometime I do the first when things are very simple (or very complicated :-( ) + # if (a){ if (b){ ... @@ -797,6 +831,7 @@ int UMSDOS_rmdir( ... } return status; + # Again, while this help clarifying the code, I often get a lot of iput(), unlike the first style, where I can place few @@ -812,6 +847,7 @@ int UMSDOS_rmdir( where an iput() is done, the inode is simply nulled, disabling the last one. + # if (a){ if (b){ ... @@ -824,6 +860,7 @@ int UMSDOS_rmdir( } iput (dir); return status; + # Note that the umsdos_lockcreate() and umsdos_unlockcreate() function pair goes against this practice of "forgetting" the inode as soon @@ -842,28 +879,37 @@ int UMSDOS_rmdir( ret = -EBUSY; }else if ((empty = umsdos_isempty (sdir)) != 0){ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count)); - if (empty == 1){ - /* We have to removed the EMD file */ - ret = msdos_unlink(sdir,UMSDOS_EMD_FILE - ,UMSDOS_EMD_NAMELEN); + /* check sticky bit */ + if ( !(dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == sdir->i_uid || + current->fsuid == dir->i_uid ) { + if (empty == 1){ + /* We have to removed the EMD file */ + ret = msdos_unlink(sdir,UMSDOS_EMD_FILE + ,UMSDOS_EMD_NAMELEN); + sdir = NULL; + } + /* sdir must be free before msdos_rmdir() */ + iput (sdir); sdir = NULL; - } - /* sdir must be free before msdos_rmdir() */ - iput (sdir); - sdir = NULL; - PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink)); - if (ret == 0){ - struct umsdos_info info; - dir->i_count++; - umsdos_parse (name,len,&info); - /* The findentry is there only to complete */ - /* the mangling */ - umsdos_findentry (dir,&info,2); - ret = msdos_rmdir (dir,info.fake.fname - ,info.fake.len); + PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink)); if (ret == 0){ - ret = umsdos_delentry (dir,&info,1); + struct umsdos_info info; + dir->i_count++; + umsdos_parse (name,len,&info); + /* The findentry is there only to complete */ + /* the mangling */ + umsdos_findentry (dir,&info,2); + ret = msdos_rmdir (dir,info.fake.fname + ,info.fake.len); + if (ret == 0){ + ret = umsdos_delentry (dir,&info,1); + } } + }else{ + /* sticky bit set and we don't have permission */ + PRINTK(("sticky set ")); + ret = -EPERM; } }else{ /* @@ -887,63 +933,72 @@ int UMSDOS_unlink ( const char * name, int len) { - struct umsdos_info info; int ret = umsdos_nevercreat(dir,name,len,-EPERM); if (ret == 0){ + struct umsdos_info info; ret = umsdos_parse (name,len,&info); if (ret == 0){ umsdos_lockcreate(dir); ret = umsdos_findentry(dir,&info,1); if (ret == 0){ PRINTK (("UMSDOS_unlink %s ",info.fake.fname)); - if (info.entry.flags & UMSDOS_HLINK){ - /* #Specification: hard link / deleting a link - When we deletes a file, and this file is a link - we must subtract 1 to the nlink field of the - hidden link. - - If the count goes to 0, we delete this hidden - link too. - */ - /* - First, get the inode of the hidden link - using the standard lookup function. - */ - struct inode *inode; - dir->i_count++; - ret = UMSDOS_lookup (dir,name,len,&inode); - if (ret == 0){ - PRINTK (("unlink nlink = %d ",inode->i_nlink)); - inode->i_nlink--; - if (inode->i_nlink == 0){ - struct inode *hdir = iget(inode->i_sb - ,inode->u.umsdos_i.i_dir_owner); - struct umsdos_dirent entry; - ret = umsdos_inode2entry (hdir,inode,&entry); - if (ret == 0){ - ret = UMSDOS_unlink (hdir,entry.name - ,entry.name_len); + /* check sticky bit */ + if ( !(dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == info.entry.uid || + current->fsuid == dir->i_uid ) { + if (info.entry.flags & UMSDOS_HLINK){ + /* #Specification: hard link / deleting a link + When we deletes a file, and this file is a link + we must subtract 1 to the nlink field of the + hidden link. + + If the count goes to 0, we delete this hidden + link too. + */ + /* + First, get the inode of the hidden link + using the standard lookup function. + */ + struct inode *inode; + dir->i_count++; + ret = UMSDOS_lookup (dir,name,len,&inode); + if (ret == 0){ + PRINTK (("unlink nlink = %d ",inode->i_nlink)); + inode->i_nlink--; + if (inode->i_nlink == 0){ + struct inode *hdir = iget(inode->i_sb + ,inode->u.umsdos_i.i_dir_owner); + struct umsdos_dirent entry; + ret = umsdos_inode2entry (hdir,inode,&entry); + if (ret == 0){ + ret = UMSDOS_unlink (hdir,entry.name + ,entry.name_len); + }else{ + iput (hdir); + } }else{ - iput (hdir); + struct iattr newattrs; + newattrs.ia_valid = 0; + ret = UMSDOS_notify_change (inode, &newattrs); } - }else{ - struct iattr newattrs; - newattrs.ia_valid = 0; - ret = UMSDOS_notify_change (inode, &newattrs); + iput (inode); } - iput (inode); } - } - if (ret == 0){ - ret = umsdos_delentry (dir,&info,0); if (ret == 0){ - PRINTK (("Avant msdos_unlink %s ",info.fake.fname)); - dir->i_count++; - ret = msdos_unlink_umsdos (dir,info.fake.fname - ,info.fake.len); - PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname - ,info.entry.mode,ret)); + ret = umsdos_delentry (dir,&info,0); + if (ret == 0){ + PRINTK (("Avant msdos_unlink %s ",info.fake.fname)); + dir->i_count++; + ret = msdos_unlink_umsdos (dir,info.fake.fname + ,info.fake.len); + PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname + ,info.entry.mode,ret)); + } } + }else{ + /* sticky bit set and we've not got permission */ + PRINTK(("sticky set ")); + ret = -EPERM; } } umsdos_unlockcreate(dir); @@ -986,7 +1041,7 @@ int UMSDOS_rename( ,new_len,0); if (ret == -EEXIST){ /* #Specification: rename / new name exist - If the destination name already exist, it will + If the destination name already exist, it will silently be removed. EXT2 does it this way and this is the spec of SUNOS. So does UMSDOS. diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index d7272ed96..d708709bd 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -7,6 +7,10 @@ * (For directory without EMD file). */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -32,8 +36,10 @@ static int UMSDOS_rreaddir ( { int ret = 0; while (1){ + int len = -1; ret = msdos_readdir(dir,filp,dirent,count); - if (ret == 5 + if (ret > 0) len = get_fs_word(&dirent->d_reclen); + if (len == 5 && pseudo_root != NULL && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){ /* @@ -45,7 +51,7 @@ static int UMSDOS_rreaddir ( if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break; }else{ if (pseudo_root != NULL - && ret == 2 + && len == 2 && dir == dir->i_sb->s_mounted && dir == pseudo_root->i_sb->s_mounted){ char name[2]; @@ -60,11 +66,18 @@ static int UMSDOS_rreaddir ( return ret; } -int UMSDOS_rlookup( +/* + Lookup into a non promoted directory. + If the result is a directory, make sure we find out if it is + a promoted one or not (calling umsdos_setup_dir_inode(inode)). +*/ +int umsdos_rlookup_x( struct inode *dir, const char *name, int len, - struct inode **result) /* Will hold inode of the file, if successful */ + struct inode **result, /* Will hold inode of the file, if successful */ + int nopseudo) /* Don't care about pseudo root mode */ + /* so locating "linux" will work */ { int ret; if (pseudo_root != NULL @@ -84,7 +97,7 @@ int UMSDOS_rlookup( ret = umsdos_real_lookup (dir,name,len,result); if (ret == 0){ struct inode *inode = *result; - if (inode == pseudo_root){ + if (inode == pseudo_root && !nopseudo){ /* #Specification: pseudo root / DOS/linux Even in the real root directory (c:\), the directory /linux won't show @@ -102,6 +115,14 @@ int UMSDOS_rlookup( iput (dir); return ret; } +int UMSDOS_rlookup( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + return umsdos_rlookup_x(dir,name,len,result,0); +} static int UMSDOS_rrmdir ( struct inode *dir, diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 1b1e561c2..6ab27c5dd 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -6,6 +6,9 @@ * * Extended MS-DOS regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile index 097563244..b8a19d0bf 100644 --- a/fs/xiafs/Makefile +++ b/fs/xiafs/Makefile @@ -20,6 +20,9 @@ OBJS= bitmap.o truncate.o namei.o inode.o \ xiafs.o: $(OBJS) $(LD) -r -o xiafs.o $(OBJS) +modules: xiafs.o + ln -sf ../fs/xiafs/xiafs.o $(TOPDIR)/modules + dep: $(CPP) -M *.c > .depend diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c index ca93d1546..bca7e4367 100644 --- a/fs/xiafs/bitmap.c +++ b/fs/xiafs/bitmap.c @@ -11,6 +11,10 @@ /* bitmap.c contains the code that handles the inode and block bitmaps */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/locks.h> #include <linux/xia_fs.h> @@ -54,7 +58,7 @@ zone_found: for (j=0; j < 32; j++) if (tmp & (1 << j)) break; - if (set_bit(j, bmap+i)) { + if (set_bit(j,bmap+i)) { start_bit=j + (i << 5) + 1; goto repeat; } diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c index d9db56ddc..5a88c2f0b 100644 --- a/fs/xiafs/dir.c +++ b/fs/xiafs/dir.c @@ -9,6 +9,10 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> #include <linux/errno.h> @@ -19,11 +23,8 @@ #include "xiafs_mac.h" -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - static int xiafs_dir_read(struct inode *, struct file *, char *, int); -static int xiafs_readdir(struct inode *, struct file *, struct dirent *, int); +static int xiafs_readdir(struct inode *, struct file *, void *, filldir_t); static struct file_operations xiafs_dir_operations = { NULL, /* lseek - default */ @@ -65,10 +66,10 @@ static int xiafs_dir_read(struct inode * inode, return -EISDIR; } -static int xiafs_readdir(struct inode * inode, - struct file * filp, struct dirent * dirent, int count) +static int xiafs_readdir(struct inode * inode, struct file * filp, + void * dirent, filldir_t filldir) { - u_int offset, i,ret; + u_int offset, i; struct buffer_head * bh; struct xiafs_direct * de; @@ -76,8 +77,7 @@ static int xiafs_readdir(struct inode * inode, return -EBADF; if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1) ) return -EBADF; - ret = 0; - while (!ret && filp->f_pos < inode->i_size) { + while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (XIAFS_ZSIZE(inode->i_sb) - 1); bh = xiafs_bread(inode, filp->f_pos >> XIAFS_ZSIZE_BITS(inode->i_sb),0); if (!bh) { @@ -93,7 +93,7 @@ static int xiafs_readdir(struct inode * inode, offset = i; de = (struct xiafs_direct *) (offset + bh->b_data); - while (!ret && offset < XIAFS_ZSIZE(inode->i_sb) && filp->f_pos < inode->i_size) { + while (offset < XIAFS_ZSIZE(inode->i_sb) && filp->f_pos < inode->i_size) { if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes || de->d_rec_len < 12 || (char *)de+de->d_rec_len > XIAFS_ZSIZE(inode->i_sb)+bh->b_data || @@ -104,21 +104,18 @@ static int xiafs_readdir(struct inode * inode, brelse(bh); return 0; } - offset += de->d_rec_len; - filp->f_pos += de->d_rec_len; if (de->d_ino) { - for (i = 0; i < de->d_name_len ; i++) - put_fs_byte(de->d_name[i],i+dirent->d_name); - put_fs_byte(0,i+dirent->d_name); - put_fs_long(de->d_ino,&dirent->d_ino); - put_fs_word(i,&dirent->d_reclen); if (!IS_RDONLY (inode)) { - inode->i_atime=CURRENT_TIME; + inode->i_atime=CURRENT_TIME; inode->i_dirt=1; } - ret = ROUND_UP(NAME_OFFSET(dirent)+i+1); - break; + if (filldir(dirent, de->d_name, de->d_name_len, filp->f_pos, de->d_ino) < 0) { + brelse(bh); + return 0; + } } + offset += de->d_rec_len; + filp->f_pos += de->d_rec_len; de = (struct xiafs_direct *) (offset + bh->b_data); } brelse(bh); @@ -131,5 +128,5 @@ static int xiafs_readdir(struct inode * inode, inode->i_atime=CURRENT_TIME; inode->i_dirt=1; } - return ret; + return 0; } diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c index 5678ffd0b..c67daaed9 100644 --- a/fs/xiafs/file.c +++ b/fs/xiafs/file.c @@ -9,6 +9,10 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/xiafs/fsync.c b/fs/xiafs/fsync.c index 67681b2c6..5bd0a7066 100644 --- a/fs/xiafs/fsync.c +++ b/fs/xiafs/fsync.c @@ -8,6 +8,10 @@ * xiafs fsync primitive */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c index 171499a95..5f9fc833a 100644 --- a/fs/xiafs/inode.c +++ b/fs/xiafs/inode.c @@ -9,6 +9,14 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/sched.h> #include <linux/xia_fs.h> #include <linux/kernel.h> @@ -43,6 +51,7 @@ void xiafs_put_super(struct super_block *sb) for(i = 0 ; i < _XIAFS_ZMAP_SLOTS ; i++) brelse(sb->u.xiafs_sb.s_zmap_buf[i]); unlock_super(sb); + MOD_DEC_USE_COUNT; } static struct super_operations xiafs_sops = { @@ -63,6 +72,7 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data, struct xiafs_super_block *sp; int i, z, dev; + MOD_INC_USE_COUNT; dev=s->s_dev; lock_super(s); @@ -72,6 +82,7 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data, s->s_dev=0; unlock_super(s); printk("XIA-FS: read super_block failed (%s %d)\n", WHERE_ERR); + MOD_DEC_USE_COUNT; return NULL; } sp = (struct xiafs_super_block *) bh->b_data; @@ -83,6 +94,7 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data, if (!silent) printk("VFS: Can't find a xiafs filesystem on dev 0x%04x.\n", dev); + MOD_DEC_USE_COUNT; return NULL; } s->s_blocksize = sp->s_zone_size; @@ -93,7 +105,10 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data, brelse(bh); set_blocksize(dev, s->s_blocksize); bh = bread (dev, 0, s->s_blocksize); - if(!bh) return NULL; + if(!bh) { + MOD_DEC_USE_COUNT; + return NULL; + } sp = (struct xiafs_super_block *) (((char *)bh->b_data) + BLOCK_SIZE) ; }; s->u.xiafs_sb.s_nzones = sp->s_nzones; @@ -152,23 +167,23 @@ xiafs_read_super_fail: s->s_dev=0; unlock_super(s); printk("XIA-FS: read bitmaps failed (%s %d)\n", WHERE_ERR); + MOD_DEC_USE_COUNT; return NULL; } -void xiafs_statfs(struct super_block *sb, struct statfs *buf) +void xiafs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - long tmp; - - put_fs_long(_XIAFS_SUPER_MAGIC, &buf->f_type); - put_fs_long(XIAFS_ZSIZE(sb), &buf->f_bsize); - put_fs_long(sb->u.xiafs_sb.s_ndatazones, &buf->f_blocks); - tmp = xiafs_count_free_zones(sb); - put_fs_long(tmp, &buf->f_bfree); - put_fs_long(tmp, &buf->f_bavail); - put_fs_long(sb->u.xiafs_sb.s_ninodes, &buf->f_files); - put_fs_long(xiafs_count_free_inodes(sb), &buf->f_ffree); - put_fs_long(_XIAFS_NAME_LEN, &buf->f_namelen); - /* don't know what should be put in buf->f_fsid */ + struct statfs tmp; + + tmp.f_type = _XIAFS_SUPER_MAGIC; + tmp.f_bsize = XIAFS_ZSIZE(sb); + tmp.f_blocks = sb->u.xiafs_sb.s_ndatazones; + tmp.f_bfree = xiafs_count_free_zones(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = sb->u.xiafs_sb.s_ninodes; + tmp.f_ffree = xiafs_count_free_inodes(sb); + tmp.f_namelen = _XIAFS_NAME_LEN; + memcpy_tofs(buf, &tmp, bufsiz); } static int zone_bmap(struct buffer_head * bh, int nr) @@ -500,3 +515,26 @@ int xiafs_sync_inode (struct inode *inode) brelse (bh); return err; } + +#ifdef MODULE + +/* Every kernel module contains stuff like this. */ + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type xiafs_fs_type = { + xiafs_read_super, "xiafs", 1, NULL +}; + +int init_module(void) +{ + register_filesystem(&xiafs_fs_type); + return 0; +} + +void cleanup_module(void) +{ + unregister_filesystem(&xiafs_fs_type); +} + +#endif diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c index 0532b1754..46b3590b2 100644 --- a/fs/xiafs/namei.c +++ b/fs/xiafs/namei.c @@ -9,6 +9,10 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/sched.h> #include <linux/xia_fs.h> #include <linux/kernel.h> @@ -356,6 +360,7 @@ int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode) inode->i_op = &xiafs_dir_inode_operations; inode->i_size = XIAFS_ZSIZE(dir->i_sb); inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_dirt = 1; dir_block = xiafs_bread(inode,0,1); if (!dir_block) { iput(dir); @@ -758,8 +763,7 @@ try_again: retval = -EEXIST; if (new_bh) goto end_rename; - retval = -EACCES; - if (!permission(old_inode, MAY_WRITE)) + if ((retval = permission(old_inode, MAY_WRITE)) != 0) goto end_rename; retval = -EINVAL; if (subdir(new_dir, old_inode)) diff --git a/fs/xiafs/symlink.c b/fs/xiafs/symlink.c index 757ad5796..1c64ebc6d 100644 --- a/fs/xiafs/symlink.c +++ b/fs/xiafs/symlink.c @@ -9,6 +9,10 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c index bdb9d39be..2e8dec10d 100644 --- a/fs/xiafs/truncate.c +++ b/fs/xiafs/truncate.c @@ -9,6 +9,10 @@ * This software may be redistributed per Linux Copyright. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/sched.h> #include <linux/xia_fs.h> |