diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /fs | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'fs')
146 files changed, 14738 insertions, 2037 deletions
diff --git a/fs/Config.in b/fs/Config.in index a2ddc3916..9a6a53960 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -31,6 +31,11 @@ else define_bool CONFIG_JOLIET n fi +tristate 'UDF filesystem support' CONFIG_UDF_FS +if [ "$CONFIG_UDF_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' UDF read-write support (EXPERIMENTAL)' CONFIG_UDF_RW +fi + tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -71,11 +76,10 @@ if [ "$CONFIG_INET" = "y" ]; then if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then bool ' Root file system on NFS' CONFIG_ROOT_NFS fi - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'NFS server support' CONFIG_NFSD - if [ "$CONFIG_NFSD" != "n" ]; then - bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN - fi + # considering that RedHat-6.0 ships with this on, I guess it's not really experimental + tristate 'NFS server support' CONFIG_NFSD + if [ "$CONFIG_NFSD" != "n" ]; then + bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN fi if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_bool CONFIG_SUNRPC y @@ -103,24 +107,7 @@ fi mainmenu_option next_comment comment 'Partition Types' - -bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL -bool 'Macintosh partition map support' CONFIG_MAC_PARTITION -bool 'OSF partition table support' CONFIG_OSF_PARTITION -bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL -bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION -bool 'SGI disklabel support' CONFIG_SGI_DISKLABEL -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Ultrix partition table support (experimental)' CONFIG_ULTRIX_PARTITION - bool 'Unixware slices support (EXPERIMENTAL)' CONFIG_UNIXWARE_DISKLABEL -fi - +source fs/partitions/Config.in endmenu - -if [ "$CONFIG_AFFS_FS" != "n" ]; then - define_bool CONFIG_AMIGA_PARTITION y -fi - source fs/nls/Config.in - endmenu diff --git a/fs/Makefile b/fs/Makefile index 069b1a2a4..8bfbd050b 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,12 +13,14 @@ O_TARGET := fs.o O_OBJS = open.o read_write.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 readdir.o select.o fifo.o locks.o filesystems.o \ - dcache.o inode.o attr.o bad_inode.o $(BINFMTS) + dcache.o inode.o attr.o bad_inode.o file.o iobuf.o $(BINFMTS) MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs qnx4 + nfsd nls devpts adfs partitions qnx4 udf + +SUB_DIRS := partitions ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -223,6 +225,14 @@ else endif endif +ifeq ($(CONFIG_UDF_FS),y) +SUB_DIRS += udf +else + ifeq ($(CONFIG_UDF_FS),m) + MOD_SUB_DIRS += udf + endif +endif + ifeq ($(CONFIG_AUTOFS_FS),y) SUB_DIRS += autofs else diff --git a/fs/adfs/super.c b/fs/adfs/super.c index e5f54f414..c8db4d2e1 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -385,7 +385,7 @@ static struct file_system_type adfs_fs_type = { "adfs", FS_REQUIRES_DEV, adfs_read_super, NULL }; -__initfunc(int init_adfs_fs(void)) +int __init init_adfs_fs(void) { return register_filesystem(&adfs_fs_type); } diff --git a/fs/affs/super.c b/fs/affs/super.c index 9084a4cf3..fe5fd1042 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -688,7 +688,7 @@ static struct file_system_type affs_fs_type = { NULL }; -__initfunc(int init_affs_fs(void)) +int __init init_affs_fs(void) { return register_filesystem(&affs_fs_type); } diff --git a/fs/autofs/init.c b/fs/autofs/init.c index 2afca0547..a4c5184f2 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -34,7 +34,7 @@ void cleanup_module(void) #else /* MODULE */ -__initfunc(int init_autofs_fs(void)) +int __init init_autofs_fs(void) { return register_filesystem(&autofs_fs_type); } diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 52c1f7aab..0e5c0a609 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -31,16 +31,12 @@ 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); +static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file); extern void dump_thread(struct pt_regs *, struct user *); static struct linux_binfmt aout_format = { -#ifndef MODULE - NULL, NULL, load_aout_binary, load_aout_library, aout_core_dump -#else - NULL, &__this_module, load_aout_binary, load_aout_library, aout_core_dump -#endif + NULL, THIS_MODULE, load_aout_binary, load_aout_library, aout_core_dump, PAGE_SIZE }; static void set_brk(unsigned long start, unsigned long end) @@ -64,12 +60,12 @@ static int dump_write(struct file *file, const void *addr, int nr) #define DUMP_WRITE(addr, nr) \ if (!dump_write(file, (void *)(addr), (nr))) \ - goto close_coredump; + goto end_coredump; #define DUMP_SEEK(offset) \ if (file->f_op->llseek) { \ if (file->f_op->llseek(file,(offset),0) != (offset)) \ - goto close_coredump; \ + goto end_coredump; \ } else file->f_pos = (offset) /* @@ -83,14 +79,10 @@ if (file->f_op->llseek) { \ */ static inline int -do_aout_core_dump(long signr, struct pt_regs * regs) +do_aout_core_dump(long signr, struct pt_regs * regs, struct file *file) { - struct dentry * dentry = NULL; - struct inode * inode = NULL; - struct file * file; mm_segment_t fs; int has_dumped = 0; - char corefile[6+sizeof(current->comm)]; unsigned long dump_start, dump_size; struct user dump; #if defined(__alpha__) @@ -106,32 +98,8 @@ do_aout_core_dump(long signr, struct pt_regs * regs) # define START_STACK(u) (u.start_stack) #endif - if (!current->dumpable || atomic_read(¤t->mm->count) != 1) - return 0; - current->dumpable = 0; - -/* See if we have enough room to write the upage. */ - if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE) - return 0; fs = get_fs(); set_fs(KERNEL_DS); - memcpy(corefile,"core.",5); -#if 0 - memcpy(corefile+5,current->comm,sizeof(current->comm)); -#else - corefile[4] = '\0'; -#endif - file = filp_open(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(file)) - goto end_coredump; - dentry = file->f_dentry; - inode = dentry->d_inode; - if (!S_ISREG(inode->i_mode)) - goto close_coredump; - if (!inode->i_op || !inode->i_op->default_file_ops) - goto close_coredump; - if (!file->f_op->write) - goto close_coredump; has_dumped = 1; current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(current->comm)); @@ -210,20 +178,18 @@ do_aout_core_dump(long signr, struct pt_regs * regs) /* Finally dump the task struct. Not be used by gdb, but could be useful */ set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); -close_coredump: - filp_close(file, NULL); end_coredump: set_fs(fs); return has_dumped; } static int -aout_core_dump(long signr, struct pt_regs * regs) +aout_core_dump(long signr, struct pt_regs * regs, struct file *file) { int retval; MOD_INC_USE_COUNT; - retval = do_aout_core_dump(signr, regs); + retval = do_aout_core_dump(signr, regs, file); MOD_DEC_USE_COUNT; return retval; } @@ -349,7 +315,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs current->personality = PER_LINUX; #if defined(__sparc__) && !defined(__sparc_v9__) - memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); + memcpy(¤t->thread.core_exec, &ex, sizeof(struct exec)); #endif current->mm->end_code = ex.a_text + @@ -587,17 +553,15 @@ load_aout_library(int fd) } -int __init init_aout_binfmt(void) +static int __init init_aout_binfmt(void) { return register_binfmt(&aout_format); } -#ifdef MODULE -int init_module(void) { - return init_aout_binfmt(); -} - -void cleanup_module( void) { +static void __exit exit_aout_binfmt(void) +{ unregister_binfmt(&aout_format); } -#endif + +module_init(init_aout_binfmt) +module_exit(exit_aout_binfmt) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index e3087a18b..aa59a2f7f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -54,7 +54,7 @@ extern void dump_thread(struct pt_regs *, struct user *); * don't even try. */ #ifdef USE_ELF_CORE_DUMP -static int elf_core_dump(long signr, struct pt_regs * regs); +static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file); #else #define elf_core_dump NULL #endif @@ -64,11 +64,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs); #define ELF_PAGEALIGN(_v) (((_v) + ELF_EXEC_PAGESIZE - 1) & ~(ELF_EXEC_PAGESIZE - 1)) static struct linux_binfmt elf_format = { -#ifndef MODULE - NULL, NULL, load_elf_binary, load_elf_library, elf_core_dump -#else - NULL, &__this_module, load_elf_binary, load_elf_library, elf_core_dump -#endif + NULL, THIS_MODULE, load_elf_binary, load_elf_library, elf_core_dump, ELF_EXEC_PAGESIZE }; static void set_brk(unsigned long start, unsigned long end) @@ -1026,10 +1022,10 @@ static int writenote(struct memelfnote *men, struct file *file) #define DUMP_WRITE(addr, nr) \ if (!dump_write(file, (addr), (nr))) \ - goto close_coredump; + goto end_coredump; #define DUMP_SEEK(off) \ if (!dump_seek(file, (off))) \ - goto close_coredump; + goto end_coredump; /* * Actual dumper * @@ -1037,14 +1033,10 @@ static int writenote(struct memelfnote *men, struct file *file) * and then they are actually written out. If we run out of core limit * we just truncate. */ -static int elf_core_dump(long signr, struct pt_regs * regs) +static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file) { int has_dumped = 0; - struct file *file; - struct dentry *dentry; - struct inode *inode; mm_segment_t fs; - char corefile[6+sizeof(current->comm)]; int segs; int i; size_t size; @@ -1058,12 +1050,6 @@ static int elf_core_dump(long signr, struct pt_regs * regs) elf_fpregset_t fpu; /* NT_PRFPREG */ struct elf_prpsinfo psinfo; /* NT_PRPSINFO */ - if (!current->dumpable || - limit < ELF_EXEC_PAGESIZE || - atomic_read(¤t->mm->count) != 1) - return 0; - current->dumpable = 0; - #ifndef CONFIG_BINFMT_ELF MOD_INC_USE_COUNT; #endif @@ -1112,27 +1098,6 @@ static int elf_core_dump(long signr, struct pt_regs * regs) fs = get_fs(); set_fs(KERNEL_DS); - memcpy(corefile,"core.",5); -#if 0 - memcpy(corefile+5,current->comm,sizeof(current->comm)); -#else - corefile[4] = '\0'; -#endif - file = filp_open(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); - if (IS_ERR(file)) - goto end_coredump; - dentry = file->f_dentry; - inode = dentry->d_inode; - if (inode->i_nlink > 1) - goto close_coredump; /* multiple links - don't dump */ - - if (!S_ISREG(inode->i_mode)) - goto close_coredump; - if (!inode->i_op || !inode->i_op->default_file_ops) - goto close_coredump; - if (!file->f_op->write) - goto close_coredump; - has_dumped = 1; current->flags |= PF_DUMPCORE; @@ -1289,7 +1254,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) for(i = 0; i < numnote; i++) if (!writenote(¬es[i], file)) - goto close_coredump; + goto end_coredump; set_fs(fs); @@ -1316,9 +1281,6 @@ static int elf_core_dump(long signr, struct pt_regs * regs) (off_t) file->f_pos, offset); } - close_coredump: - filp_close(file, NULL); - end_coredump: set_fs(fs); #ifndef CONFIG_BINFMT_ELF @@ -1328,26 +1290,16 @@ static int elf_core_dump(long signr, struct pt_regs * regs) } #endif /* USE_ELF_CORE_DUMP */ -int __init init_elf_binfmt(void) +static int __init init_elf_binfmt(void) { return register_binfmt(&elf_format); } -#ifdef MODULE - -int init_module(void) -{ - /* Install the COFF, ELF and XOUT loaders. - * N.B. We *rely* on the table being the right size with the - * right number of free slots... - */ - return init_elf_binfmt(); -} - - -void cleanup_module( void) +static void __exit exit_elf_binfmt(void) { /* Remove the COFF and ELF loaders. */ unregister_binfmt(&elf_format); } -#endif + +module_init(init_elf_binfmt) +module_exit(exit_elf_binfmt) diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index f102f2dec..aa2deaf6e 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -105,25 +105,18 @@ static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) } struct linux_binfmt em86_format = { -#ifndef MODULE - NULL, 0, load_em86, NULL, NULL -#else - NULL, &__this_module, load_em86, NULL, NULL -#endif + NULL, THIS_MODULE, load_em86, NULL, NULL, 0 }; -int __init init_em86_binfmt(void) +static int __init init_em86_binfmt(void) { return register_binfmt(&em86_format); } -#ifdef MODULE -int init_module(void) +static void __exit exit_em86_binfmt(void) { - return init_em86_binfmt(); -} - -void cleanup_module( void) { unregister_binfmt(&em86_format); } -#endif + +module_init(init_em86_binfmt) +module_exit(exit_em86_binfmt) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 7b9cead81..6143bd5d0 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -27,8 +27,8 @@ #include <linux/proc_fs.h> #include <linux/string.h> #include <linux/ctype.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> -#include <asm/spinlock.h> /* * We should make this work with a "stub-only" /proc, @@ -64,11 +64,7 @@ static void entry_proc_cleanup(struct binfmt_entry *e); static int entry_proc_setup(struct binfmt_entry *e); static struct linux_binfmt misc_format = { -#ifndef MODULE - NULL, 0, load_misc_binary, NULL, NULL -#else - NULL, &__this_module, load_misc_binary, NULL, NULL -#endif + NULL, THIS_MODULE, load_misc_binary, NULL, NULL, 0 }; static struct proc_dir_entry *bm_dir = NULL; @@ -493,7 +489,7 @@ static void bm_modcount(struct inode *inode, int fill) } #endif -int __init init_misc_binfmt(void) +static int __init init_misc_binfmt(void) { int error = -ENOENT; struct proc_dir_entry *status = NULL, *reg; @@ -528,14 +524,7 @@ cleanup_bm: goto out; } -#ifdef MODULE -EXPORT_NO_SYMBOLS; -int init_module(void) -{ - return init_misc_binfmt(); -} - -void cleanup_module(void) +static void __exit exit_misc_binfmt(void) { unregister_binfmt(&misc_format); remove_proc_entry("register", bm_dir); @@ -543,5 +532,8 @@ void cleanup_module(void) clear_entries(); remove_proc_entry("sys/fs/binfmt_misc", NULL); } -#endif -#undef VERBOSE_STATUS + +EXPORT_NO_SYMBOLS; + +module_init(init_misc_binfmt) +module_exit(exit_misc_binfmt) diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 8cc685aca..3dad604e3 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -101,25 +101,18 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs) } struct linux_binfmt script_format = { -#ifndef MODULE - NULL, 0, load_script, NULL, NULL -#else - NULL, &__this_module, load_script, NULL, NULL -#endif + NULL, THIS_MODULE, load_script, NULL, NULL, 0 }; -int __init init_script_binfmt(void) +static int __init init_script_binfmt(void) { return register_binfmt(&script_format); } -#ifdef MODULE -int init_module(void) +static void __exit exit_script_binfmt(void) { - return init_script_binfmt(); -} - -void cleanup_module( void) { unregister_binfmt(&script_format); } -#endif + +module_init(init_script_binfmt) +module_exit(exit_script_binfmt) diff --git a/fs/buffer.c b/fs/buffer.c index 108b385ea..c4fefe5cc 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -40,10 +40,12 @@ #include <linux/file.h> #include <linux/init.h> #include <linux/quotaops.h> +#include <linux/iobuf.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/bitops.h> +#include <asm/mmu_context.h> #define NR_SIZES 7 static char buffersize_index[65] = @@ -144,8 +146,8 @@ void __wait_on_buffer(struct buffer_head * bh) atomic_inc(&bh->b_count); add_wait_queue(&bh->b_wait, &wait); repeat: - tsk->state = TASK_UNINTERRUPTIBLE; run_task_queue(&tq_disk); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (buffer_locked(bh)) { schedule(); goto repeat; @@ -225,7 +227,6 @@ repeat: continue; atomic_inc(&bh->b_count); - bh->b_flushtime = 0; spin_unlock(&lru_list_lock); ll_rw_block(WRITE, 1, &bh); atomic_dec(&bh->b_count); @@ -306,7 +307,7 @@ int fsync_dev(kdev_t dev) return sync_buffers(dev, 1); } -asmlinkage int sys_sync(void) +asmlinkage long sys_sync(void) { fsync_dev(0); return 0; @@ -336,7 +337,7 @@ int file_fsync(struct file *filp, struct dentry *dentry) return sync_buffers(dev, 1); } -asmlinkage int sys_fsync(unsigned int fd) +asmlinkage long sys_fsync(unsigned int fd) { struct file * file; struct dentry * dentry; @@ -373,7 +374,7 @@ out: return err; } -asmlinkage int sys_fdatasync(unsigned int fd) +asmlinkage long sys_fdatasync(unsigned int fd) { struct file * file; struct dentry * dentry; @@ -435,7 +436,6 @@ void invalidate_buffers(kdev_t dev) } if (atomic_read(&bh->b_count)) continue; - bh->b_flushtime = 0; clear_bit(BH_Protected, &bh->b_state); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Dirty, &bh->b_state); @@ -650,7 +650,6 @@ void set_blocksize(kdev_t dev, int size) clear_bit(BH_Dirty, &bh->b_state); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Req, &bh->b_state); - bh->b_flushtime = 0; } if (atomic_read(&bh->b_count) == 0) { __remove_from_queues(bh); @@ -676,7 +675,6 @@ static void refill_freelist(int size) void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *dev_id) { bh->b_list = BUF_CLEAN; - bh->b_flushtime = 0; bh->b_end_io = handler; bh->b_dev_id = dev_id; } @@ -700,7 +698,6 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) unsigned long flags; struct buffer_head *tmp; struct page *page; - int free; mark_buffer_uptodate(bh, uptodate); @@ -720,7 +717,7 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) * deemed complete once all buffers have been visited * (b_count==0) and are now unlocked. We must make sure that * only the _last_ buffer that decrements its count is the one - * that free's the page.. + * that unlock the page.. */ spin_lock_irqsave(&page_uptodate_lock, flags); unlock_buffer(bh); @@ -745,26 +742,15 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) /* * Run the hooks that have to be done when a page I/O has completed. - * - * Note - we need to test the flags before we unlock the page, but - * we must not actually free the page until after the unlock! */ if (test_and_clear_bit(PG_decr_after, &page->flags)) atomic_dec(&nr_async_pages); - if (test_and_clear_bit(PG_free_swap_after, &page->flags)) - swap_free(page->offset); - - free = test_and_clear_bit(PG_free_after, &page->flags); - - if (page->owner != -1) + if (page->owner != (void *)-1) PAGE_BUG(page); - page->owner = (int)current; + page->owner = current; UnlockPage(page); - if (free) - __free_page(page); - return; still_busy: @@ -790,12 +776,8 @@ struct buffer_head * getblk(kdev_t dev, int block, int size) repeat: bh = get_hash_table(dev, block, size); - if (bh) { - if (!buffer_dirty(bh)) { - bh->b_flushtime = 0; - } + if (bh) goto out; - } isize = BUFSIZE_INDEX(size); spin_lock(&free_list[isize].lock); @@ -838,7 +820,7 @@ out: * pressures on different devices - thus the (currently unused) * 'dev' parameter. */ -int too_many_dirty_buffers; +static int too_many_dirty_buffers; void balance_dirty(kdev_t dev) { @@ -916,19 +898,21 @@ void __brelse(struct buffer_head * buf) */ void __bforget(struct buffer_head * buf) { + /* grab the lru lock here to block bdflush. */ spin_lock(&lru_list_lock); write_lock(&hash_table_lock); - if (atomic_read(&buf->b_count) != 1 || buffer_locked(buf)) { - touch_buffer(buf); - atomic_dec(&buf->b_count); - } else { - atomic_set(&buf->b_count, 0); - buf->b_state = 0; - if (buf->b_pprev) - __hash_unlink(buf); - __remove_from_lru_list(buf, buf->b_list); - put_last_free(buf); - } + if (!atomic_dec_and_test(&buf->b_count) || buffer_locked(buf)) + goto in_use; + if (buf->b_pprev) + __hash_unlink(buf); + write_unlock(&hash_table_lock); + __remove_from_lru_list(buf, buf->b_list); + spin_unlock(&lru_list_lock); + buf->b_state = 0; + put_last_free(buf); + return; + + in_use: write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); } @@ -1034,13 +1018,6 @@ static __inline__ void __put_unused_buffer_head(struct buffer_head * bh) } } -static void put_unused_buffer_head(struct buffer_head *bh) -{ - spin_lock(&unused_list_lock); - __put_unused_buffer_head(bh); - spin_unlock(&unused_list_lock); -} - /* * Reserve NR_RESERVED buffer heads for async IO requests to avoid * no-buffer-head deadlock. Return NULL on failure; waiting for @@ -1113,7 +1090,6 @@ static struct buffer_head * get_unused_buffer_head(int async) */ static struct buffer_head * create_buffers(unsigned long page, unsigned long size, int async) { - DECLARE_WAITQUEUE(wait, current); struct buffer_head *bh, *head; long offset; @@ -1137,7 +1113,6 @@ try_again: bh->b_data = (char *) (page+offset); bh->b_list = BUF_CLEAN; - bh->b_flushtime = 0; bh->b_end_io = end_buffer_io_bad; } return head; @@ -1146,11 +1121,13 @@ try_again: */ no_grow: if (head) { + spin_lock(&unused_list_lock); do { bh = head; head = head->b_this_page; - put_unused_buffer_head(bh); + __put_unused_buffer_head(bh); } while (head); + spin_unlock(&unused_list_lock); /* Wake up any waiters ... */ wake_up(&buffer_wait); @@ -1177,14 +1154,7 @@ no_grow: * Set our state for sleeping, then check again for buffer heads. * This ensures we won't miss a wake_up from an interrupt. */ - add_wait_queue(&buffer_wait, &wait); - current->state = TASK_UNINTERRUPTIBLE; - if (nr_unused_buffer_heads < MAX_BUF_PER_PAGE) { - current->policy |= SCHED_YIELD; - schedule(); - } - remove_wait_queue(&buffer_wait, &wait); - current->state = TASK_RUNNING; + wait_event(buffer_wait, nr_unused_buffer_heads >= MAX_BUF_PER_PAGE); goto try_again; } @@ -1195,12 +1165,12 @@ static int create_page_buffers(int rw, struct page *page, kdev_t dev, int b[], i if (!PageLocked(page)) BUG(); - if (page->owner != (int)current) + if (page->owner != current) PAGE_BUG(page); /* * Allocate async buffer heads pointing to this page, just for I/O. - * They show up in the buffer hash table and are registered in - * page->buffers. + * They don't show up in the buffer hash table, but they *are* + * registered in page->buffers. */ head = create_buffers(page_address(page), size, 1); if (page->buffers) @@ -1250,7 +1220,7 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset if (!PageLocked(page)) BUG(); if (!page->buffers) - return 0; + return 1; head = page->buffers; bh = head; @@ -1263,15 +1233,12 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset */ if (offset <= curr_off) { if (buffer_mapped(bh)) { - atomic_inc(&bh->b_count); - wait_on_buffer(bh); - if (bh->b_dev == B_FREE) - BUG(); mark_buffer_clean(bh); + wait_on_buffer(bh); clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Mapped, &bh->b_state); + clear_bit(BH_Req, &bh->b_state); bh->b_blocknr = 0; - atomic_dec(&bh->b_count); } } curr_off = next_off; @@ -1289,11 +1256,13 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset * instead. */ if (!offset) { - if (!try_to_free_buffers(page)) + if (!try_to_free_buffers(page)) { atomic_add(PAGE_CACHE_SIZE, &buffermem); + return 0; + } } - return 0; + return 1; } static void create_empty_buffers(struct page *page, struct inode *inode, unsigned long blocksize) @@ -1526,6 +1495,395 @@ out: } /* + * For moronic filesystems that do not allow holes in file. + * we allow offset==PAGE_SIZE, bytes==0 + */ + +int block_write_cont_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int err, partial; + unsigned long blocksize, start_block, end_block; + unsigned long start_offset, start_bytes, end_bytes; + unsigned long bbits, blocks, i, len; + struct buffer_head *bh, *head; + char * target_buf, *target_data; + unsigned long data_offset = offset; + + offset = page->offset-inode->i_size; + if (offset < 0) + offset = 0; + else if (offset >= data_offset) + offset = data_offset; + bytes += data_offset-offset; + + target_buf = (char *)page_address(page) + offset; + target_data = (char *)page_address(page) + data_offset; + + if (!PageLocked(page)) + BUG(); + + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; + + bbits = inode->i_sb->s_blocksize_bits; + block = page->offset >> bbits; + blocks = PAGE_SIZE >> bbits; + start_block = offset >> bbits; + end_block = (offset + bytes - 1) >> bbits; + start_offset = offset & (blocksize - 1); + start_bytes = blocksize - start_offset; + if (start_bytes > bytes) + start_bytes = bytes; + end_bytes = (offset+bytes) & (blocksize - 1); + if (end_bytes > bytes) + end_bytes = bytes; + + if (offset < 0 || offset > PAGE_SIZE) + BUG(); + if (bytes+offset < 0 || bytes+offset > PAGE_SIZE) + BUG(); + if (start_block < 0 || start_block > blocks) + BUG(); + if (end_block < 0 || end_block >= blocks) + BUG(); + // FIXME: currently we assume page alignment. + if (page->offset & (PAGE_SIZE-1)) + BUG(); + + i = 0; + bh = head; + partial = 0; + do { + if (!bh) + BUG(); + + if ((i < start_block) || (i > end_block)) { + if (!buffer_uptodate(bh)) + partial = 1; + goto skip; + } + + /* + * If the buffer is not up-to-date, we need to ask the low-level + * FS to do something for us (we used to have assumptions about + * the meaning of b_blocknr etc, that's bad). + * + * If "update" is set, that means that the low-level FS should + * try to make sure that the block is up-to-date because we're + * not going to fill it completely. + */ + bh->b_end_io = end_buffer_io_sync; + if (!buffer_mapped(bh)) { + err = inode->i_op->get_block(inode, block, bh, 1); + if (err) + goto out; + } + + if (!buffer_uptodate(bh) && (start_offset || (end_bytes && (i == end_block)))) { + if (buffer_new(bh)) { + memset(bh->b_data, 0, bh->b_size); + } else { + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + err = -EIO; + if (!buffer_uptodate(bh)) + goto out; + } + } + + len = blocksize; + if (start_offset) { + len = start_bytes; + start_offset = 0; + } else if (end_bytes && (i == end_block)) { + len = end_bytes; + end_bytes = 0; + } + err = 0; + if (target_buf+len<=target_data) + memset(target_buf, 0, len); + else if (target_buf<target_data) { + memset(target_buf, 0, target_data-target_buf); + copy_from_user(target_data, buf, + len+target_buf-target_data); + } else + err = copy_from_user(target_buf, buf, len); + target_buf += len; + buf += len; + + /* + * we dirty buffers only after copying the data into + * the page - this way we can dirty the buffer even if + * the bh is still doing IO. + * + * NOTE! This also does a direct dirty balace check, + * rather than relying on bdflush just waking up every + * once in a while. This is to catch (and slow down) + * the processes that write tons of buffer.. + * + * Note how we do NOT want to do this in the full block + * case: full pages are flushed not by the people who + * dirtied them, but by people who need memory. And we + * should not penalize them for somebody else writing + * lots of dirty pages. + */ + set_bit(BH_Uptodate, &bh->b_state); + if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { + __mark_dirty(bh, 0); + if (too_many_dirty_buffers) + balance_dirty(bh->b_dev); + } + + if (err) { + err = -EFAULT; + goto out; + } + +skip: + i++; + block++; + bh = bh->b_this_page; + } while (bh != head); + + /* + * is this a partial write that happened to make all buffers + * uptodate then we can optimize away a bogus readpage() for + * the next read(). Here we 'discover' wether the page went + * uptodate as a result of this (potentially partial) write. + */ + if (!partial) + SetPageUptodate(page); + return bytes; +out: + ClearPageUptodate(page); + return err; +} + + +/* + * IO completion routine for a buffer_head being used for kiobuf IO: we + * can't dispatch the kiobuf callback until io_count reaches 0. + */ + +static void end_buffer_io_kiobuf(struct buffer_head *bh, int uptodate) +{ + struct kiobuf *kiobuf; + + mark_buffer_uptodate(bh, uptodate); + + kiobuf = bh->b_kiobuf; + if (atomic_dec_and_test(&kiobuf->io_count)) + kiobuf->end_io(kiobuf); + if (!uptodate) + kiobuf->errno = -EIO; +} + + +/* + * For brw_kiovec: submit a set of buffer_head temporary IOs and wait + * for them to complete. Clean up the buffer_heads afterwards. + */ + +#define dprintk(x...) + +static int do_kio(struct kiobuf *kiobuf, + int rw, int nr, struct buffer_head *bh[], int size) +{ + int iosize; + int i; + struct buffer_head *tmp; + + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + dprintk ("do_kio start %d\n", rw); + + if (rw == WRITE) + rw = WRITERAW; + atomic_add(nr, &kiobuf->io_count); + kiobuf->errno = 0; + ll_rw_block(rw, nr, bh); + + kiobuf_wait_for_io(kiobuf); + + spin_lock(&unused_list_lock); + + iosize = 0; + for (i = nr; --i >= 0; ) { + iosize += size; + tmp = bh[i]; + if (!buffer_uptodate(tmp)) { + /* We are traversing bh'es in reverse order so + clearing iosize on error calculates the + amount of IO before the first error. */ + iosize = 0; + } + __put_unused_buffer_head(tmp); + } + + spin_unlock(&unused_list_lock); + + dprintk ("do_kio end %d %d\n", iosize, err); + + if (iosize) + return iosize; + if (kiobuf->errno) + return kiobuf->errno; + return -EIO; +} + +/* + * Start I/O on a physical range of kernel memory, defined by a vector + * of kiobuf structs (much like a user-space iovec list). + * + * The kiobuf must already be locked for IO. IO is submitted + * asynchronously: you need to check page->locked, page->uptodate, and + * maybe wait on page->wait. + * + * It is up to the caller to make sure that there are enough blocks + * passed in to completely map the iobufs to disk. + */ + +int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], + kdev_t dev, unsigned long b[], int size, int bmap) +{ + int err; + int length; + int transferred; + int i; + int bufind; + int pageind; + int bhind; + int offset; + unsigned long blocknr; + struct kiobuf * iobuf = NULL; + unsigned long page; + struct page * map; + struct buffer_head *tmp, *bh[KIO_MAX_SECTORS]; + + if (!nr) + return 0; + + /* + * First, do some alignment and validity checks + */ + for (i = 0; i < nr; i++) { + iobuf = iovec[i]; + if ((iobuf->offset & (size-1)) || + (iobuf->length & (size-1))) + return -EINVAL; + if (!iobuf->locked) + panic("brw_kiovec: iobuf not locked for I/O"); + if (!iobuf->nr_pages) + panic("brw_kiovec: iobuf not initialised"); + } + + /* DEBUG */ +#if 0 + return iobuf->length; +#endif + dprintk ("brw_kiovec: start\n"); + + /* + * OK to walk down the iovec doing page IO on each page we find. + */ + bufind = bhind = transferred = err = 0; + for (i = 0; i < nr; i++) { + iobuf = iovec[i]; + offset = iobuf->offset; + length = iobuf->length; + dprintk ("iobuf %d %d %d\n", offset, length, size); + + for (pageind = 0; pageind < iobuf->nr_pages; pageind++) { + page = iobuf->pagelist[pageind]; + map = iobuf->maplist[pageind]; + if (map && PageBIGMEM(map)) { + err = -EIO; + goto error; + } + + while (length > 0) { + blocknr = b[bufind++]; + tmp = get_unused_buffer_head(0); + if (!tmp) { + err = -ENOMEM; + goto error; + } + + tmp->b_dev = B_FREE; + tmp->b_size = size; + tmp->b_data = (char *) (page + offset); + tmp->b_this_page = tmp; + + init_buffer(tmp, end_buffer_io_kiobuf, NULL); + tmp->b_dev = dev; + tmp->b_blocknr = blocknr; + tmp->b_state = 1 << BH_Mapped; + tmp->b_kiobuf = iobuf; + + if (rw == WRITE) { + set_bit(BH_Uptodate, &tmp->b_state); + set_bit(BH_Dirty, &tmp->b_state); + } + + dprintk ("buffer %d (%d) at %p\n", + bhind, tmp->b_blocknr, tmp->b_data); + bh[bhind++] = tmp; + length -= size; + offset += size; + + /* + * Start the IO if we have got too much + */ + if (bhind >= KIO_MAX_SECTORS) { + err = do_kio(iobuf, rw, bhind, bh, size); + if (err >= 0) + transferred += err; + else + goto finished; + bhind = 0; + } + + if (offset >= PAGE_SIZE) { + offset = 0; + break; + } + } /* End of block loop */ + } /* End of page loop */ + } /* End of iovec loop */ + + /* Is there any IO still left to submit? */ + if (bhind) { + err = do_kio(iobuf, rw, bhind, bh, size); + if (err >= 0) + transferred += err; + else + goto finished; + } + + finished: + dprintk ("brw_kiovec: end (%d, %d)\n", transferred, err); + if (transferred) + return transferred; + return err; + + error: + /* We got an error allocation the bh'es. Just free the current + buffer_heads and exit. */ + spin_lock(&unused_list_lock); + for (i = bhind; --i >= 0; ) { + __put_unused_buffer_head(bh[bhind]); + } + spin_unlock(&unused_list_lock); + goto finished; +} + +/* * Start I/O on a page. * This function expects the page to be locked and may return * before I/O is complete. You then have to check page->locked, @@ -1556,7 +1914,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) } if (!page->buffers) BUG(); - page->owner = -1; + page->owner = (void *)-1; head = page->buffers; bh = head; @@ -1605,7 +1963,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) } else { if (!nr && rw == READ) { SetPageUptodate(page); - page->owner = (int)current; + page->owner = current; UnlockPage(page); } if (nr && (rw == WRITE)) @@ -1639,8 +1997,7 @@ int block_read_full_page(struct file * file, struct page * page) blocks = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; iblock = page->offset >> inode->i_sb->s_blocksize_bits; - page->owner = -1; - head = page->buffers; + page->owner = (void *)-1; bh = head; nr = 0; @@ -1674,7 +2031,7 @@ int block_read_full_page(struct file * file, struct page * page) * uptodate as well. */ SetPageUptodate(page); - page->owner = (int)current; + page->owner = current; UnlockPage(page); } return 0; @@ -1687,6 +2044,7 @@ int block_read_full_page(struct file * file, struct page * page) static int grow_buffers(int size) { unsigned long page; + struct page * page_map; struct buffer_head *bh, *tmp; struct buffer_head * insert_point; int isize; @@ -1699,10 +2057,8 @@ static int grow_buffers(int size) if (!(page = __get_free_page(GFP_BUFFER))) return 0; bh = create_buffers(page, size, 0); - if (!bh) { - free_page(page); - return 0; - } + if (!bh) + goto no_buffer_head; isize = BUFSIZE_INDEX(size); @@ -1729,9 +2085,15 @@ static int grow_buffers(int size) free_list[isize].list = bh; spin_unlock(&free_list[isize].lock); - mem_map[MAP_NR(page)].buffers = bh; + page_map = mem_map + MAP_NR(page); + page_map->buffers = bh; + lru_cache_add(page_map); atomic_add(PAGE_SIZE, &buffermem); return 1; + +no_buffer_head: + free_page(page); + return 0; } /* @@ -1940,7 +2302,6 @@ static int sync_old_buffers(void) continue; /* OK, now we are committed to write it out. */ - bh->b_flushtime = 0; atomic_inc(&bh->b_count); spin_unlock(&lru_list_lock); ll_rw_block(WRITE, 1, &bh); @@ -1954,48 +2315,52 @@ static int sync_old_buffers(void) return 0; } - /* This is the interface to bdflush. As we get more sophisticated, we can * pass tuning parameters to this "process", to adjust how it behaves. * We would want to verify each parameter, however, to make sure that it * is reasonable. */ -asmlinkage int sys_bdflush(int func, long data) +asmlinkage long sys_bdflush(int func, long data) { - int i, error = -EPERM; - if (!capable(CAP_SYS_ADMIN)) - goto out; + return -EPERM; if (func == 1) { - error = sync_old_buffers(); - goto out; + int error; + struct mm_struct *user_mm; + + /* + * bdflush will spend all of it's time in kernel-space, + * without touching user-space, so we can switch it into + * 'lazy TLB mode' to reduce the cost of context-switches + * to and from bdflush. + */ + user_mm = start_lazy_tlb(); + error = sync_old_buffers(); + end_lazy_tlb(user_mm); + return error; } /* Basically func 1 means read param 1, 2 means write param 1, etc */ if (func >= 2) { - i = (func-2) >> 1; - error = -EINVAL; - if (i < 0 || i >= N_PARAM) - goto out; - if((func & 1) == 0) { - error = put_user(bdf_prm.data[i], (int*)data); - goto out; + int i = (func-2) >> 1; + if (i >= 0 && i < N_PARAM) { + if ((func & 1) == 0) + return put_user(bdf_prm.data[i], (int*)data); + + if (data >= bdflush_min[i] && data <= bdflush_max[i]) { + bdf_prm.data[i] = data; + return 0; + } } - if (data < bdflush_min[i] || data > bdflush_max[i]) - goto out; - bdf_prm.data[i] = data; - error = 0; - goto out; - }; + return -EINVAL; + } /* Having func 0 used to launch the actual bdflush and then never * return (unless explicitly killed). We return zero here to * remain semi-compatible with present update(8) programs. */ - error = 0; -out: - return error; + return 0; } /* @@ -2060,7 +2425,6 @@ int bdflush(void * unused) major = MAJOR(bh->b_dev); written++; - bh->b_flushtime = 0; /* * For the loop major we can try to do asynchronous writes, @@ -2068,12 +2432,7 @@ int bdflush(void * unused) */ atomic_inc(&bh->b_count); spin_unlock(&lru_list_lock); - if (major == LOOP_MAJOR && written > 1) { - ll_rw_block(WRITEA, 1, &bh); - if (buffer_dirty(bh)) - --written; - } else - ll_rw_block(WRITE, 1, &bh); + ll_rw_block(WRITE, 1, &bh); atomic_dec(&bh->b_count); goto repeat; } @@ -2097,3 +2456,12 @@ int bdflush(void * unused) } } } + +static int __init bdflush_init(void) +{ + kernel_thread(bdflush, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + return 0; +} + +module_init(bdflush_init) + diff --git a/fs/coda/cache.c b/fs/coda/cache.c index d3412e52c..fa8a66e0c 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -65,7 +65,7 @@ static void coda_ccremove(struct coda_cache *el) if ( ! list_empty(&el->cc_cclist) ) list_del(&el->cc_cclist); else - printk("coda_cnremove: loose cc entry!"); + printk("coda_ccremove: loose cc entry!"); } /* remove a cache entry from the inode's list */ @@ -139,7 +139,7 @@ void coda_cache_enter(struct inode *inode, int mask) /* remove all cached acl matches from an inode */ void coda_cache_clear_inode(struct inode *inode) { - struct list_head *lh, *le; + struct list_head *le; struct coda_inode_info *cii; struct coda_cache *cc; ENTRY; @@ -150,9 +150,10 @@ void coda_cache_clear_inode(struct inode *inode) } cii = ITOC(inode); - lh = le = &cii->c_cnhead; - while ( (le = le->next ) != lh ) { + le = cii->c_cnhead.next; + while ( le != &cii->c_cnhead ) { cc = list_entry(le, struct coda_cache, cc_cnlist); + le = le->next; coda_cnremove(cc); coda_ccremove(cc); CODA_FREE(cc, sizeof(*cc)); @@ -162,7 +163,7 @@ void coda_cache_clear_inode(struct inode *inode) /* remove all acl caches */ void coda_cache_clear_all(struct super_block *sb) { - struct list_head *lh, *le; + struct list_head *le; struct coda_cache *cc; struct coda_sb_info *sbi = coda_sbp(sb); @@ -174,9 +175,10 @@ void coda_cache_clear_all(struct super_block *sb) if ( list_empty(&sbi->sbi_cchead) ) return; - lh = le = &sbi->sbi_cchead; - while ( (le = le->next ) != lh ) { + le = sbi->sbi_cchead.next; + while ( le != &sbi->sbi_cchead ) { cc = list_entry(le, struct coda_cache, cc_cclist); + le = le->next; coda_cnremove(cc); coda_ccremove(cc); CODA_FREE(cc, sizeof(*cc)); @@ -186,7 +188,7 @@ void coda_cache_clear_all(struct super_block *sb) /* remove all acl caches for a principal */ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) { - struct list_head *lh, *le; + struct list_head *le; struct coda_cache *cc; struct coda_sb_info *sbi = coda_sbp(sb); @@ -198,9 +200,10 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) if (list_empty(&sbi->sbi_cchead)) return; - lh = le = &sbi->sbi_cchead; - while ( (le = le->next ) != lh ) { + le = sbi->sbi_cchead.next; + while ( le != &sbi->sbi_cchead ) { cc = list_entry(le, struct coda_cache, cc_cclist); + le = le->next; if ( coda_cred_eq(&cc->cc_cred, cred)) { coda_cnremove(cc); coda_ccremove(cc); diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index ffae7746f..faa983e0f 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -108,10 +108,10 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) if ( coda_f2i(fid) != ino ) { if ( !coda_fid_is_weird(fid) ) printk("Coda: unknown weird fid: ino %ld, fid %s." - "Tell Peter.\n", ino, coda_f2s(&cnp->c_fid)); + "Tell Peter.\n", (long)ino, coda_f2s(&cnp->c_fid)); list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); CDEBUG(D_UPCALL, "Added %ld ,%s to volroothead\n", - ino, coda_f2s(&cnp->c_fid)); + (long)ino, coda_f2s(&cnp->c_fid)); } coda_fill_inode(*inode, &attr); @@ -199,7 +199,7 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) inode = iget(sb, nr); if ( !inode ) { printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n", - sb, nr); + sb, (long)nr); return NULL; } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 10b1cb171..883e34a91 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -125,12 +125,12 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) if ( length > CODA_MAXNAMLEN ) { printk("name too long: lookup, %s (%*s)\n", - coda_f2s(&dircnp->c_fid), length, name); + coda_f2s(&dircnp->c_fid), (int)length, name); return ERR_PTR(-ENAMETOOLONG); } - CDEBUG(D_INODE, "name %s, len %d in ino %ld, fid %s\n", - name, length, dir->i_ino, coda_f2s(&dircnp->c_fid)); + CDEBUG(D_INODE, "name %s, len %ld in ino %ld, fid %s\n", + name, (long)length, dir->i_ino, coda_f2s(&dircnp->c_fid)); /* control object, create inode on the fly */ if (coda_isroot(dir) && coda_iscontrol(name, length)) { @@ -157,7 +157,7 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) return ERR_PTR(error); } else if (error != -ENOENT) { CDEBUG(D_INODE, "error for %s(%*s)%d\n", - coda_f2s(&dircnp->c_fid), length, name, error); + coda_f2s(&dircnp->c_fid), (int)length, name, error); return ERR_PTR(error); } CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", @@ -504,10 +504,10 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, old_cnp = ITOC(old_dir); new_cnp = ITOC(new_dir); - CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s" - "(%d length, %d strlen).old:d_count: %d, new:d_count: %d\n", - old_name, old_length, strlen(old_name), new_name, new_length, - strlen(new_name),old_dentry->d_count, new_dentry->d_count); + CDEBUG(D_INODE, "old: %s, (%d length, %ld strlen), new: %s" + "(%d length, %ld strlen).old:d_count: %d, new:d_count: %d\n", + old_name, old_length, (long)strlen(old_name), new_name, new_length, + (long)strlen(new_name),old_dentry->d_count, new_dentry->d_count); /* the C library will do unlink/create etc */ if ( coda_crossvol_rename == 0 && @@ -594,12 +594,12 @@ int coda_open(struct inode *i, struct file *f) error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); if (error) { CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n", - dev, ino, error); + dev, (long)ino, error); return error; } /* coda_upcall returns ino number of cached object, get inode */ - CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino); + CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, (long)ino); error = coda_inode_grab(dev, ino, &cont_inode); if ( error || !cont_inode ){ @@ -622,9 +622,9 @@ int coda_open(struct inode *i, struct file *f) CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", error, i->i_count, i->i_ino); - CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %x\n", + CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", cnp->c_ovp->i_ino, cnp->c_ovp->i_count, - (int)(cnp->c_ovp->i_op)); + (cnp->c_ovp->i_op)); EXIT; return 0; } diff --git a/fs/coda/file.c b/fs/coda/file.c index 7e83ea954..378c4f7a6 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -155,8 +155,8 @@ static ssize_t coda_file_read(struct file *coda_file, char *buff, result = cont_file.f_op->read(&cont_file , buff, count, &(cont_file.f_pos)); - CDEBUG(D_FILE, "ops at %x result %d, count %d, position: %d\n", - (int)cont_file.f_op, result, count, (int)cont_file.f_pos); + CDEBUG(D_FILE, "ops at %p result %d, count %ld, position: %d\n", + cont_file.f_op, result, (long)count, (int)cont_file.f_pos); coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); return result; @@ -292,10 +292,10 @@ int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind) if ( *ind == NULL ) { printk("coda_inode_grab: iget(dev: %d, ino: %ld) - returns NULL.\n", dev, ino); + returns NULL.\n", dev, (long)ino); return -ENOENT; } - CDEBUG(D_FILE, "ino: %ld, ops at %x\n", ino, (int)(*ind)->i_op); + CDEBUG(D_FILE, "ino: %ld, ops at %p\n", (long)ino, (*ind)->i_op); return 0; } diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 604e906a2..e2f2931b1 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -203,7 +203,7 @@ static void coda_delete_inode(struct inode *inode) coda_cache_clear_inode(inode); CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); - inode->u.generic_ip = NULL; + inode->u.coda_i.c_magic = 0; clear_inode(inode); EXIT; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index abef74563..330b634a3 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -98,8 +98,8 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) return -EFAULT; - CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), count %d\n", - current->pid, hdr.opcode, hdr.unique, count); + CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), count %ld\n", + current->pid, hdr.opcode, hdr.unique, (long)count); if (DOWNCALL(hdr.opcode)) { struct super_block *sb = NULL; @@ -160,8 +160,8 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, /* move data into response buffer. */ if (req->uc_outSize < count) { - printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n", - req->uc_outSize, count, hdr.opcode, hdr.unique); + printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n", + req->uc_outSize, (long)count, hdr.opcode, hdr.unique); count = req->uc_outSize; /* don't have more space! */ } if (copy_from_user(req->uc_data, buf, count)) @@ -172,8 +172,8 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, req->uc_flags |= REQ_WRITE; CDEBUG(D_PSDEV, - "Found! Count %d for (opc,uniq)=(%ld,%ld), upc_req at %x\n", - count, hdr.opcode, hdr.unique, (int)&req); + "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n", + (long)count, hdr.opcode, hdr.unique, &req); wake_up(&req->uc_sleep); return(count); @@ -190,7 +190,7 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, struct upc_req *req; int result = count ; - CDEBUG(D_PSDEV, "count %d\n", count); + CDEBUG(D_PSDEV, "count %ld\n", (long)count); if (list_empty(&(vcp->vc_pending))) { return -1; } @@ -203,8 +203,8 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, result = req->uc_inSize; if (count < req->uc_inSize) { - printk ("psdev_read: Venus read %d bytes of %d in message\n", - count, req->uc_inSize); + printk ("psdev_read: Venus read %ld bytes of %d in message\n", + (long)count, req->uc_inSize); } if ( copy_to_user(buf, req->uc_data, result)) @@ -334,7 +334,7 @@ static struct file_operations coda_psdev_fops = { -__initfunc(int init_coda(void)) +int __init init_coda(void) { int status; printk(KERN_INFO "Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n"); diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 62fd62e35..9aecd8a6e 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -625,9 +625,9 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) add_wait_queue(&vmp->uc_sleep, &wait); for (;;) { if ( coda_hard == 0 ) - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); else - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); /* got a reply */ if ( vmp->uc_flags & REQ_WRITE ) @@ -710,8 +710,8 @@ ENTRY; /* Append msg to pending queue and poke Venus. */ list_add(&(req->uc_chain), vcommp->vc_pending.prev); CDEBUG(D_UPCALL, - "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n", - current->pid, req->uc_opcode, req->uc_unique, (int)req); + "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %p.zzz.\n", + current->pid, req->uc_opcode, req->uc_unique, req); wake_up_interruptible(&vcommp->vc_waitq); /* We can be interrupted while we wait for Venus to process @@ -731,8 +731,8 @@ ENTRY; req->uc_opcode, jiffies - req->uc_posttime, req->uc_unique, req->uc_outSize); CDEBUG(D_UPCALL, - "..process %d woken up by Venus for req at 0x%x, data at %x\n", - current->pid, (int)req, (int)req->uc_data); + "..process %d woken up by Venus for req at %p, data at %p\n", + current->pid, req, req->uc_data); if (vcommp->vc_pid) { /* i.e. Venus is still alive */ /* Op went through, interrupt or not... */ if (req->uc_flags & REQ_WRITE) { diff --git a/fs/dcache.c b/fs/dcache.c index 0d7cf9c9e..cbdfcfaf5 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -20,6 +20,7 @@ #include <linux/malloc.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -473,9 +474,11 @@ void shrink_dcache_memory(int priority, unsigned int gfp_mask) { if (gfp_mask & __GFP_IO) { int count = 0; + lock_kernel(); if (priority) count = dentry_stat.nr_unused / priority; prune_dcache(count); + unlock_kernel(); } } @@ -813,7 +816,7 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen) * return NULL; * } */ -asmlinkage int sys_getcwd(char *buf, unsigned long size) +asmlinkage long sys_getcwd(char *buf, unsigned long size) { int error; struct dentry *pwd = current->fs->pwd; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index bad4281ff..b11cae5ba 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -348,7 +348,7 @@ void devpts_pty_kill(int number) } } -__initfunc(int init_devpts_fs(void)) +int __init init_devpts_fs(void) { return register_filesystem(&devpts_fs_type); diff --git a/fs/devpts/root.c b/fs/devpts/root.c index 9b5408235..5b5948e80 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -152,10 +152,9 @@ static struct dentry *devpts_root_lookup(struct inode * dir, struct dentry * den unsigned int nentry = *p++ - '0'; if ( nentry > 9 ) return NULL; - nentry += entry * 10; - if (nentry < entry) + if ( entry >= ~0U/10 ) return NULL; - entry = nentry; + entry = nentry + entry * 10; } } diff --git a/fs/dquot.c b/fs/dquot.c index 9dfbac082..d39a95b2c 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -199,7 +199,7 @@ static void __wait_on_dquot(struct dquot *dquot) add_wait_queue(&dquot->dq_wait, &wait); repeat: - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); if (dquot->dq_flags & DQ_LOCKED) { schedule(); goto repeat; @@ -1337,7 +1337,7 @@ cleanup: * calls. Maybe we need to add the process quotas etc. in the future, * but we probably should use rlimits for that. */ -asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) +asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr) { int cmds = 0, type = 0, flags = 0; kdev_t dev; diff --git a/fs/efs/super.c b/fs/efs/super.c index 929c9c4f1..df0f167cb 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -32,7 +32,7 @@ static struct super_operations efs_superblock_operations = { NULL /* remount */ }; -__initfunc(int init_efs_fs(void)) { +int __init init_efs_fs(void) { return register_filesystem(&efs_fs_type); } @@ -50,36 +50,6 @@ static struct linux_binfmt *formats = (struct linux_binfmt *) NULL; -void __init binfmt_setup(void) -{ -#ifdef CONFIG_BINFMT_MISC - init_misc_binfmt(); -#endif - -#ifdef CONFIG_BINFMT_ELF - init_elf_binfmt(); -#endif - -#ifdef CONFIG_BINFMT_ELF32 - init_elf32_binfmt(); -#endif - -#ifdef CONFIG_BINFMT_AOUT - init_aout_binfmt(); -#endif - -#ifdef CONFIG_BINFMT_AOUT32 - init_aout32_binfmt(); -#endif - -#ifdef CONFIG_BINFMT_EM86 - init_em86_binfmt(); -#endif - - /* This cannot be configured out of the kernel */ - init_script_binfmt(); -} - int register_binfmt(struct linux_binfmt * fmt) { struct linux_binfmt ** tmp = &formats; @@ -98,7 +68,6 @@ int register_binfmt(struct linux_binfmt * fmt) return 0; } -#ifdef CONFIG_MODULES int unregister_binfmt(struct linux_binfmt * fmt) { struct linux_binfmt ** tmp = &formats; @@ -112,7 +81,6 @@ int unregister_binfmt(struct linux_binfmt * fmt) } return -EINVAL; } -#endif /* CONFIG_MODULES */ /* N.B. Error returns must be < 0 */ int open_dentry(struct dentry * dentry, int mode) @@ -167,7 +135,7 @@ out: * * Also note that we take the address to load from from the file itself. */ -asmlinkage int sys_uselib(const char * library) +asmlinkage long sys_uselib(const char * library) { int fd, retval; struct file * file; @@ -306,7 +274,7 @@ int setup_arg_pages(struct linux_binprm *bprm) mpnt->vm_ops = NULL; mpnt->vm_offset = 0; mpnt->vm_file = NULL; - mpnt->vm_pte = 0; + mpnt->vm_private_data = NULL; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; } @@ -366,54 +334,33 @@ end_readexec: static int exec_mmap(void) { struct mm_struct * mm, * old_mm; - int retval, nr; - if (atomic_read(¤t->mm->count) == 1) { - flush_cache_mm(current->mm); + old_mm = current->mm; + if (old_mm && atomic_read(&old_mm->mm_users) == 1) { + flush_cache_mm(old_mm); mm_release(); - release_segments(current->mm); - exit_mmap(current->mm); - flush_tlb_mm(current->mm); + exit_mmap(old_mm); + flush_tlb_mm(old_mm); return 0; } - retval = -ENOMEM; mm = mm_alloc(); - if (!mm) - goto fail_nomem; - - mm->cpu_vm_mask = (1UL << smp_processor_id()); - mm->total_vm = 0; - mm->rss = 0; - /* - * Make sure we have a private ldt if needed ... - */ - nr = current->tarray_ptr - &task[0]; - copy_segments(nr, current, mm); - - old_mm = current->mm; - current->mm = mm; - retval = new_page_tables(current); - if (retval) - goto fail_restore; - activate_context(current); - up(&mm->mmap_sem); - mm_release(); - mmput(old_mm); - return 0; + if (mm) { + struct mm_struct *active_mm = current->active_mm; - /* - * Failure ... restore the prior mm_struct. - */ -fail_restore: - current->mm = old_mm; - /* restore the ldt for this task */ - copy_segments(nr, current, NULL); - release_segments(mm); - kmem_cache_free(mm_cachep, mm); - -fail_nomem: - return retval; + current->mm = mm; + current->active_mm = mm; + activate_mm(active_mm, mm); + mm_release(); + if (old_mm) { + if (active_mm != old_mm) BUG(); + mmput(old_mm); + return 0; + } + mmdrop(active_mm); + return 0; + } + return -ENOMEM; } /* @@ -468,9 +415,9 @@ static inline void flush_old_files(struct files_struct * files) unsigned long set, i; i = j * __NFDBITS; - if (i >= files->max_fds) + if (i >= files->max_fds || i >= files->max_fdset) break; - set = xchg(&files->close_on_exec.fds_bits[j], 0); + set = xchg(&files->close_on_exec->fds_bits[j], 0); j++; for ( ; set ; i++,set >>= 1) { if (set & 1) @@ -632,7 +579,7 @@ int prepare_binprm(struct linux_binprm *bprm) if (id_change || cap_raised) { /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ - /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */ + /* (current->mm->mm_users > 1 is ok, as we'll get a new mm anyway) */ if (IS_NOSUID(inode) || must_not_trace_exec(current) || (atomic_read(¤t->fs->count) > 1) @@ -850,3 +797,54 @@ out: return retval; } + +int do_coredump(long signr, struct pt_regs * regs) +{ + struct linux_binfmt * binfmt; + char corename[6+sizeof(current->comm)]; + struct file * file; + struct dentry * dentry; + struct inode * inode; + + lock_kernel(); + binfmt = current->binfmt; + if (!binfmt || !binfmt->core_dump) + goto fail; + if (!current->dumpable || atomic_read(¤t->mm->mm_users) != 1) + goto fail; + current->dumpable = 0; + if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) + goto fail; + + memcpy(corename,"core.", 5); +#if 0 + memcpy(corename+5,current->comm,sizeof(current->comm)); +#else + corename[4] = '\0'; +#endif + file = filp_open(corename, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); + if (IS_ERR(file)) + goto fail; + dentry = file->f_dentry; + inode = dentry->d_inode; + if (inode->i_nlink > 1) + goto close_fail; /* multiple links - don't dump */ + + if (!S_ISREG(inode->i_mode)) + goto close_fail; + if (!inode->i_op || !inode->i_op->default_file_ops) + goto close_fail; + if (!file->f_op->write) + goto close_fail; + if (!binfmt->core_dump(signr, regs, file)) + goto close_fail; + filp_close(file, NULL); + unlock_kernel(); + return 1; + +close_fail: + filp_close(file, NULL); +fail: + unlock_kernel(); + return 0; +} diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 053022309..b7cfd2212 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -368,11 +368,10 @@ int ext2_new_block (const struct inode * inode, unsigned long goal, struct super_block * sb; struct ext2_group_desc * gdp; struct ext2_super_block * es; - - *err = -ENOSPC; #ifdef EXT2FS_DEBUG static int goal_hits = 0, goal_attempts = 0; #endif + *err = -ENOSPC; sb = inode->i_sb; if (!sb) { printk ("ext2_new_block: nonexistent device"); diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index f6a5ecfc6..d4b5ca33f 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -200,7 +200,7 @@ void ext2_free_inode (struct inode * inode) } if (inode->i_nlink) { printk ("ext2_free_inode: inode has nlink=%d\n", - (int) inode->i_nlink); + inode->i_nlink); return; } if (!sb) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 171f34a16..52e97c585 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -259,20 +259,22 @@ repeat: } if (metadata) { result = getblk (inode->i_dev, tmp, blocksize); + memset(result->b_data, 0, blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); if (*p) { ext2_free_blocks (inode, tmp, 1); - brelse (result); + bforget (result); goto repeat; } - memset(result->b_data, 0, blocksize); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); } else { if (*p) { /* * Nobody is allowed to change block allocation * state from under us: */ + ext2_error (inode->i_sb, "block_getblk", + "data block filled under us"); BUG(); ext2_free_blocks (inode, tmp, 1); goto repeat; @@ -366,23 +368,29 @@ repeat: goto out; if (metadata) { result = getblk (bh->b_dev, tmp, blocksize); + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); if (*p) { ext2_free_blocks (inode, tmp, 1); - brelse (result); + bforget (result); goto repeat; } - memset(result->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); } else { + if (*p) { + /* + * Nobody is allowed to change block allocation + * state from under us: + */ + ext2_error (inode->i_sb, "block_getblk", + "data block filled under us"); + BUG(); + ext2_free_blocks (inode, tmp, 1); + goto repeat; + } *phys = tmp; *new = 1; } - if (*p) { - ext2_free_blocks (inode, tmp, 1); - brelse (result); - goto repeat; - } *p = le32_to_cpu(tmp); mark_buffer_dirty(bh, 1); if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { @@ -848,10 +856,11 @@ int ext2_notify_change(struct dentry *dentry, struct iattr *iattr) unsigned int flags; retval = -EPERM; - if ((iattr->ia_attr_flags & - (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ - (inode->u.ext2_i.i_flags & - (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { + if (iattr->ia_valid & ATTR_ATTR_FLAG && + ((!(iattr->ia_attr_flags & ATTR_FLAG_APPEND) != + !(inode->u.ext2_i.i_flags & EXT2_APPEND_FL)) || + (!(iattr->ia_attr_flags & ATTR_FLAG_IMMUTABLE) != + !(inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)))) { if (!capable(CAP_LINUX_IMMUTABLE)) goto out; } else if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 9666118ee..90cb80050 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -618,7 +618,7 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) if (inode->i_nlink != 2) ext2_warning (inode->i_sb, "ext2_rmdir", "empty directory has nlink!=2 (%d)", - (int) inode->i_nlink); + inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; inode->i_size = 0; @@ -656,7 +656,7 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) if (!inode->i_nlink) { ext2_warning (inode->i_sb, "ext2_unlink", "Deleting nonexistent file (%lu), %d", - inode->i_ino, (int) inode->i_nlink); + inode->i_ino, inode->i_nlink); inode->i_nlink = 1; } retval = ext2_delete_entry (de, bh); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 758da8ff0..37b8e15c5 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -739,7 +739,7 @@ static struct file_system_type ext2_fs_type = { NULL }; -__initfunc(int init_ext2_fs(void)) +int __init init_ext2_fs(void) { return register_filesystem(&ext2_fs_type); } diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c index 4460e94d8..7ab13267d 100644 --- a/fs/fat/buffer.c +++ b/fs/fat/buffer.c @@ -17,195 +17,189 @@ # define PRINTK(x) #endif -struct buffer_head *fat_bread ( +struct buffer_head *fat_bread(struct super_block *sb, int block) +{ + return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block); +} +struct buffer_head *fat_getblk(struct super_block *sb, int block) +{ + return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block); +} +void fat_brelse (struct super_block *sb, struct buffer_head *bh) +{ + if (bh) + MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh); +} +void fat_mark_buffer_dirty ( struct super_block *sb, - int block) + struct buffer_head *bh, + int dirty) { - struct buffer_head *ret = NULL; - - PRINTK(("fat_bread: block=0x%x\n", block)); - /* - * Note that the blocksize is 512, 1024 or 2048, but the first read - * is always of size 1024 (or 2048). Doing readahead may be - * counterproductive or just plain wrong. - */ + MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty); +} +void fat_set_uptodate ( + struct super_block *sb, + struct buffer_head *bh, + int val) +{ + MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val); +} +int fat_is_uptodate(struct super_block *sb, struct buffer_head *bh) +{ + return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh); +} +void fat_ll_rw_block ( + struct super_block *sb, + int opr, + int nbreq, + struct buffer_head *bh[32]) +{ + MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh); +} - if(MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_bread) - return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block); +struct buffer_head *default_fat_bread(struct super_block *sb, int block) +{ + return bread (sb->s_dev,block,512); +} +struct buffer_head *default_fat_getblk(struct super_block *sb, int block) +{ + return getblk (sb->s_dev,block,512); +} +void default_fat_brelse(struct super_block *sb, struct buffer_head *bh) +{ + brelse (bh); +} +void default_fat_mark_buffer_dirty ( + struct super_block *sb, + struct buffer_head *bh, + int dirty) +{ + mark_buffer_dirty (bh,dirty); +} +void default_fat_set_uptodate ( + struct super_block *sb, + struct buffer_head *bh, + int val) +{ + mark_buffer_uptodate(bh, val); +} +int default_fat_is_uptodate (struct super_block *sb, struct buffer_head *bh) +{ + return buffer_uptodate(bh); +} +void default_fat_ll_rw_block ( + struct super_block *sb, + int opr, + int nbreq, + struct buffer_head *bh[32]) +{ + ll_rw_block(opr,nbreq,bh); +} - if (sb->s_blocksize == 512) { - ret = bread (sb->s_dev,block,512); +struct buffer_head *bigblock_fat_bread ( + struct super_block *sb, + int block) +{ + struct buffer_head *ret = NULL; + struct buffer_head *real; + if (sb->s_blocksize == 1024){ + real = bread (sb->s_dev,block>>1,1024); } else { - struct buffer_head *real; - if (sb->s_blocksize == 1024){ - real = bread (sb->s_dev,block>>1,1024); - } else { - real = bread (sb->s_dev,block>>2,2048); - } - - 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 MS-DOS filesystem that sits - * 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). + real = bread (sb->s_dev,block>>2,2048); + } - * 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 (sb->s_blocksize == 2048) { - if (block & 3) ret->b_data += (block & 3) << 9; - }else{ - if (block & 1) ret->b_data += 512; - } - ret->b_next = real; + 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 MS-DOS filesystem that sits + * 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 (sb->s_blocksize == 2048) { + if (block & 3) ret->b_data += (block & 3) << 9; }else{ - brelse (real); + if (block & 1) ret->b_data += 512; } + ret->b_next = real; + }else{ + brelse (real); } } return ret; } -struct buffer_head *fat_getblk(struct super_block *sb, int block) -{ - struct buffer_head *ret = NULL; - PRINTK(("fat_getblk: block=0x%x\n", block)); - - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_getblk) - return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block); - - if (sb->s_blocksize == 512){ - ret = getblk (sb->s_dev,block,512); - } else { - /* - * #Specification: msdos / special device / writing - * A write is always preceded by a read of the complete block - * (large hardware sector size). This defeat write performance. - * There is a possibility to optimize this when writing large - * chunk by making sure we are filling large block. Volunteer ? - */ - ret = fat_bread (sb,block); - } - return ret; -} - -void fat_brelse ( +void bigblock_fat_brelse ( struct super_block *sb, struct buffer_head *bh) { - if (bh != NULL) { - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_brelse) - return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh); - - if (sb->s_blocksize == 512){ - brelse (bh); - }else{ - brelse (bh->b_next); - /* We can free the dummy because a new one is allocated at - each fat_getblk() and fat_bread(). - */ - kfree (bh); - } - } + brelse (bh->b_next); + /* + * We can free the dummy because a new one is allocated at + * each fat_getblk() and fat_bread(). + */ + kfree (bh); } - -void fat_mark_buffer_dirty ( + +void bigblock_fat_mark_buffer_dirty ( struct super_block *sb, struct buffer_head *bh, int dirty) { - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty) { - MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty); - return; - } - - if (sb->s_blocksize != 512){ - bh = bh->b_next; - } - mark_buffer_dirty (bh,dirty); + mark_buffer_dirty (bh->b_next,dirty); } -void fat_set_uptodate ( +void bigblock_fat_set_uptodate ( struct super_block *sb, struct buffer_head *bh, int val) { - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_set_uptodate) { - MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val); - return; - } - - if (sb->s_blocksize != 512){ - bh = bh->b_next; - } - mark_buffer_uptodate(bh, val); + mark_buffer_uptodate(bh->b_next, val); } -int fat_is_uptodate ( + +int bigblock_fat_is_uptodate ( struct super_block *sb, struct buffer_head *bh) { - if(MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_is_uptodate) - return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh); - - if (sb->s_blocksize != 512){ - bh = bh->b_next; - } - return buffer_uptodate(bh); + return buffer_uptodate(bh->b_next); } -void fat_ll_rw_block ( +void bigblock_fat_ll_rw_block ( struct super_block *sb, int opr, int nbreq, struct buffer_head *bh[32]) { - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block) { - MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh); - return; - } - - if (sb->s_blocksize == 512){ - ll_rw_block(opr,nbreq,bh); - }else{ - 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); - } + 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/fat/cache.c b/fs/fat/cache.c index b984fe759..45f4a800c 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -30,14 +30,20 @@ static struct fat_cache *fat_cache,cache[FAT_CACHE]; int fat_access(struct super_block *sb,int nr,int new_value) { + return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value); +} + +int fat_bmap(struct inode *inode,int sector) +{ + return MSDOS_SB(inode->i_sb)->cvf_format->cvf_bmap(inode,sector); +} + +int default_fat_access(struct super_block *sb,int nr,int new_value) +{ struct buffer_head *bh,*bh2,*c_bh,*c_bh2; unsigned char *p_first,*p_last; int copy,first,last,next,b; - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->fat_access) - return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value); - if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters) return 0; if (MSDOS_SB(sb)->fat_bits == 32) { @@ -265,14 +271,11 @@ int fat_get_cluster(struct inode *inode,int cluster) return nr; } -int fat_smap(struct inode *inode,int sector) +int default_fat_bmap(struct inode *inode,int sector) { - struct msdos_sb_info *sb; + struct msdos_sb_info *sb=MSDOS_SB(inode->i_sb); int cluster,offset; - sb = MSDOS_SB(inode->i_sb); - if (sb->cvf_format && sb->cvf_format->cvf_smap) - return sb->cvf_format->cvf_smap(inode,sector); if ((sb->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) && !MSDOS_I(inode)->i_start))) { @@ -280,6 +283,8 @@ int fat_smap(struct inode *inode,int sector) return 0; return sector+sb->dir_start; } + if (sector >= (MSDOS_I(inode)->i_realsize>>9)) + return 0; cluster = sector/sb->cluster_size; offset = sector % sb->cluster_size; if (!(cluster = fat_get_cluster(inode,cluster))) return 0; diff --git a/fs/fat/cvf.c b/fs/fat/cvf.c index 9dd70f8c5..24cf1164f 100644 --- a/fs/fat/cvf.c +++ b/fs/fat/cvf.c @@ -21,6 +21,83 @@ #define MAX_CVF_FORMATS 3 +struct buffer_head *default_fat_bread(struct super_block *,int); +struct buffer_head *default_fat_getblk(struct super_block *, int); +struct buffer_head *bigblock_fat_bread(struct super_block *, int); +void default_fat_brelse(struct super_block *, struct buffer_head *); +void bigblock_fat_brelse(struct super_block *, struct buffer_head *); +void default_fat_mark_buffer_dirty (struct super_block *, + struct buffer_head *, int); +void bigblock_fat_mark_buffer_dirty (struct super_block *, + struct buffer_head *, int); +void default_fat_set_uptodate (struct super_block *, struct buffer_head *,int); +void bigblock_fat_set_uptodate (struct super_block *, struct buffer_head *,int); +int default_fat_is_uptodate(struct super_block *, struct buffer_head *); +int bigblock_fat_is_uptodate(struct super_block *, struct buffer_head *); +int default_fat_access(struct super_block *sb,int nr,int new_value); +void default_fat_ll_rw_block ( + struct super_block *sb, + int opr, + int nbreq, + struct buffer_head *bh[32]); +void bigblock_fat_ll_rw_block ( + struct super_block *sb, + int opr, + int nbreq, + struct buffer_head *bh[32]); +int default_fat_bmap(struct inode *inode,int block); +ssize_t default_fat_file_write( + struct file *filp, + const char *buf, + size_t count, + loff_t *ppos); + +struct cvf_format default_cvf = { + 0, /* version - who cares? */ + "plain", + 0, /* flags - who cares? */ + NULL, + NULL, + NULL, + default_fat_bread, + default_fat_getblk, + default_fat_brelse, + default_fat_mark_buffer_dirty, + default_fat_set_uptodate, + default_fat_is_uptodate, + default_fat_ll_rw_block, + default_fat_access, + NULL, + default_fat_bmap, + generic_file_read, + default_fat_file_write, + NULL, + NULL +}; + +struct cvf_format bigblock_cvf = { + 0, /* version - who cares? */ + "big_blocks", + 0, /* flags - who cares? */ + NULL, + NULL, + NULL, + bigblock_fat_bread, + bigblock_fat_bread, + bigblock_fat_brelse, + bigblock_fat_mark_buffer_dirty, + bigblock_fat_set_uptodate, + bigblock_fat_is_uptodate, + bigblock_fat_ll_rw_block, + default_fat_access, + NULL, + default_fat_bmap, + NULL, + default_fat_file_write, + NULL, + NULL +}; + struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL}; int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0}; diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 82d29a0df..628c0bb9c 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -605,7 +605,7 @@ int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, struct super_block *sb = dir->i_sb; loff_t offset, curr; int row; - int res; + struct buffer_head *new_bh; offset = curr = 0; *bh = NULL; @@ -621,11 +621,47 @@ int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, } if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) return -ENOSPC; - if ((res = fat_add_cluster(dir)) < 0) return res; + new_bh = fat_extend_dir(dir); + if (!new_bh) + return -ENOSPC; + fat_brelse(sb, new_bh); do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots); return offset; } +int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat) +{ + struct super_block *sb = dir->i_sb; + struct buffer_head *bh; + struct msdos_dir_entry *de; + __u16 date, time; + + if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC; + /* zeroed out, so... */ + fat_date_unix2dos(dir->i_mtime,&time,&date); + de = (struct msdos_dir_entry*)&bh->b_data[0]; + memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME); + memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME); + de[0].attr = de[1].attr = ATTR_DIR; + de[0].time = de[1].time = CT_LE_W(time); + de[0].date = de[1].date = CT_LE_W(date); + if (is_vfat) { /* extra timestamps */ + de[0].ctime = de[1].ctime = CT_LE_W(time); + de[0].adate = de[0].cdate = + de[1].adate = de[1].cdate = CT_LE_W(date); + } + de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart); + de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16); + de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart); + de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16); + fat_mark_buffer_dirty(sb, bh, 1); + fat_brelse(sb, bh); + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); + + return 0; +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c index a8cd88cac..070361aa9 100644 --- a/fs/fat/fatfs_syms.c +++ b/fs/fat/fatfs_syms.c @@ -17,9 +17,9 @@ extern struct file_operations fat_dir_operations; -EXPORT_SYMBOL(fat_add_cluster); -EXPORT_SYMBOL(fat_add_cluster1); +EXPORT_SYMBOL(fat_new_dir); EXPORT_SYMBOL(fat_bmap); +EXPORT_SYMBOL(fat_get_block); EXPORT_SYMBOL(fat_brelse); EXPORT_SYMBOL(fat_cache_inval_inode); EXPORT_SYMBOL(fat_clear_inode); @@ -44,9 +44,7 @@ EXPORT_SYMBOL(fat_read_super); EXPORT_SYMBOL(fat_search_long); EXPORT_SYMBOL(fat_readdir); EXPORT_SYMBOL(fat_scan); -EXPORT_SYMBOL(fat_smap); EXPORT_SYMBOL(fat_statfs); -EXPORT_SYMBOL(fat_truncate); EXPORT_SYMBOL(fat_uni2esc); EXPORT_SYMBOL(fat_unlock_creation); EXPORT_SYMBOL(fat_write_inode); diff --git a/fs/fat/file.c b/fs/fat/file.c index 5d964da2b..19b91b74f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -57,7 +57,7 @@ struct inode_operations fat_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - fat_bmap, /* get_block */ + fat_get_block, /* get_block */ block_read_full_page, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ @@ -67,392 +67,117 @@ struct inode_operations fat_file_inode_operations = { NULL /* revalidate */ }; -/* #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_file_mmap normally - used since it allocate extra buffer. generic_file_mmap is used for - normal device (512 bytes hardware sectors). -*/ -static struct file_operations fat_file_operations_1024 = { - NULL, /* lseek - default */ - fat_file_read, /* read */ - fat_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select v2.0.x/poll v2.1.x - default */ - NULL, /* ioctl - default */ - fat_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - 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 - MS-DOS 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 fat_file_inode_operations_1024 = { - &fat_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, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - fat_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -static struct file_operations fat_file_operations_readpage = { - NULL, /* lseek - default */ - fat_file_read, /* read */ - fat_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select v2.0.x/poll v2.1.x - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - file_fsync /* fsync */ -}; - -struct inode_operations fat_file_inode_operations_readpage = { - &fat_file_operations_readpage, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - fat_readpage, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - fat_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -#define MSDOS_PREFETCH 32 -struct fat_pre { - int file_sector;/* Next sector to read in the prefetch table */ - /* This is relative to the file, not the disk */ - struct buffer_head *bhlist[MSDOS_PREFETCH]; /* All buffers needed */ - int nblist; /* Number of buffers in bhlist */ - int nolist; /* index in bhlist */ -}; -/* - Order the prefetch of more sectors. -*/ -static void fat_prefetch ( - struct inode *inode, - struct fat_pre *pre, - int nb) /* How many must we 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 */ - int i; - for (i=0; i<nb; i++){ - int sector = fat_smap(inode,pre->file_sector); - if (sector != 0){ - struct buffer_head *bh; - PRINTK (("fsector2 %d -> %d\n",pre->file_sector-1,sector)); - pre->file_sector++; - bh = fat_getblk(sb, sector); - if (bh == NULL) break; - pre->bhlist[pre->nblist++] = bh; - if (!fat_is_uptodate(sb,bh)) - bhreq[nbreq++] = bh; - }else{ - break; - } - } - if (nbreq > 0) fat_ll_rw_block (sb,READ,nbreq,bhreq); - for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL; -} - -/* - Read a file into user space -*/ -static ssize_t fat_file_read_text( +ssize_t fat_file_read( struct file *filp, char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; - struct super_block *sb = inode->i_sb; - char *start = buf; - char *end = buf + count; - int i; - int left_in_file; - struct fat_pre pre; - + return MSDOS_SB(inode->i_sb)->cvf_format + ->cvf_file_read(filp,buf,count,ppos); +} - if (!inode) { - printk("fat_file_read: inode = NULL\n"); - return -EINVAL; + +int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { + unsigned long phys; + phys = fat_bmap(inode, iblock); + if (phys) { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + return 0; } - /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ - if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { - printk("fat_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; + if (!create) + return 0; + if (iblock<<9 != MSDOS_I(inode)->i_realsize) { + BUG(); + return -EIO; } - if (*ppos >= inode->i_size || count == 0) return 0; - /* - Tell the buffer cache which block we expect to read in advance - Since we are limited with the stack, we preread only MSDOS_PREFETCH - because we have to keep the result into the local - arrays pre.bhlist and bhreq. - - Each time we process one block in bhlist, we replace - it by a new prefetch block if needed. - */ - PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,*ppos,inode->i_size,count)); - { - /* - We must prefetch complete block, so we must - take in account the offset in the first block. - */ - int count_max = (*ppos & (SECTOR_SIZE-1)) + count; - int to_reada; /* How many block to read all at once */ - pre.file_sector = *ppos >> SECTOR_BITS; - to_reada = count_max / SECTOR_SIZE; - if (count_max & (SECTOR_SIZE-1)) to_reada++; - if (filp->f_reada || !MSDOS_I(inode)->i_binary){ - /* Doing a read ahead on ASCII file make sure we always */ - /* read enough, since we don't know how many blocks */ - /* we really need */ - int ahead = read_ahead[MAJOR(inode->i_dev)]; - PRINTK (("to_reada %d ahead %d\n",to_reada,ahead)); - if (ahead == 0) ahead = 8; - to_reada += ahead; - } - if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH; - pre.nblist = 0; - fat_prefetch (inode,&pre,to_reada); + if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) { + if (fat_add_cluster(inode)) + return -ENOSPC; } - pre.nolist = 0; - PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist)); - while ((left_in_file = inode->i_size - *ppos) > 0 - && buf < end){ - struct buffer_head *bh = pre.bhlist[pre.nolist]; - char *data; - int size,offset; - if (bh == NULL) break; - pre.bhlist[pre.nolist] = NULL; - pre.nolist++; - if (pre.nolist == MSDOS_PREFETCH/2){ - memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2 - ,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0])); - pre.nblist -= MSDOS_PREFETCH/2; - fat_prefetch (inode,&pre,MSDOS_PREFETCH/2); - pre.nolist = 0; - } - PRINTK (("file_read pos %ld nblist %d %d %d\n",*ppos,pre.nblist,pre.fetched,count)); - wait_on_buffer(bh); - if (!fat_is_uptodate(sb,bh)){ - /* read error ? */ - fat_brelse (sb, bh); - break; - } - offset = *ppos & (SECTOR_SIZE-1); - data = bh->b_data + offset; - size = MIN(SECTOR_SIZE-offset,left_in_file); - if (MSDOS_I(inode)->i_binary) { - size = MIN(size,end-buf); - copy_to_user(buf,data,size); - buf += size; - *ppos += size; - }else{ - for (; size && buf < end; size--) { - char ch = *data++; - ++*ppos; - if (ch == 26){ - *ppos = inode->i_size; - break; - }else if (ch != '\r'){ - put_user(ch,buf++); - } + MSDOS_I(inode)->i_realsize+=SECTOR_SIZE; + phys=fat_bmap(inode, iblock); + if (!phys) + BUG(); + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + bh_result->b_state |= (1UL << BH_New); + return 0; +} + +static int fat_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct page *new_page, **hash; + unsigned long pgpos; + unsigned long page_cache = 0; + long status; + + pgpos = inode->i_size & PAGE_CACHE_MASK; + while (pgpos < page->offset) { + hash = page_hash(inode, pgpos); +repeat_find: new_page = __find_lock_page(inode, pgpos, hash); + if (!new_page) { + if (!page_cache) { + page_cache = page_cache_alloc(); + if (page_cache) + goto repeat_find; + status = -ENOMEM; + goto out; } + new_page = page_cache_entry(page_cache); + if (add_to_page_cache_unique(new_page,inode,pgpos,hash)) + goto repeat_find; + page_cache = 0; } - fat_brelse(sb, bh); + status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL); + UnlockPage(new_page); + page_cache_release(new_page); + if (status < 0) + goto out; + pgpos = MSDOS_I(inode)->i_realsize & PAGE_CACHE_MASK; } - PRINTK (("--- %d -> %d\n",count,(int)(buf-start))); - for (i=0; i<pre.nblist; i++) - fat_brelse (sb, pre.bhlist[i]); - if (start == buf) - return -EIO; - if (!IS_RDONLY(inode)) - inode->i_atime = CURRENT_TIME; - filp->f_reada = 1; /* Will be reset if a lseek is done */ - return buf-start; + status = block_write_cont_page(file, page, offset, bytes, buf); +out: + if (page_cache) + page_cache_free(page_cache); + return status; } -ssize_t fat_file_read( +ssize_t fat_file_write( struct file *filp, - char *buf, + const char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; - if (MSDOS_SB(inode->i_sb)->cvf_format && - MSDOS_SB(inode->i_sb)->cvf_format->cvf_file_read) - return MSDOS_SB(inode->i_sb)->cvf_format - ->cvf_file_read(filp,buf,count,ppos); - - /* - * MS-DOS filesystems with a blocksize > 512 may have blocks - * spread over several hardware sectors (unaligned), which - * is not something the generic routines can (or would want - * to) handle). - */ - if (!MSDOS_I(inode)->i_binary || inode->i_sb->s_blocksize > 512) - return fat_file_read_text(filp, buf, count, ppos); - return generic_file_read(filp, buf, count, ppos); + struct super_block *sb = inode->i_sb; + return MSDOS_SB(sb)->cvf_format + ->cvf_file_write(filp,buf,count,ppos); } -/* - Write to a file either from user space -*/ -ssize_t fat_file_write( + +ssize_t default_fat_file_write( struct file *filp, const char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; - struct super_block *sb = inode->i_sb; - int sector,offset,size,left,written; - int error,carry; - const char *start; - char *to,ch; - struct buffer_head *bh; - int binary_mode = MSDOS_I(inode)->i_binary; - - PRINTK(("fat_file_write: dentry=%p, inode=%p, ino=%ld\n", - filp->f_dentry, inode, inode->i_ino)); - if (!inode) { - printk("fat_file_write: inode = NULL\n"); - return -EINVAL; - } - if (MSDOS_SB(sb)->cvf_format && - MSDOS_SB(sb)->cvf_format->cvf_file_write) - return MSDOS_SB(sb)->cvf_format - ->cvf_file_write(filp,buf,count,ppos); - - /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ - if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { - printk("fat_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - /* system files may be immutable */ - if (IS_IMMUTABLE(inode)) - return -EPERM; -/* - * ok, append may not work when many processes are writing at the same time - * but so what. That way leads to madness anyway. - */ - if (filp->f_flags & O_APPEND) - *ppos = inode->i_size; - if (count == 0) - return 0; - if (*ppos + count > 0x7FFFFFFFLL) { - count = 0x7FFFFFFFLL-*ppos; - if (!count) - return -EFBIG; + int retval; + + retval = generic_file_write(filp, buf, count, ppos, + fat_write_partial_page); + if (retval > 0) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + MSDOS_I(inode)->i_attrs |= ATTR_ARCH; + mark_inode_dirty(inode); } - - error = carry = 0; - for (start = buf; count || carry; count -= size) { - while (!(sector = fat_smap(inode,*ppos >> SECTOR_BITS))) - if ((error = fat_add_cluster(inode)) < 0) break; - if (error) { - fat_truncate(inode); - break; - } - offset = *ppos & (SECTOR_SIZE-1); - size = MIN(SECTOR_SIZE-offset,MAX(carry,count)); - if (binary_mode - && offset == 0 - && (size == SECTOR_SIZE - || *ppos + size >= inode->i_size)){ - /* No need to read the block first since we will */ - /* completely overwrite it */ - /* or at least write past the end of file */ - if (!(bh = fat_getblk(sb,sector))){ - error = -EIO; - break; - } - } else if (!(bh = fat_bread(sb,sector))) { - error = -EIO; - break; - } - if (binary_mode) { - copy_from_user(bh->b_data+offset,buf,written = size); - buf += size; - } else { - written = left = SECTOR_SIZE-offset; - to = (char *) bh->b_data+(*ppos & (SECTOR_SIZE-1)); - if (carry) { - *to++ = '\n'; - left--; - carry = 0; - } - for (size = 0; size < count && left; size++) { - get_user(ch, buf++); - if (ch == '\n') { - *to++ = '\r'; - left--; - } - if (!left) carry = 1; - else { - *to++ = ch; - left--; - } - } - written -= left; - } - update_vm_cache(inode, *ppos, bh->b_data + (*ppos & (SECTOR_SIZE-1)), written); - *ppos += written; - if (*ppos > inode->i_size) { - inode->i_size = *ppos; - mark_inode_dirty(inode); - } - fat_set_uptodate(sb, bh, 1); - fat_mark_buffer_dirty(sb, bh, 0); - fat_brelse(sb, bh); - } - if (start == buf) - return error; - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - mark_inode_dirty(inode); - return buf-start; + return retval; } void fat_truncate(struct inode *inode) @@ -460,14 +185,14 @@ void fat_truncate(struct inode *inode) int cluster; /* Why no return value? Surely the disk could fail... */ + if (IS_RDONLY (inode)) + return /* -EPERM */; if (IS_IMMUTABLE(inode)) return /* -EPERM */; - if(inode->i_sb->s_flags&MS_RDONLY) { - printk("FAT: fat_truncate called though fs is read-only, uhh...\n"); - return /* -EROFS */; - } cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; + MSDOS_I(inode)->i_realsize = ((inode->i_size-1) | (SECTOR_SIZE-1)) + 1; (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; + inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b55bfd712..4262fa756 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -33,6 +33,8 @@ #include <asm/uaccess.h> #include <asm/unaligned.h> +extern struct cvf_format default_cvf, bigblock_cvf; + /* #define FAT_PARANOIA 1 */ #define DEBUG_LEVEL 0 #ifdef FAT_DEBUG @@ -166,7 +168,7 @@ void fat_clear_inode(struct inode *inode) void fat_put_super(struct super_block *sb) { - if (MSDOS_SB(sb)->cvf_format) { + if (MSDOS_SB(sb)->cvf_format->cvf_version) { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version); MSDOS_SB(sb)->cvf_format->unmount_cvf(sb); } @@ -305,12 +307,12 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug, else opts->quiet = 1; } else if (!strcmp(this_char,"blocksize")) { - if (*value) ret = 0; - else if (*blksize != 512 && - *blksize != 1024 && - *blksize != 2048) { - printk ("MSDOS FS: Invalid blocksize " - "(512, 1024, or 2048)\n"); + if (!value || !*value) ret = 0; + else { + *blksize = simple_strtoul(value,&value,0); + if (*value || (*blksize != 512 && + *blksize != 1024 && *blksize != 2048)) + ret = 0; } } else if (!strcmp(this_char,"sys_immutable")) { @@ -364,7 +366,6 @@ static void fat_read_root(struct inode *inode) struct super_block *sb = inode->i_sb; int nr; - MSDOS_I(inode)->i_binary = 1; INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); MSDOS_I(inode)->i_location = 0; MSDOS_I(inode)->i_fat_inode = inode; @@ -394,6 +395,7 @@ static void fat_read_root(struct inode *inode) inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ inode->i_blksize*MSDOS_SB(sb)->cluster_size; MSDOS_I(inode)->i_logstart = 0; + MSDOS_I(inode)->i_realsize = inode->i_size; MSDOS_I(inode)->i_attrs = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = 0; @@ -477,10 +479,10 @@ fat_read_super(struct super_block *sb, void *data, int silent, sb->s_blocksize = 1024; set_blocksize(sb->s_dev, 1024); } - bh = fat_bread(sb, 0); + bh = bread(sb->s_dev, 0, sb->s_blocksize); unlock_super(sb); - if (bh == NULL || !fat_is_uptodate(sb,bh)) { - fat_brelse (sb, bh); + if (bh == NULL || !buffer_uptodate(bh)) { + brelse (bh); goto out_no_bread; } @@ -514,7 +516,7 @@ fat_read_super(struct super_block *sb, void *data, int silent, /* Must be FAT32 */ fat32 = 1; - MSDOS_SB(sb)->fat_length= CF_LE_W(b->fat32_length)*sector_mult; + MSDOS_SB(sb)->fat_length= CF_LE_L(b->fat32_length)*sector_mult; MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster); /* MC - if info_sector is 0, don't multiply by 0 */ @@ -572,7 +574,7 @@ fat_read_super(struct super_block *sb, void *data, int silent, MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track || !b->heads; } - fat_brelse(sb, bh); + brelse(bh); set_blocksize(sb->s_dev, blksize); /* This must be done after the brelse because the bh is a dummy @@ -589,6 +591,10 @@ fat_read_super(struct super_block *sb, void *data, int silent, i = detect_cvf(sb,cvf_format); if (i >= 0) error = cvf_formats[i]->mount_cvf(sb,cvf_options); + else if (sb->s_blocksize == 512) + MSDOS_SB(sb)->cvf_format = &default_cvf; + else + MSDOS_SB(sb)->cvf_format = &bigblock_cvf; 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," @@ -616,7 +622,7 @@ fat_read_super(struct super_block *sb, void *data, int silent, sb->s_magic = MSDOS_SUPER_MAGIC; /* set up enough so that it can read an inode */ init_waitqueue_head(&MSDOS_SB(sb)->fat_wait); - MSDOS_SB(sb)->fat_lock = 0; + init_MUTEX(&MSDOS_SB(sb)->fat_lock); MSDOS_SB(sb)->prev_free = 0; cp = opts.codepage ? opts.codepage : 437; @@ -717,25 +723,6 @@ int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } - -int fat_bmap(struct inode *inode,int block) -{ - struct msdos_sb_info *sb; - int cluster,offset; - - sb = MSDOS_SB(inode->i_sb); - if (sb->cvf_format && - sb->cvf_format->cvf_bmap) - return sb->cvf_format->cvf_bmap(inode,block); - if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) { - return sb->dir_start + block; - } - cluster = block/sb->cluster_size; - offset = block % sb->cluster_size; - if (!(cluster = fat_get_cluster(inode,cluster))) return 0; - return (cluster-2)*sb->cluster_size+sb->data_start+offset; -} - static int is_exec(char *extension) { char *exe_extensions = "EXECOMBAT", *walk; @@ -752,7 +739,6 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) struct super_block *sb = inode->i_sb; int nr; - MSDOS_I(inode)->i_binary = 1; INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); MSDOS_I(inode)->i_location = 0; MSDOS_I(inode)->i_fat_inode = inode; @@ -789,6 +775,7 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) break; } } + MSDOS_I(inode)->i_realsize = inode->i_size; } else { /* not a directory */ inode->i_mode = MSDOS_MKMODE(de->attr, ((IS_NOEXEC(inode) || @@ -796,14 +783,7 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) !is_exec(de->ext))) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG; - if (MSDOS_SB(sb)->cvf_format) - inode->i_op = (MSDOS_SB(sb)->cvf_format->flags & CVF_USE_READPAGE) - ? &fat_file_inode_operations_readpage - : &fat_file_inode_operations_1024; - else - inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048) - ? &fat_file_inode_operations_1024 - : &fat_file_inode_operations; + inode->i_op = &fat_file_inode_operations; MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (MSDOS_SB(sb)->fat_bits == 32) { MSDOS_I(inode)->i_start |= @@ -812,12 +792,11 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = 1; inode->i_size = CF_LE_L(de->size); + MSDOS_I(inode)->i_realsize = ((inode->i_size-1)|(SECTOR_SIZE-1))+1; } if(de->attr & ATTR_SYS) if (MSDOS_SB(sb)->options.sys_immutable) inode->i_flags |= S_IMMUTABLE; - MSDOS_I(inode)->i_binary = - fat_is_binary(MSDOS_SB(sb)->options.conversion, de->ext); MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; /* this is as close to the truth as we can get ... */ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 4cd218d58..cd2762732 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -79,35 +79,29 @@ int fat_is_binary(char conversion,char *extension) /* File creation lock. This is system-wide to avoid deadlocks in rename. */ /* (rename might deadlock before detecting cross-FS moves.) */ -static DECLARE_WAIT_QUEUE_HEAD(creation_wait); -static int creation_lock = 0; - +static DECLARE_MUTEX(creation_lock); void fat_lock_creation(void) { - while (creation_lock) sleep_on(&creation_wait); - creation_lock = 1; + down(&creation_lock); } void fat_unlock_creation(void) { - creation_lock = 0; - wake_up(&creation_wait); + up(&creation_lock); } void lock_fat(struct super_block *sb) { - while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait); - MSDOS_SB(sb)->fat_lock = 1; + down(&(MSDOS_SB(sb)->fat_lock)); } void unlock_fat(struct super_block *sb) { - MSDOS_SB(sb)->fat_lock = 0; - wake_up(&MSDOS_SB(sb)->fat_wait); + up(&(MSDOS_SB(sb)->fat_lock)); } /* Flushes the number of free clusters on FAT32 */ @@ -144,7 +138,70 @@ void fat_clusters_flush(struct super_block *sb) * represented by inode. The cluster is zero-initialized. */ -struct buffer_head *fat_add_cluster1(struct inode *inode) +/* not a directory */ + +int fat_add_cluster(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + int count,nr,limit,last,curr,file_cluster; + int res = -ENOSPC; + int cluster_size = MSDOS_SB(sb)->cluster_size; + + if (!MSDOS_SB(sb)->free_clusters) return res; + lock_fat(sb); + limit = MSDOS_SB(sb)->clusters; + nr = limit; /* to keep GCC happy */ + for (count = 0; count < limit; count++) { + nr = ((count+MSDOS_SB(sb)->prev_free) % limit)+2; + if (fat_access(sb,nr,-1) == 0) break; + } + MSDOS_SB(sb)->prev_free = (count+MSDOS_SB(sb)->prev_free+1) % limit; + if (count >= limit) { + MSDOS_SB(sb)->free_clusters = 0; + unlock_fat(sb); + return res; + } + fat_access(sb,nr,EOF_FAT(sb)); + if (MSDOS_SB(sb)->free_clusters != -1) + MSDOS_SB(sb)->free_clusters--; + if (MSDOS_SB(sb)->fat_bits == 32) + fat_clusters_flush(sb); + unlock_fat(sb); + 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 ((curr = MSDOS_I(inode)->i_start) != 0) { + fat_cache_lookup(inode,INT_MAX,&last,&curr); + file_cluster = last; + while (curr && curr != -1){ + file_cluster++; + if (!(curr = fat_access(sb, last = curr,-1))) { + fat_fs_panic(sb,"File without EOF"); + return res; + } + } + } + if (last) fat_access(sb,last,nr); + else { + MSDOS_I(inode)->i_start = nr; + MSDOS_I(inode)->i_logstart = nr; + mark_inode_dirty(inode); + } + fat_cache_add(inode,file_cluster,nr); + inode->i_blocks += cluster_size; + return 0; +} + +struct buffer_head *fat_extend_dir(struct inode *inode) { struct super_block *sb = inode->i_sb; int count,nr,limit,last,curr,sector,last_sector,file_cluster; @@ -248,30 +305,17 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); fat_cache_add(inode,file_cluster,nr); } inode->i_blocks += cluster_size; - if (S_ISDIR(inode->i_mode)) { - if (inode->i_size & (SECTOR_SIZE-1)) { - fat_fs_panic(sb,"Odd directory size"); - inode->i_size = (inode->i_size+SECTOR_SIZE) & - ~(SECTOR_SIZE-1); - } - inode->i_size += SECTOR_SIZE*cluster_size; -#ifdef DEBUG -printk("size is %d now (%x)\n",inode->i_size,inode); -#endif - mark_inode_dirty(inode); + if (inode->i_size & (SECTOR_SIZE-1)) { + fat_fs_panic(sb,"Odd directory size"); + inode->i_size = (inode->i_size+SECTOR_SIZE) & + ~(SECTOR_SIZE-1); } + inode->i_size += SECTOR_SIZE*cluster_size; + MSDOS_I(inode)->i_realsize += SECTOR_SIZE*cluster_size; + mark_inode_dirty(inode); return res; } -int fat_add_cluster(struct inode *inode) -{ - struct buffer_head *bh = fat_add_cluster1(inode); - if (!bh) - return -ENOSPC; - fat_brelse(inode->i_sb, bh); - return 0; -} - /* Linear day numbers of the respective 1sts in non-leap years. */ static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; @@ -351,7 +395,7 @@ int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, if (*bh) fat_brelse(sb, *bh); *bh = NULL; - if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1) + if ((sector = fat_bmap(dir,offset >> SECTOR_BITS)) == -1) return -1; PRINTK (("get_entry sector %d %p\n",sector,*bh)); PRINTK (("get_entry sector apres brelse\n")); diff --git a/fs/fcntl.c b/fs/fcntl.c index d12e66244..26c9bf4a0 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -12,35 +12,89 @@ extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); -static inline int dupfd(struct file *file, unsigned int arg) +/* + * locate_fd finds a free file descriptor in the open_fds fdset, + * expanding the fd arrays if necessary. The files write lock will be + * held on exit to ensure that the fd can be entered atomically. + */ + +static inline int locate_fd(struct files_struct *files, + struct file *file, int start) { - struct files_struct * files = current->files; + unsigned int newfd; int error; - error = -EMFILE; write_lock(&files->file_lock); - arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg); - if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur) - goto out_putf; - FD_SET(arg, &files->open_fds); - FD_CLR(arg, &files->close_on_exec); - write_unlock(&files->file_lock); - fd_install(arg, file); - error = arg; + +repeat: + error = -EMFILE; + if (start < files->next_fd) + start = files->next_fd; + if (start >= files->max_fdset) { + expand: + error = expand_files(files, start); + if (error < 0) + goto out; + goto repeat; + } + + newfd = find_next_zero_bit(files->open_fds->fds_bits, + files->max_fdset, start); + + error = -EMFILE; + if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur) + goto out; + if (newfd >= files->max_fdset) + goto expand; + + error = expand_files(files, newfd); + if (error < 0) + goto out; + if (error) /* If we might have blocked, try again. */ + goto repeat; + + if (start <= files->next_fd) + files->next_fd = newfd + 1; + + error = newfd; + out: return error; +} + +static inline void allocate_fd(struct files_struct *files, + struct file *file, int fd) +{ + FD_SET(fd, files->open_fds); + FD_CLR(fd, files->close_on_exec); + write_unlock(&files->file_lock); + fd_install(fd, file); +} + +static int dupfd(struct file *file, int start) +{ + struct files_struct * files = current->files; + int ret; + + ret = locate_fd(files, file, start); + if (ret < 0) + goto out_putf; + allocate_fd(files, file, ret); + return ret; out_putf: + write_unlock(&files->file_lock); fput(file); - goto out; + return ret; } -asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) +asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; struct file * file; + struct files_struct * files = current->files; - read_lock(¤t->files->file_lock); + write_lock(¤t->files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; err = newfd; @@ -49,22 +103,41 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) err = -EBADF; if (newfd >= NR_OPEN) goto out_unlock; /* following POSIX.1 6.2.1 */ - get_file(file); - read_unlock(¤t->files->file_lock); + get_file(file); /* We are now finished with oldfd */ + + err = expand_files(files, newfd); + if (err < 0) { + write_unlock(&files->file_lock); + fput(file); + goto out; + } + + /* To avoid races with open() and dup(), we will mark the fd as + * in-use in the open-file bitmap throughout the entire dup2() + * process. This is quite safe: do_close() uses the fd array + * entry, not the bitmap, to decide what work needs to be + * done. --sct */ + FD_SET(newfd, files->open_fds); + write_unlock(&files->file_lock); + + do_close(newfd, 0); + + write_lock(&files->file_lock); + allocate_fd(files, file, newfd); + err = newfd; - sys_close(newfd); - err = dupfd(file, newfd); out: return err; out_unlock: - read_unlock(¤t->files->file_lock); + write_unlock(¤t->files->file_lock); goto out; } -asmlinkage int sys_dup(unsigned int fildes) +asmlinkage long sys_dup(unsigned int fildes) { int ret = -EBADF; struct file * file = fget(fildes); + if (file) ret = dupfd(file, 0); return ret; @@ -117,13 +190,13 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) } break; case F_GETFD: - err = FD_ISSET(fd, ¤t->files->close_on_exec); + err = FD_ISSET(fd, current->files->close_on_exec); break; case F_SETFD: if (arg&1) - FD_SET(fd, ¤t->files->close_on_exec); + FD_SET(fd, current->files->close_on_exec); else - FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, current->files->close_on_exec); break; case F_GETFL: err = filp->f_flags; @@ -151,7 +224,6 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_owner.pid; break; case F_SETOWN: - err = 0; filp->f_owner.pid = arg; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; @@ -162,7 +234,8 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_owner.signum; break; case F_SETSIG: - if (arg <= 0 || arg > _NSIG) { + /* arg == 0 restores default behaviour. */ + if (arg < 0 || arg > _NSIG) { err = -EINVAL; break; } @@ -171,10 +244,9 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) break; default: /* sockets need a few special fcntls. */ + err = -EINVAL; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, cmd, arg); - else - err = -EINVAL; break; } fput(filp); @@ -12,38 +12,38 @@ #include <linux/mm.h> #include <linux/malloc.h> -static int fifo_open(struct inode * inode,struct file * filp) +static int fifo_open(struct inode *inode, struct file *filp) { - int retval = 0; - unsigned long page = 0; - struct pipe_inode_info *info, *tmp = NULL; - - if (inode->i_pipe) - goto got_it; - tmp = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL); - if (inode->i_pipe) - goto got_it; - if (!tmp) - goto oom; - page = __get_free_page(GFP_KERNEL); - if (inode->i_pipe) - goto got_it; - if (!page) - goto oom; - inode->i_pipe = tmp; - PIPE_LOCK(*inode) = 0; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_BASE(*inode) = (char *) page; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - init_waitqueue_head(&PIPE_WAIT(*inode)); - tmp = NULL; /* no need to free it */ - page = 0; - -got_it: - - switch( filp->f_mode ) { + int ret; + ret = -ERESTARTSYS; + if (down_interruptible(PIPE_SEM(*inode))) + goto err_nolock_nocleanup; + + if (! inode->i_pipe) { + unsigned long page; + struct pipe_inode_info *info; + + info = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL); + + ret = -ENOMEM; + if (!info) + goto err_nocleanup; + page = __get_free_page(GFP_KERNEL); + if (!page) { + kfree(info); + goto err_nocleanup; + } + + inode->i_pipe = info; + + init_waitqueue_head(PIPE_WAIT(*inode)); + PIPE_BASE(*inode) = (char *) page; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + } + + switch (filp->f_mode) { case 1: /* * O_RDONLY @@ -51,26 +51,26 @@ got_it: * opened, even when there is no process writing the FIFO. */ filp->f_op = &connecting_fifo_fops; - if (!PIPE_READERS(*inode)++) - wake_up_interruptible(&PIPE_WAIT(*inode)); - if (!(filp->f_flags & O_NONBLOCK) && !PIPE_WRITERS(*inode)) { - PIPE_RD_OPENERS(*inode)++; + if (PIPE_READERS(*inode)++ == 0) + wake_up_interruptible(PIPE_WAIT(*inode)); + + if (!(filp->f_flags & O_NONBLOCK)) { while (!PIPE_WRITERS(*inode)) { - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - interruptible_sleep_on(&PIPE_WAIT(*inode)); + if (signal_pending(current)) + goto err_rd; + up(PIPE_SEM(*inode)); + interruptible_sleep_on(PIPE_WAIT(*inode)); + + /* Note that using down_interruptible here + and similar places below is pointless, + since we have to acquire the lock to clean + up properly. */ + down(PIPE_SEM(*inode)); } - if (!--PIPE_RD_OPENERS(*inode)) - wake_up_interruptible(&PIPE_WAIT(*inode)); } - while (PIPE_WR_OPENERS(*inode)) - interruptible_sleep_on(&PIPE_WAIT(*inode)); + if (PIPE_WRITERS(*inode)) filp->f_op = &read_fifo_fops; - if (retval && !--PIPE_READERS(*inode)) - wake_up_interruptible(&PIPE_WAIT(*inode)); break; case 2: @@ -79,29 +79,21 @@ got_it: * POSIX.1 says that O_NONBLOCK means return -1 with * errno=ENXIO when there is no process reading the FIFO. */ - if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode)) { - retval = -ENXIO; - break; - } + ret = -ENXIO; + if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode)) + goto err; + filp->f_op = &write_fifo_fops; if (!PIPE_WRITERS(*inode)++) - wake_up_interruptible(&PIPE_WAIT(*inode)); - if (!PIPE_READERS(*inode)) { - PIPE_WR_OPENERS(*inode)++; - while (!PIPE_READERS(*inode)) { - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - interruptible_sleep_on(&PIPE_WAIT(*inode)); - } - if (!--PIPE_WR_OPENERS(*inode)) - wake_up_interruptible(&PIPE_WAIT(*inode)); + wake_up_interruptible(PIPE_WAIT(*inode)); + + while (!PIPE_READERS(*inode)) { + if (signal_pending(current)) + goto err_wr; + up(PIPE_SEM(*inode)); + interruptible_sleep_on(PIPE_WAIT(*inode)); + down(PIPE_SEM(*inode)); } - while (PIPE_RD_OPENERS(*inode)) - interruptible_sleep_on(&PIPE_WAIT(*inode)); - if (retval && !--PIPE_WRITERS(*inode)) - wake_up_interruptible(&PIPE_WAIT(*inode)); break; case 3: @@ -112,39 +104,47 @@ got_it: * the process can at least talk to itself. */ filp->f_op = &rdwr_fifo_fops; - if (!PIPE_READERS(*inode)++) - wake_up_interruptible(&PIPE_WAIT(*inode)); - while (PIPE_WR_OPENERS(*inode)) - interruptible_sleep_on(&PIPE_WAIT(*inode)); - if (!PIPE_WRITERS(*inode)++) - wake_up_interruptible(&PIPE_WAIT(*inode)); - while (PIPE_RD_OPENERS(*inode)) - interruptible_sleep_on(&PIPE_WAIT(*inode)); + + PIPE_READERS(*inode)++; + PIPE_WRITERS(*inode)++; + if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1) + wake_up_interruptible(PIPE_WAIT(*inode)); break; default: - retval = -EINVAL; + ret = -EINVAL; + goto err; } - if (retval) - goto cleanup; -out: - if (tmp) - kfree(tmp); - if (page) - free_page(page); - return retval; - -cleanup: + + /* Ok! */ + up(PIPE_SEM(*inode)); + return 0; + +err_rd: + if (!--PIPE_READERS(*inode)) + wake_up_interruptible(PIPE_WAIT(*inode)); + ret = -ERESTARTSYS; + goto err; + +err_wr: + if (!--PIPE_WRITERS(*inode)) + wake_up_interruptible(PIPE_WAIT(*inode)); + ret = -ERESTARTSYS; + goto err; + +err: if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { - info = inode->i_pipe; + struct pipe_inode_info *info = inode->i_pipe; inode->i_pipe = NULL; free_page((unsigned long)info->base); kfree(info); } - goto out; -oom: - retval = -ENOMEM; - goto out; + +err_nocleanup: + up(PIPE_SEM(*inode)); + +err_nolock_nocleanup: + return ret; } /* diff --git a/fs/file.c b/fs/file.c new file mode 100644 index 000000000..fd33dc8b8 --- /dev/null +++ b/fs/file.c @@ -0,0 +1,240 @@ +/* + * linux/fs/open.c + * + * Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes + * + * Manage the dynamic fd arrays in the process files_struct. + */ + +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> + +#include <asm/bitops.h> + + +/* + * Allocate an fd array, using get_free_page() if possible. + * Note: the array isn't cleared at allocation time. + */ +struct file ** alloc_fd_array(int num) +{ + struct file **new_fds; + int size = num * sizeof(struct file *); + + if (size < PAGE_SIZE) + new_fds = (struct file **) kmalloc(size, GFP_KERNEL); + else if (size == PAGE_SIZE) + new_fds = (struct file **) __get_free_page(GFP_KERNEL); + else + new_fds = (struct file **) vmalloc(size); + return new_fds; +} + +void free_fd_array(struct file **array, int num) +{ + int size = num * sizeof(struct file *); + + if (!array) { + printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); + return; + } + + if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */ + return; + else if (size < PAGE_SIZE) + kfree(array); + else if (size == PAGE_SIZE) + free_page((unsigned long) array); + else + vfree(array); +} + +/* + * Expand the fd array in the files_struct. Called with the files + * spinlock held for write. + */ + +int expand_fd_array(struct files_struct *files, int nr) +{ + struct file **new_fds; + int error, nfds; + + + error = -EMFILE; + if (files->max_fds >= NR_OPEN || nr > NR_OPEN) + goto out; + + nfds = files->max_fds; + write_unlock(&files->file_lock); + + /* + * Expand to the max in easy steps, and keep expanding it until + * we have enough for the requested fd array size. + */ + + do { +#if NR_OPEN_DEFAULT < 256 + if (nfds < 256) + nfds = 256; + else +#endif + if (nfds < (PAGE_SIZE / sizeof(struct file *))) + nfds = PAGE_SIZE / sizeof(struct file *); + else { + nfds = nfds * 2; + if (nfds > NR_OPEN) + nfds = NR_OPEN; + } + } while (nfds < nr); + + error = -ENOMEM; + new_fds = alloc_fd_array(nfds); + write_lock(&files->file_lock); + if (!new_fds) + goto out; + + /* Copy the existing array and install the new pointer */ + + if (nfds > files->max_fds) { + struct file **old_fds; + int i; + + old_fds = xchg(&files->fd, new_fds); + i = xchg(&files->max_fds, nfds); + + /* Don't copy/clear the array if we are creating a new + fd array for fork() */ + if (i) { + memcpy(new_fds, old_fds, i * sizeof(struct file *)); + /* clear the remainder of the array */ + memset(&new_fds[i], 0, + (nfds-i) * sizeof(struct file *)); + + write_unlock(&files->file_lock); + free_fd_array(old_fds, i); + write_lock(&files->file_lock); + } + } else { + /* Somebody expanded the array while we slept ... */ + write_unlock(&files->file_lock); + free_fd_array(new_fds, nfds); + write_lock(&files->file_lock); + } + error = 0; +out: + return error; +} + +/* + * Allocate an fdset array, using get_free_page() if possible. + * Note: the array isn't cleared at allocation time. + */ +fd_set * alloc_fdset(int num) +{ + fd_set *new_fdset; + int size = num / 8; + + if (size < PAGE_SIZE) + new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL); + else if (size == PAGE_SIZE) + new_fdset = (fd_set *) __get_free_page(GFP_KERNEL); + else + new_fdset = (fd_set *) vmalloc(size); + return new_fdset; +} + +void free_fdset(fd_set *array, int num) +{ + int size = num / 8; + + if (!array) { + printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num); + return; + } + + if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */ + return; + else if (size < PAGE_SIZE) + kfree(array); + else if (size == PAGE_SIZE) + free_page((unsigned long) array); + else + vfree(array); +} + +/* + * Expand the fdset in the files_struct. Called with the files spinlock + * held for write. + */ +int expand_fdset(struct files_struct *files, int nr) +{ + fd_set *new_openset = 0, *new_execset = 0; + int error, nfds = 0; + + error = -EMFILE; + if (files->max_fdset >= NR_OPEN || nr > NR_OPEN) + goto out; + + nfds = files->max_fdset; + write_unlock(&files->file_lock); + + /* Expand to the max in easy steps */ + do { + if (nfds < (PAGE_SIZE * 8)) + nfds = PAGE_SIZE * 8; + else { + nfds = nfds * 2; + if (nfds > NR_OPEN) + nfds = NR_OPEN; + } + } while (nfds < nr); + + error = -ENOMEM; + new_openset = alloc_fdset(nfds); + new_execset = alloc_fdset(nfds); + write_lock(&files->file_lock); + if (!new_openset || !new_execset) + goto out; + + error = 0; + + /* Copy the existing tables and install the new pointers */ + if (nfds > files->max_fdset) { + int i = files->max_fdset / (sizeof(unsigned long) * 8); + int count = (nfds - files->max_fdset) / 8; + + /* + * Don't copy the entire array if the current fdset is + * not yet initialised. + */ + if (i) { + memcpy (new_openset, files->open_fds, files->max_fdset/8); + memcpy (new_execset, files->close_on_exec, files->max_fdset/8); + memset (&new_openset->fds_bits[i], 0, count); + memset (&new_execset->fds_bits[i], 0, count); + } + + nfds = xchg(&files->max_fdset, nfds); + new_openset = xchg(&files->open_fds, new_openset); + new_execset = xchg(&files->close_on_exec, new_execset); + write_unlock(&files->file_lock); + free_fdset (new_openset, nfds); + free_fdset (new_execset, nfds); + write_lock(&files->file_lock); + return 0; + } + /* Somebody expanded the array while we slept ... */ + +out: + write_unlock(&files->file_lock); + if (new_openset) + free_fdset(new_openset, nfds); + if (new_execset) + free_fdset(new_execset, nfds); + write_lock(&files->file_lock); + return error; +} + diff --git a/fs/file_table.c b/fs/file_table.c index cb9ef16e9..ee2f4e788 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -116,16 +116,29 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) return 0; } -void _fput(struct file *file) +/* + * Called when retiring the last use of a file pointer. + */ +static void __fput(struct file *filp) { - atomic_inc(&file->f_count); + struct dentry * dentry = filp->f_dentry; + struct inode * inode = dentry->d_inode; + + if (filp->f_op && filp->f_op->release) + filp->f_op->release(inode, filp); + filp->f_dentry = NULL; + if (filp->f_mode & FMODE_WRITE) + put_write_access(inode); + dput(dentry); +} +void _fput(struct file *file) +{ lock_kernel(); locks_remove_flock(file); /* Still need the */ __fput(file); /* big lock here. */ unlock_kernel(); - atomic_set(&file->f_count, 0); file_list_lock(); list_del(&file->f_list); list_add(&file->f_list, &free_list); @@ -182,7 +195,7 @@ int fs_may_remount_ro(struct super_block *sb) /* Writable file? */ if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) - return 0; + goto too_bad; } file_list_unlock(); return 1; /* Tis' cool bro. */ diff --git a/fs/filesystems.c b/fs/filesystems.c index ee3f32c67..b05b3b657 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -26,6 +26,7 @@ #include <linux/romfs_fs.h> #include <linux/auto_fs.h> #include <linux/qnx4_fs.h> +#include <linux/udf_fs.h> #include <linux/ntfs_fs.h> #include <linux/hfs_fs.h> #include <linux/devpts_fs.h> @@ -149,6 +150,10 @@ void __init filesystem_setup(void) #ifdef CONFIG_QNX4FS_FS init_qnx4_fs(); #endif + +#ifdef CONFIG_UDF_FS + init_udf_fs(); +#endif #ifdef CONFIG_NLS init_nls(); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index cae7bbf72..cb10a760c 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -493,7 +493,7 @@ bail3: return NULL; } -__initfunc(int init_hfs_fs(void)) +int __init init_hfs_fs(void) { hfs_cat_init(); return register_filesystem(&hfs_fs); diff --git a/fs/inode.c b/fs/inode.c index 242696d87..66f76f927 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -103,7 +103,7 @@ static void __wait_on_inode(struct inode * inode) add_wait_queue(&inode->i_wait, &wait); repeat: - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); if (inode->i_state & I_LOCK) { schedule(); goto repeat; diff --git a/fs/iobuf.c b/fs/iobuf.c new file mode 100644 index 000000000..b46a13bfd --- /dev/null +++ b/fs/iobuf.c @@ -0,0 +1,136 @@ +/* + * iobuf.c + * + * Keep track of the general-purpose IO-buffer structures used to track + * abstract kernel-space io buffers. + * + */ + +#include <linux/iobuf.h> +#include <linux/malloc.h> +#include <linux/slab.h> + +static kmem_cache_t *kiobuf_cachep; + +/* + * The default IO completion routine for kiobufs: just wake up + * the kiobuf, nothing more. + */ + +void simple_wakeup_kiobuf(struct kiobuf *kiobuf) +{ + wake_up(&kiobuf->wait_queue); +} + + +void __init kiobuf_init(void) +{ + kiobuf_cachep = kmem_cache_create("kiobuf", + sizeof(struct kiobuf), + 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if(!kiobuf_cachep) + panic("Cannot create kernel iobuf cache\n"); +} + + +int alloc_kiovec(int nr, struct kiobuf **bufp) +{ + int i; + struct kiobuf *iobuf; + + for (i = 0; i < nr; i++) { + iobuf = kmem_cache_alloc(kiobuf_cachep, SLAB_KERNEL); + if (!iobuf) { + free_kiovec(i, bufp); + return -ENOMEM; + } + + memset(iobuf, 0, sizeof(*iobuf)); + init_waitqueue_head(&iobuf->wait_queue); + iobuf->end_io = simple_wakeup_kiobuf; + iobuf->array_len = KIO_STATIC_PAGES; + iobuf->pagelist = iobuf->page_array; + iobuf->maplist = iobuf->map_array; + *bufp++ = iobuf; + } + + return 0; +} + +void free_kiovec(int nr, struct kiobuf **bufp) +{ + int i; + struct kiobuf *iobuf; + + for (i = 0; i < nr; i++) { + iobuf = bufp[i]; + if (iobuf->array_len > KIO_STATIC_PAGES) { + kfree (iobuf->pagelist); + kfree (iobuf->maplist); + } + kmem_cache_free(kiobuf_cachep, bufp[i]); + } +} + +int expand_kiobuf(struct kiobuf *iobuf, int wanted) +{ + unsigned long * pagelist; + struct page ** maplist; + + if (iobuf->array_len >= wanted) + return 0; + + pagelist = (unsigned long *) + kmalloc(wanted * sizeof(unsigned long), GFP_KERNEL); + if (!pagelist) + return -ENOMEM; + + maplist = (struct page **) + kmalloc(wanted * sizeof(struct page **), GFP_KERNEL); + if (!maplist) { + kfree(pagelist); + return -ENOMEM; + } + + /* Did it grow while we waited? */ + if (iobuf->array_len >= wanted) { + kfree(pagelist); + kfree(maplist); + return 0; + } + + memcpy (pagelist, iobuf->pagelist, wanted * sizeof(unsigned long)); + memcpy (maplist, iobuf->maplist, wanted * sizeof(struct page **)); + + if (iobuf->array_len > KIO_STATIC_PAGES) { + kfree (iobuf->pagelist); + kfree (iobuf->maplist); + } + + iobuf->pagelist = pagelist; + iobuf->maplist = maplist; + iobuf->array_len = wanted; + return 0; +} + + +void kiobuf_wait_for_io(struct kiobuf *kiobuf) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&kiobuf->wait_queue, &wait); +repeat: + run_task_queue(&tq_disk); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&kiobuf->io_count) != 0) { + schedule(); + goto repeat; + } + tsk->state = TASK_RUNNING; + remove_wait_queue(&kiobuf->wait_queue, &wait); +} + + + diff --git a/fs/ioctl.c b/fs/ioctl.c index b9f2363e6..e7e226056 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -48,7 +48,7 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) } -asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; unsigned int flag; @@ -61,11 +61,11 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) lock_kernel(); switch (cmd) { case FIOCLEX: - FD_SET(fd, ¤t->files->close_on_exec); + FD_SET(fd, current->files->close_on_exec); break; case FIONCLEX: - FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, current->files->close_on_exec); break; case FIONBIO: diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 1cf86ae63..f055c52e0 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1469,7 +1469,7 @@ static struct file_system_type iso9660_fs_type = { NULL }; -__initfunc(int init_iso9660_fs(void)) +int __init init_iso9660_fs(void) { return register_filesystem(&iso9660_fs_type); } diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index efda2a30d..7a4674d85 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -172,7 +172,7 @@ reclaimer(void *ptr) /* First, reclaim all locks that have been granted previously. */ do { - for (fl = file_lock_table; fl; fl = fl->fl_next) { + for (fl = file_lock_table; fl; fl = fl->fl_nextlink) { inode = fl->fl_file->f_dentry->d_inode; if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 10c66ed4d..73caf6349 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -335,9 +335,12 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); - /* Now add block to block list of the conflicting lock */ - dprintk("lockd: blocking on this lock.\n"); - posix_block_lock(conflock, &block->b_call.a_args.lock.fl); + if (!block->b_call.a_args.lock.fl.fl_prevblock) { + /* Now add block to block list of the conflicting lock + if we haven't done so. */ + dprintk("lockd: blocking on this lock.\n"); + posix_block_lock(conflock, &block->b_call.a_args.lock.fl); + } up(&file->f_sema); return nlm_lck_blocked; @@ -440,7 +443,7 @@ nlmsvc_notify_blocked(struct file_lock *fl) dprintk("lockd: VFS unblock notification for block %p\n", fl); posix_unblock_lock(fl); for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) { - if (&block->b_call.a_args.lock.fl == fl) { + if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) { svc_wake_up(block->b_daemon); nlmsvc_insert_block(block, 0); return; diff --git a/fs/locks.c b/fs/locks.c index 5cb324374..802958a68 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -193,6 +193,14 @@ static void locks_insert_block(struct file_lock *blocker, { struct file_lock *prevblock; + if (waiter->fl_prevblock) { + printk(KERN_ERR "locks_insert_block: remove duplicated lock " + "(pid=%d %ld-%ld type=%d)\n", + waiter->fl_pid, waiter->fl_start, + waiter->fl_end, waiter->fl_type); + locks_delete_block(waiter->fl_prevblock, waiter); + } + if (blocker->fl_prevblock == NULL) /* No previous waiters - list is empty */ prevblock = blocker; @@ -282,7 +290,7 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) /* flock() system call entry point. Apply a FL_FLOCK style lock to * an open file descriptor. */ -asmlinkage int sys_flock(unsigned int fd, unsigned int cmd) +asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) { struct file_lock file_lock; struct file *filp; diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 8c396f3e6..060d5b26c 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -16,6 +16,8 @@ #include <linux/stat.h> #include <linux/kernel.h> #include <linux/string.h> +#include <linux/locks.h> +#include <linux/quotaops.h> #include <asm/bitops.h> @@ -51,8 +53,9 @@ static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, _ return(sum); } -void minix_free_block(struct super_block * sb, int block) +void minix_free_block(struct inode * inode, int block) { + struct super_block * sb = inode->i_sb; struct buffer_head * bh; unsigned int bit,zone; @@ -80,12 +83,15 @@ void minix_free_block(struct super_block * sb, int block) if (!minix_clear_bit(bit,bh->b_data)) printk("free_block (%s:%d): bit already cleared\n", kdevname(sb->s_dev), block); + else + DQUOT_FREE_BLOCK(sb, inode, 1); mark_buffer_dirty(bh, 1); return; } -int minix_new_block(struct super_block * sb) +int minix_new_block(struct inode * inode) { + struct super_block * sb = inode->i_sb; struct buffer_head * bh; int i,j; @@ -94,6 +100,9 @@ int minix_new_block(struct super_block * sb) return 0; } repeat: + if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) + return -EDQUOT; + j = 8192; bh = NULL; for (i = 0; i < sb->u.minix_sb.s_zmap_blocks; i++) { @@ -105,6 +114,7 @@ repeat: return 0; if (minix_set_bit(j,bh->b_data)) { printk("new_block: bit already set"); + DQUOT_FREE_BLOCK(sb, inode, 1); goto repeat; } mark_buffer_dirty(bh, 1); @@ -222,6 +232,10 @@ void minix_free_inode(struct inode * inode) printk("free_inode: nonexistent imap in superblock\n"); return; } + + DQUOT_FREE_INODE(inode->i_sb, inode); + DQUOT_DROP(inode); + bh = inode->i_sb->u.minix_sb.s_imap[ino >> 13]; minix_clear_inode(inode); clear_inode(inode); @@ -230,7 +244,7 @@ void minix_free_inode(struct inode * inode) mark_buffer_dirty(bh, 1); } -struct inode * minix_new_inode(const struct inode * dir) +struct inode * minix_new_inode(const struct inode * dir, int * error) { struct super_block * sb; struct inode * inode; @@ -274,6 +288,20 @@ struct inode * minix_new_inode(const struct inode * dir) inode->i_blocks = inode->i_blksize = 0; insert_inode_hash(inode); mark_inode_dirty(inode); + + unlock_super(sb); +printk("m_n_i: allocated inode "); + if(DQUOT_ALLOC_INODE(sb, inode)) { +printk("fails quota test\n"); + sb->dq_op->drop(inode); + inode->i_nlink = 0; + iput(inode); + *error = -EDQUOT; + return NULL; + } +printk("is within quota\n"); + + *error = 0; return inode; } diff --git a/fs/minix/file.c b/fs/minix/file.c index 9eab1fb17..6683c393c 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -24,6 +24,7 @@ #define MAX(a,b) (((a)>(b))?(a):(b)) #include <linux/fs.h> +#include <linux/minix_fs.h> /* * Write to a file (through the page cache). diff --git a/fs/minix/inode.c b/fs/minix/inode.c index e5352090d..6cc9522fa 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -518,7 +518,7 @@ repeat: } } - tmp = minix_new_block(inode->i_sb); + tmp = minix_new_block(inode); if (!tmp) { *err = -ENOSPC; return NULL; @@ -526,7 +526,7 @@ repeat: if (metadata) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -540,7 +540,7 @@ repeat: * state from under us: */ BUG(); - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); goto repeat; } *phys = tmp; @@ -599,13 +599,13 @@ repeat: } } - tmp = minix_new_block(inode->i_sb); + tmp = minix_new_block(inode); if (!tmp) goto out; if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -617,7 +617,7 @@ repeat: *new = 1; } if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -754,7 +754,7 @@ repeat: } } - tmp = minix_new_block(inode->i_sb); + tmp = minix_new_block(inode); if (!tmp) { *err = -ENOSPC; return NULL; @@ -762,7 +762,7 @@ repeat: if (metadata) { result = getblk(inode->i_dev, tmp, BLOCK_SIZE); if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -776,7 +776,7 @@ repeat: * state from under us: */ BUG(); - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); goto repeat; } *phys = tmp; @@ -835,13 +835,13 @@ repeat: } } - tmp = minix_new_block(inode->i_sb); + tmp = minix_new_block(inode); if (!tmp) goto out; if (metadata) { result = getblk(bh->b_dev, tmp, BLOCK_SIZE); if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -853,7 +853,7 @@ repeat: *new = 1; } if (*p) { - minix_free_block(inode->i_sb, tmp); + minix_free_block(inode, tmp); brelse(result); goto repeat; } @@ -1238,7 +1238,7 @@ static struct file_system_type minix_fs_type = { NULL }; -__initfunc(int init_minix_fs(void)) +int __init init_minix_fs(void) { return register_filesystem(&minix_fs_type); } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index ae5aa8a5a..b268a4676 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -11,6 +11,7 @@ #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/errno.h> +#include <linux/quotaops.h> #include <asm/uaccess.h> @@ -212,7 +213,9 @@ int minix_create(struct inode * dir, struct dentry *dentry, int mode) struct buffer_head * bh; struct minix_dir_entry * de; - inode = minix_new_inode(dir); + inode = minix_new_inode(dir, &error); + if (error) + return error; if (!inode) return -ENOSPC; inode->i_op = &minix_file_inode_operations; @@ -240,7 +243,9 @@ int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) struct buffer_head * bh; struct minix_dir_entry * de; - inode = minix_new_inode(dir); + inode = minix_new_inode(dir, &error); + if (error) + return error; if (!inode) return -ENOSPC; inode->i_uid = current->fsuid; @@ -271,7 +276,9 @@ int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) info = &dir->i_sb->u.minix_sb; if (dir->i_nlink >= info->s_link_max) return -EMLINK; - inode = minix_new_inode(dir); + inode = minix_new_inode(dir, &error); + if (error) + return error; if (!inode) return -ENOSPC; inode->i_op = &minix_dir_inode_operations; @@ -383,6 +390,7 @@ int minix_rmdir(struct inode * dir, struct dentry *dentry) if (!bh) goto end_rmdir; inode = dentry->d_inode; + DQUOT_INIT(inode); if (!empty_dir(inode)) { retval = -ENOTEMPTY; @@ -422,6 +430,7 @@ int minix_unlink(struct inode * dir, struct dentry *dentry) retval = -ENOENT; inode = dentry->d_inode; + DQUOT_INIT(inode); bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh || de->inode != inode->i_ino) @@ -456,7 +465,10 @@ int minix_symlink(struct inode * dir, struct dentry *dentry, int i; char c; - if (!(inode = minix_new_inode(dir))) + inode = minix_new_inode(dir, &i); + if (i) + return i; + if (!inode) return -ENOSPC; inode->i_mode = S_IFLNK | 0777; @@ -554,6 +566,8 @@ int minix_rename(struct inode * old_dir, struct dentry *old_dentry, if (!new_inode) { brelse(new_bh); new_bh = NULL; + } else { + DQUOT_INIT(new_inode); } } if (S_ISDIR(old_inode->i_mode)) { diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 6724d0064..f26aa086c 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -62,11 +62,8 @@ repeat: } *p = 0; mark_inode_dirty(inode); - if (bh) { - mark_buffer_clean(bh); - brelse(bh); - } - minix_free_block(inode->i_sb,tmp); + bforget(bh); + minix_free_block(inode,tmp); } return retry; } @@ -113,8 +110,8 @@ repeat: } *ind = 0; mark_buffer_dirty(ind_bh, 1); - brelse(bh); - minix_free_block(inode->i_sb,tmp); + bforget(bh); + minix_free_block(inode,tmp); } ind = (unsigned short *) ind_bh->b_data; for (i = 0; i < 512; i++) @@ -126,7 +123,7 @@ repeat: else { tmp = *p; *p = 0; - minix_free_block(inode->i_sb,tmp); + minix_free_block(inode,tmp); } } brelse(ind_bh); @@ -172,7 +169,7 @@ repeat: tmp = *p; *p = 0; mark_inode_dirty(inode); - minix_free_block(inode->i_sb,tmp); + minix_free_block(inode,tmp); } } brelse(dind_bh); @@ -226,11 +223,8 @@ repeat: } *p = 0; mark_inode_dirty(inode); - if (bh) { - mark_buffer_clean(bh); - brelse(bh); - } - minix_free_block(inode->i_sb,tmp); + bforget(bh); + minix_free_block(inode,tmp); } return retry; } @@ -277,8 +271,8 @@ repeat: } *ind = 0; mark_buffer_dirty(ind_bh, 1); - brelse(bh); - minix_free_block(inode->i_sb,tmp); + bforget(bh); + minix_free_block(inode,tmp); } ind = (unsigned long *) ind_bh->b_data; for (i = 0; i < 256; i++) @@ -290,7 +284,7 @@ repeat: else { tmp = *p; *p = 0; - minix_free_block(inode->i_sb,tmp); + minix_free_block(inode,tmp); } } brelse(ind_bh); @@ -336,7 +330,7 @@ repeat: tmp = *p; *p = 0; mark_inode_dirty(inode); - minix_free_block(inode->i_sb,tmp); + minix_free_block(inode,tmp); } } brelse(dind_bh); @@ -382,7 +376,7 @@ repeat: tmp = *p; *p = 0; mark_inode_dirty(inode); - minix_free_block(inode->i_sb,tmp); + minix_free_block(inode,tmp); } } brelse(tind_bh); @@ -406,7 +400,8 @@ static void V2_minix_truncate(struct inode * inode) (unsigned long *) inode->u.minix_i.u.i2_data + 9); if (!retry) break; - current->counter = 0; + run_task_queue(&tq_disk); + current->policy |= SCHED_YIELD; schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c index b44c669ce..df865b482 100644 --- a/fs/msdos/msdosfs_syms.c +++ b/fs/msdos/msdosfs_syms.c @@ -34,7 +34,7 @@ struct file_system_type msdos_fs_type = { NULL }; -__initfunc(int init_msdos_fs(void)) +int __init init_msdos_fs(void) { return register_filesystem(&msdos_fs_type); } diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index fa5e4aed5..d69aba48d 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -8,7 +8,6 @@ #define __NO_VERSION__ -#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> @@ -26,23 +25,18 @@ /* MS-DOS "device special files" */ static const char *reserved_names[] = { -#ifndef CONFIG_ATARI /* GEMDOS is less stupid */ "CON ","PRN ","NUL ","AUX ", "LPT1 ","LPT2 ","LPT3 ","LPT4 ", "COM1 ","COM2 ","COM3 ","COM4 ", -#endif NULL }; /* Characters that are undesirable in an MS-DOS file name */ static char bad_chars[] = "*?<>|\""; -#ifdef CONFIG_ATARI -/* GEMDOS is less restrictive */ -static char bad_if_strict[] = " "; -#else -static char bad_if_strict[] = "+=,; "; -#endif +static char bad_if_strict_pc[] = "+=,; "; +static char bad_if_strict_atari[] = " "; /* GEMDOS is less restrictive */ +#define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc) /* Must die */ void msdos_put_super(struct super_block *sb) @@ -51,8 +45,8 @@ void msdos_put_super(struct super_block *sb) } /***** Formats an MS-DOS file name. Rejects invalid names. */ -static int msdos_format_name(char conv,const char *name,int len, - char *res,char dotsOK) +static int msdos_format_name(const char *name,int len, + char *res,struct fat_mount_options *opts) /* conv is relaxed/normal/strict, name is proposed name, * len is the length of the proposed name, res is the result name, * dotsOK is if hidden files get dots. @@ -62,23 +56,27 @@ static int msdos_format_name(char conv,const char *name,int len, const char **reserved; unsigned char c; int space; + if (name[0] == '.') { /* dotfile because . and .. already done */ - if (!dotsOK) return -EINVAL; - /* Get rid of dot - test for it elsewhere */ - name++; len--; + if (opts->dotsOK) { + /* Get rid of dot - test for it elsewhere */ + name++; len--; + } + else if (!opts->atari) return -EINVAL; } -#ifndef CONFIG_ATARI - space = 1; /* disallow names that _really_ start with a dot */ -#else - space = 0; /* GEMDOS does not care */ -#endif + /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does + * not care */ + space = !opts->atari; c = 0; for (walk = res; len && walk-res < 8; walk++) { c = *name++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + if (opts->conversion != 'r' && strchr(bad_chars,c)) + return -EINVAL; + if (opts->conversion == 's' && strchr(bad_if_strict(opts),c)) + return -EINVAL; + if (c >= 'A' && c <= 'Z' && opts->conversion == 's') + return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; /* 0xE5 is legal as a first character, but we must substitute 0x05 */ /* because 0xE5 marks deleted files. Yes, DOS really does this. */ @@ -90,7 +88,7 @@ static int msdos_format_name(char conv,const char *name,int len, *walk = (c >= 'a' && c <= 'z') ? c-32 : c; } if (space) return -EINVAL; - if (conv == 's' && len && c != '.') { + if (opts->conversion == 's' && len && c != '.') { c = *name++; len--; if (c != '.') return -EINVAL; @@ -101,26 +99,31 @@ static int msdos_format_name(char conv,const char *name,int len, while (len > 0 && walk-res < MSDOS_NAME) { c = *name++; len--; - if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; - if (conv == 's' && strchr(bad_if_strict,c)) + if (opts->conversion != 'r' && strchr(bad_chars,c)) + return -EINVAL; + if (opts->conversion == 's' && + strchr(bad_if_strict(opts),c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; if (c == '.') { - if (conv == 's') + if (opts->conversion == 's') return -EINVAL; break; } - if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; + if (c >= 'A' && c <= 'Z' && opts->conversion == 's') + return -EINVAL; space = c == ' '; *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; } if (space) return -EINVAL; - if (conv == 's' && len) return -EINVAL; + if (opts->conversion == 's' && len) return -EINVAL; } while (walk-res < MSDOS_NAME) *walk++ = ' '; - for (reserved = reserved_names; *reserved; reserved++) - if (!strncmp(res,*reserved,8)) return -EINVAL; + if (!opts->atari) + /* GEMDOS is less stupid and has no reserved names */ + for (reserved = reserved_names; *reserved; reserved++) + if (!strncmp(res,*reserved,8)) return -EINVAL; return 0; } @@ -133,8 +136,7 @@ static int msdos_find(struct inode *dir,const char *name,int len, char msdos_name[MSDOS_NAME]; dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK; - res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - name,len, msdos_name,dotsOK); + res = msdos_format_name(name,len, msdos_name,&MSDOS_SB(dir->i_sb)->options); if (res < 0) return -ENOENT; res = fat_scan(dir,msdos_name,bh,de,ino); @@ -163,8 +165,7 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr) int error; char msdos_name[MSDOS_NAME]; - error = msdos_format_name(options->name_check, qstr->name, qstr->len, - msdos_name, options->dotsOK); + error = msdos_format_name(qstr->name, qstr->len, msdos_name, options); if (!error) qstr->hash = full_name_hash(msdos_name, MSDOS_NAME); return 0; @@ -180,12 +181,10 @@ static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) int error; char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; - error = msdos_format_name(options->name_check, a->name, a->len, - a_msdos_name, options->dotsOK); + error = msdos_format_name(a->name, a->len, a_msdos_name, options); if (error) goto old_compare; - error = msdos_format_name(options->name_check, b->name, b->len, - b_msdos_name, options->dotsOK); + error = msdos_format_name(b->name, b->len, b_msdos_name, options); if (error) goto old_compare; error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); @@ -288,9 +287,8 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode) int ino,res,is_hid; char msdos_name[MSDOS_NAME]; - res = msdos_format_name(MSDOS_SB(sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name, MSDOS_SB(sb)->options.dotsOK); + res = msdos_format_name(dentry->d_name.name,dentry->d_name.len, + msdos_name, &MSDOS_SB(sb)->options); if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); @@ -363,13 +361,10 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) struct inode *inode; int res,is_hid; char msdos_name[MSDOS_NAME]; - struct buffer_head *bh1; - struct msdos_dir_entry *de1; int ino; - res = msdos_format_name(MSDOS_SB(sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name,MSDOS_SB(sb)->options.dotsOK); + res = msdos_format_name(dentry->d_name.name,dentry->d_name.len, + msdos_name, &MSDOS_SB(sb)->options); if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); @@ -385,41 +380,16 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) fat_brelse(sb, bh); goto out_unlock; } - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); res = 0; dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ - if (!(bh1 = fat_add_cluster1(inode))) { - res = -ENOSPC; + res = fat_new_dir(inode, dir, 0); + if (res) goto mkdir_error; - } - fat_brelse(sb, bh); - de1 = (struct msdos_dir_entry *)bh1->b_data; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty(inode); - memcpy(de1->name,MSDOS_DOT,MSDOS_NAME); - de1->attr = ATTR_DIR; - de1->start = CT_LE_W(MSDOS_I(inode)->i_logstart); - de1->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16); - fat_date_unix2dos(inode->i_mtime,&de1->time,&de1->date); - de1->size = 0; - de1->time = CT_LE_W(de1->time); - de1->date = CT_LE_W(de1->date); - de1++; - memcpy(de1->name,MSDOS_DOTDOT,MSDOS_NAME); - de1->attr = ATTR_DIR; - de1->start = CT_LE_W(MSDOS_I(dir)->i_logstart); - de1->starthi = CT_LE_W(MSDOS_I(dir)->i_logstart >> 16); - fat_date_unix2dos(dir->i_mtime,&de1->time,&de1->date); - de1->size = 0; - de1->time = CT_LE_W(de1->time); - de1->date = CT_LE_W(de1->date); - fat_mark_buffer_dirty(sb, bh1, 1); - fat_brelse(sb, bh1); + fat_brelse(sb, bh); d_instantiate(dentry, inode); res = 0; @@ -586,14 +556,14 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, int is_hid,old_hid; /* if new file and old file are hidden */ char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; - error = msdos_format_name(MSDOS_SB(sb)->options.name_check, - old_dentry->d_name.name, old_dentry->d_name.len, - old_msdos_name,MSDOS_SB(sb)->options.dotsOK); + error = msdos_format_name(old_dentry->d_name.name, + old_dentry->d_name.len,old_msdos_name, + &MSDOS_SB(old_dir->i_sb)->options); if (error < 0) goto rename_done; - error = msdos_format_name(MSDOS_SB(sb)->options.name_check, - new_dentry->d_name.name, new_dentry->d_name.len, - new_msdos_name,MSDOS_SB(sb)->options.dotsOK); + error = msdos_format_name(new_dentry->d_name.name, + new_dentry->d_name.len,new_msdos_name, + &MSDOS_SB(new_dir->i_sb)->options); if (error < 0) goto rename_done; diff --git a/fs/namei.c b/fs/namei.c index e39cd24e1..9191689b2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -250,7 +250,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, i * FIXME! This could use version numbering or similar to * avoid unnecessary cache lookups. */ - result = cached_lookup(parent, name, flags); + result = d_lookup(parent, name); if (!result) { struct dentry * dentry = d_alloc(parent, name); result = ERR_PTR(-ENOMEM); @@ -261,8 +261,19 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, i else result = dentry; } + up(&dir->i_sem); + return result; } + + /* + * Uhhuh! Nasty case: the cache was re-populated while + * we waited on the semaphore. Need to revalidate, but + * we're going to return this entry regardless (same + * as if it was busy). + */ up(&dir->i_sem); + if (result->d_op && result->d_op->d_revalidate) + result->d_op->d_revalidate(result, flags); return result; } @@ -838,7 +849,7 @@ exit_lock: return retval; } -asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) +asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev) { int error; char * tmp; @@ -929,7 +940,7 @@ exit: return error; } -asmlinkage int sys_mkdir(const char * pathname, int mode) +asmlinkage long sys_mkdir(const char * pathname, int mode) { int error; char * tmp; @@ -1024,7 +1035,7 @@ exit: return error; } -asmlinkage int sys_rmdir(const char * pathname) +asmlinkage long sys_rmdir(const char * pathname) { int error; char * tmp; @@ -1077,7 +1088,7 @@ exit: return error; } -asmlinkage int sys_unlink(const char * pathname) +asmlinkage long sys_unlink(const char * pathname) { int error; char * tmp; @@ -1128,7 +1139,7 @@ exit: return error; } -asmlinkage int sys_symlink(const char * oldname, const char * newname) +asmlinkage long sys_symlink(const char * oldname, const char * newname) { int error; char * from; @@ -1216,7 +1227,7 @@ exit: return error; } -asmlinkage int sys_link(const char * oldname, const char * newname) +asmlinkage long sys_link(const char * oldname, const char * newname) { int error; char * from; @@ -1387,7 +1398,7 @@ exit: return error; } -asmlinkage int sys_rename(const char * oldname, const char * newname) +asmlinkage long sys_rename(const char * oldname, const char * newname) { int error; char * from; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index cf4c13fde..ee2886228 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -349,6 +349,9 @@ ncp_lookup_validate(struct dentry * dentry, int flags) int len = dentry->d_name.len; struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; + + if (!dentry->d_inode || !dir) + return 0; server = NCP_SERVER(dir); diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 36f37c232..88d56f7b2 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -134,7 +134,15 @@ void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo) if ((inode->i_size)&&(inode->i_blksize)) { inode->i_blocks = (inode->i_size-1)/(inode->i_blksize)+1; } + /* TODO: times? I'm not sure... */ + inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwinfo->i.modifyTime), + le16_to_cpu(nwinfo->i.modifyDate)); + inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwinfo->i.creationTime), + le16_to_cpu(nwinfo->i.creationDate)); + inode->i_atime = ncp_date_dos2unix(0, + le16_to_cpu(nwinfo->i.lastAccessDate)); + NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber; @@ -712,7 +720,7 @@ static struct file_system_type ncp_fs_type = { NULL }; -__initfunc(int init_ncp_fs(void)) +int __init init_ncp_fs(void) { return register_filesystem(&ncp_fs_type); } diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 0a293ca85..81ac20840 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -172,8 +172,11 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size, re_select: wait_table.nr = 0; wait_table.entry = &entry; - current->state = TASK_INTERRUPTIBLE; - if (!(file->f_op->poll(file, &wait_table) & POLLIN)) { + /* mb() is not necessary because ->poll() will serialize + instructions adding the wait_table waitqueues in the + waitqueue-head before going to calculate the mask-retval. */ + __set_current_state(TASK_INTERRUPTIBLE); + if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) { int timed_out; if (timeout > max_timeout) { /* JEJB/JSP 2/7/94 diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 31d76e5c6..6b52b2d54 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -601,6 +601,8 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) /* Filehandle matches? */ if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { + if (!list_empty(&dentry->d_subdirs)) + shrink_dcache_parent(dentry); if (dentry->d_count < 2) goto out_bad; } diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 65d3f76a2..d9a423f16 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -154,7 +154,7 @@ static struct nfs_bool_opts { * need to have root_server_addr set _before_ IPConfig gets called as it * can override it. */ -__initfunc(static void root_nfs_parse_addr(char *name)) +static void __init root_nfs_parse_addr(char *name) { int octets = 0; char *cp, *cq; @@ -183,7 +183,7 @@ __initfunc(static void root_nfs_parse_addr(char *name)) /* * Parse option string. */ -__initfunc(static void root_nfs_parse(char *name, char *buf)) +static void __init root_nfs_parse(char *name, char *buf) { char *options, *val, *cp; @@ -220,7 +220,7 @@ __initfunc(static void root_nfs_parse(char *name, char *buf)) /* * Prepare the NFS data structure and parse all options. */ -__initfunc(static int root_nfs_name(char *name)) +static int __init root_nfs_name(char *name) { char buf[NFS_MAXPATHLEN]; char *cp; @@ -261,7 +261,7 @@ __initfunc(static int root_nfs_name(char *name)) /* * Get NFS server address. */ -__initfunc(static int root_nfs_addr(void)) +static int __init root_nfs_addr(void) { if ((servaddr = root_server_addr) == INADDR_NONE) { printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n"); @@ -277,7 +277,7 @@ __initfunc(static int root_nfs_addr(void)) * Tell the user what's going on. */ #ifdef NFSROOT_DEBUG -__initfunc(static void root_nfs_print(void)) +static void __init root_nfs_print(void) { printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", nfs_path, nfs_data.hostname); @@ -292,7 +292,7 @@ __initfunc(static void root_nfs_print(void)) #endif -__initfunc(int root_nfs_init(void)) +int __init root_nfs_init(void) { #ifdef NFSROOT_DEBUG nfs_debug |= NFSDBG_ROOT; @@ -320,7 +320,7 @@ __initfunc(int root_nfs_init(void)) * Parse NFS server and directory information passed on the kernel * command line. */ -__initfunc(void nfs_root_setup(char *line, int *ints)) +void __init nfs_root_setup(char *line) { ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { @@ -335,6 +335,7 @@ __initfunc(void nfs_root_setup(char *line, int *ints)) root_nfs_parse_addr(nfs_root_name); } +__setup("nfsroot=", nfs_root_setup); /*************************************************************************** @@ -356,7 +357,7 @@ set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port) /* * Query server portmapper for the port of a daemon program. */ -__initfunc(static int root_nfs_getport(int program, int version)) +static int __init root_nfs_getport(int program, int version) { struct sockaddr_in sin; @@ -372,7 +373,7 @@ __initfunc(static int root_nfs_getport(int program, int version)) * by the user. Use defaults if portmapper is not available. * XXX: Is there any nfs server with no portmapper? */ -__initfunc(static int root_nfs_ports(void)) +static int __init root_nfs_ports(void) { int port; @@ -403,7 +404,7 @@ __initfunc(static int root_nfs_ports(void)) * Get a file handle from the server for the directory which is to be * mounted. */ -__initfunc(static int root_nfs_get_handle(void)) +static int __init root_nfs_get_handle(void) { struct sockaddr_in sin; int status; @@ -421,7 +422,7 @@ __initfunc(static int root_nfs_get_handle(void)) /* * Now actually mount the given directory. */ -__initfunc(static int root_nfs_do_mount(struct super_block *sb)) +static int __init root_nfs_do_mount(struct super_block *sb) { /* Pass the server address to NFS */ set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port); @@ -437,7 +438,7 @@ __initfunc(static int root_nfs_do_mount(struct super_block *sb)) * Get the NFS port numbers and file handle, and then read the super- * block for mounting. */ -__initfunc(int nfs_root_mount(struct super_block *sb)) +int __init nfs_root_mount(struct super_block *sb) { if (root_nfs_init() < 0 || root_nfs_ports() < 0 diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 899b62c82..a7960c1e0 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -152,7 +152,7 @@ nfs_readpage_result(struct rpc_task *task) fail++; dprintk("NFS: %d successful reads, %d failures\n", succ, fail); } - page->owner = (int)current; // HACK, FIXME, will go away. + page->owner = current; // HACK, FIXME, will go away. UnlockPage(page); free_page(address); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 77c1db091..561e8450c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -392,7 +392,7 @@ wait_on_write_request(struct nfs_wreq *req) rpc_clnt_sigmask(clnt, &oldmask); add_wait_queue(&req->wb_wait, &wait); for (;;) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); retval = 0; if (req->wb_flags & NFS_WRITE_COMPLETE) break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 14cb7d50f..999d90d25 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -995,12 +995,15 @@ out_nfserr: * that the parent is still our parent and * that we are still hashed onto it.. * - * This is requied in case two processes race + * This is required in case two processes race * on removing (or moving) the same entry: the * parent lock will serialize them, but the * other process will be too late.. + * + * Note that this nfsd_check_parent is different + * than the one in linux/include/dcache_func.h. */ -#define check_parent(dir, dentry) \ +#define nfsd_check_parent(dir, dentry) \ ((dir) == (dentry)->d_parent->d_inode && !list_empty(&dentry->d_hash)) /* @@ -1079,8 +1082,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, nfsd_double_down(&tdir->i_sem, &fdir->i_sem); err = -ENOENT; /* GAM3 check for parent changes after locking. */ - if (check_parent(fdir, odentry) && - check_parent(tdir, ndentry)) { + if (nfsd_check_parent(fdir, odentry) && + nfsd_check_parent(tdir, ndentry)) { err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { @@ -1168,7 +1171,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, fhp->fh_locked = 1; err = -ENOENT; - if (check_parent(dirp, rdentry)) + if (nfsd_check_parent(dirp, rdentry)) err = vfs_rmdir(dirp, rdentry); rdentry->d_count--; diff --git a/fs/nls/Config.in b/fs/nls/Config.in index 7e591b82a..7d86a4091 100644 --- a/fs/nls/Config.in +++ b/fs/nls/Config.in @@ -38,6 +38,7 @@ if [ "$CONFIG_NLS" = "y" ]; then tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7 tristate 'NLS ISO 8859-8 (Hebrew)' CONFIG_NLS_ISO8859_8 tristate 'NLS ISO 8859-9 (Latin 5; Turkish)' CONFIG_NLS_ISO8859_9 + tristate 'NLS ISO 8859-14 (Latin 8; Celtic)' CONFIG_NLS_ISO8859_14 tristate 'NLS ISO 8859-15 (Latin 9; Western European Languages with Euro)' CONFIG_NLS_ISO8859_15 tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R endmenu diff --git a/fs/nls/Makefile b/fs/nls/Makefile index 879fef8ff..3d7688f0d 100644 --- a/fs/nls/Makefile +++ b/fs/nls/Makefile @@ -286,6 +286,14 @@ else endif endif +ifeq ($(CONFIG_NLS_ISO8859_14),y) +NLS += nls_iso8859-14.o +else + ifeq ($(CONFIG_NLS_ISO8859_14),m) + M_OBJS += nls_iso8859-14.o + endif +endif + ifeq ($(CONFIG_NLS_ISO8859_15),y) NLS += nls_iso8859-15.o else diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index b6483a1d6..40d5d6c2c 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -429,6 +429,9 @@ int init_nls(void) #ifdef CONFIG_NLS_ISO8859_9 init_nls_iso8859_9(); #endif +#ifdef CONFIG_NLS_ISO8859_14 + init_nls_iso8859_14(); +#endif #ifdef CONFIG_NLS_ISO8859_15 init_nls_iso8859_15(); #endif diff --git a/fs/nls/nls_iso8859-14.c b/fs/nls/nls_iso8859-14.c new file mode 100644 index 000000000..b466a6560 --- /dev/null +++ b/fs/nls/nls_iso8859-14.c @@ -0,0 +1,275 @@ +/* + * linux/fs/nls_iso8859-14.c + * + * Charset iso8859-14 translation tables. + * + * Generated automatically from the Unicode and charset table + * provided by the Unicode Organisation at + * http://www.unicode.org/ + * The Unicode to charset table has only exact mappings. + * + * Rhys Jones, Swansea University Computer Society + * rhys@sucs.swan.ac.uk + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/nls.h> + +static struct nls_unicode charset2uni[256] = { + /* 0x00*/ + {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, + {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00}, + {0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00}, + {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, + /* 0x10*/ + {0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, + {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, + {0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00}, + {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00}, + /* 0x20*/ + {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00}, + {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, + {0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00}, + {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00}, + /* 0x30*/ + {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, + {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00}, + {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00}, + {0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00}, + /* 0x40*/ + {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, + {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00}, + {0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, + {0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00}, + /* 0x50*/ + {0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00}, + {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00}, + {0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00}, + {0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00}, + /* 0x60*/ + {0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00}, + {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00}, + {0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00}, + {0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00}, + /* 0x70*/ + {0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00}, + {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00}, + {0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00}, + {0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00}, + /* 0x80*/ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + /* 0x90*/ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, + /* 0xa0*/ + {0xa0, 0x00}, {0x02, 0x1e}, {0x03, 0x1e}, {0xa3, 0x00}, + {0x0a, 0x01}, {0x0b, 0x01}, {0x0a, 0x1e}, {0xa7, 0x00}, + {0x80, 0x1e}, {0xa9, 0x00}, {0x82, 0x1e}, {0x0b, 0x1e}, + {0xf2, 0x1e}, {0xad, 0x00}, {0xae, 0x00}, {0x78, 0x01}, + /* 0xb0*/ + {0x1e, 0x1e}, {0x1f, 0x1e}, {0x20, 0x01}, {0x21, 0x01}, + {0x40, 0x1e}, {0x41, 0x1e}, {0xb6, 0x00}, {0x56, 0x1e}, + {0x81, 0x1e}, {0x57, 0x1e}, {0x83, 0x1e}, {0x60, 0x1e}, + {0xf3, 0x1e}, {0x84, 0x1e}, {0x85, 0x1e}, {0x61, 0x1e}, + /* 0xc0*/ + {0xc0, 0x00}, {0xc1, 0x00}, {0xc2, 0x00}, {0xc3, 0x00}, + {0xc4, 0x00}, {0xc5, 0x00}, {0xc6, 0x00}, {0xc7, 0x00}, + {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x00}, {0xcb, 0x00}, + {0xcc, 0x00}, {0xcd, 0x00}, {0xce, 0x00}, {0xcf, 0x00}, + /* 0xd0*/ + {0x74, 0x01}, {0xd1, 0x00}, {0xd2, 0x00}, {0xd3, 0x00}, + {0xd4, 0x00}, {0xd5, 0x00}, {0xd6, 0x00}, {0x6a, 0x1e}, + {0xd8, 0x00}, {0xd9, 0x00}, {0xda, 0x00}, {0xdb, 0x00}, + {0xdc, 0x00}, {0xdd, 0x00}, {0x76, 0x01}, {0xdf, 0x00}, + /* 0xe0*/ + {0xe0, 0x00}, {0xe1, 0x00}, {0xe2, 0x00}, {0xe3, 0x00}, + {0xe4, 0x00}, {0xe5, 0x00}, {0xe6, 0x00}, {0xe7, 0x00}, + {0xe8, 0x00}, {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00}, + {0xec, 0x00}, {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00}, + /* 0xf0*/ + {0x75, 0x01}, {0xf1, 0x00}, {0xf2, 0x00}, {0xf3, 0x00}, + {0xf4, 0x00}, {0xf5, 0x00}, {0xf6, 0x00}, {0x6b, 0x1e}, + {0xf8, 0x00}, {0xf9, 0x00}, {0xfa, 0x00}, {0xfb, 0x00}, + {0xfc, 0x00}, {0xfd, 0x00}, {0x77, 0x01}, {0xff, 0x00}, +}; + +static unsigned char page00[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0xa0, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */ + 0x00, 0xa9, 0x00, 0x00, 0x00, 0xad, 0xae, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ + 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, /* 0xd0-0xd7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */ + 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0x00, /* 0xf0-0xf7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x00, 0xff, /* 0xf8-0xff */ +}; + +static unsigned char page01[256] = { + 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */ + 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */ + 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */ + 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */ + 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */ + 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */ + 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */ + 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */ + + 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ + 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + +static unsigned char page1e[256] = { + 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */ + 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */ + 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */ + 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */ + 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */ + 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */ + 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */ + 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */ + + 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ + 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + +static unsigned char *page_uni2charset[256] = { + page00, page01, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, page1e, NULL, + + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +static void inc_use_count(void) +{ + MOD_INC_USE_COUNT; +} + +static void dec_use_count(void) +{ + MOD_DEC_USE_COUNT; +} + +static struct nls_table table = { + "iso8859-14", + page_uni2charset, + charset2uni, + inc_use_count, + dec_use_count, + NULL +}; + +int init_nls_iso8859_14(void) +{ + return register_nls(&table); +} + +#ifdef MODULE +int init_module(void) +{ + return init_nls_iso8859_14(); +} + + +void cleanup_module(void) +{ + unregister_nls(&table); + return; +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff --git a/fs/noquot.c b/fs/noquot.c index cd511fd36..bc8755ed6 100644 --- a/fs/noquot.c +++ b/fs/noquot.c @@ -9,7 +9,7 @@ int nr_dquots = 0, nr_free_dquots = 0; int max_dquots = 0; -asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) +asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr) { return(-ENOSYS); } diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index cc6bfe2e3..4fe6df055 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -1028,10 +1028,10 @@ static struct file_system_type ntfs_fs_type = { /* When this code is not compiled as a module, this is the main entry point, * called by do_sys_setup() in fs/filesystems.c * - * NOTE : __initfunc() is a macro used to remove this function from memory + * NOTE : __init is a macro used to remove this function from memory * once initialization is done */ -__initfunc(int init_ntfs_fs(void)) +int __init init_ntfs_fs(void) { /* Comment this if you trust klogd. There are reasons not to trust it */ @@ -12,7 +12,7 @@ #include <asm/uaccess.h> -asmlinkage int sys_statfs(const char * path, struct statfs * buf) +asmlinkage long sys_statfs(const char * path, struct statfs * buf) { struct dentry * dentry; int error; @@ -34,7 +34,7 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf) return error; } -asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) +asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf) { struct file * file; struct super_block * sb; @@ -79,7 +79,7 @@ int do_truncate(struct dentry *dentry, unsigned long length) return error; } -asmlinkage int sys_truncate(const char * path, unsigned long length) +asmlinkage long sys_truncate(const char * path, unsigned long length) { struct dentry * dentry; struct inode * inode; @@ -128,7 +128,7 @@ out: return error; } -asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) +asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length) { struct inode * inode; struct dentry *dentry; @@ -176,7 +176,7 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -asmlinkage int sys_utime(char * filename, struct utimbuf * times) +asmlinkage long sys_utime(char * filename, struct utimbuf * times) { int error; struct dentry * dentry; @@ -224,7 +224,7 @@ out: * 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) +asmlinkage long sys_utimes(char * filename, struct timeval * utimes) { int error; struct dentry * dentry; @@ -270,7 +270,7 @@ out: * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ -asmlinkage int sys_access(const char * filename, int mode) +asmlinkage long sys_access(const char * filename, int mode) { struct dentry * dentry; int old_fsuid, old_fsgid; @@ -311,7 +311,7 @@ out: return res; } -asmlinkage int sys_chdir(const char * filename) +asmlinkage long sys_chdir(const char * filename) { int error; struct inode *inode; @@ -346,7 +346,7 @@ out: return error; } -asmlinkage int sys_fchdir(unsigned int fd) +asmlinkage long sys_fchdir(unsigned int fd) { struct file *file; struct dentry *dentry; @@ -382,7 +382,7 @@ out: return error; } -asmlinkage int sys_chroot(const char * filename) +asmlinkage long sys_chroot(const char * filename) { int error; struct inode *inode; @@ -422,7 +422,7 @@ out: return error; } -asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) +asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) { struct inode * inode; struct dentry * dentry; @@ -460,7 +460,7 @@ out: return err; } -asmlinkage int sys_chmod(const char * filename, mode_t mode) +asmlinkage long sys_chmod(const char * filename, mode_t mode) { struct dentry * dentry; struct inode * inode; @@ -551,7 +551,7 @@ out: return error; } -asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) +asmlinkage long sys_chown(const char * filename, uid_t user, gid_t group) { struct dentry * dentry; int error; @@ -568,7 +568,7 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) return error; } -asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group) +asmlinkage long sys_lchown(const char * filename, uid_t user, gid_t group) { struct dentry * dentry; int error; @@ -586,7 +586,7 @@ asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group) } -asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) +asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) { struct dentry * dentry; struct file * file; @@ -685,10 +685,14 @@ int get_unused_fd(void) struct files_struct * files = current->files; int fd, error; - error = -EMFILE; - + error = -EMFILE; write_lock(&files->file_lock); - fd = find_first_zero_bit(&files->open_fds, NR_OPEN); + +repeat: + fd = find_next_zero_bit(files->open_fds, + current->files->max_fdset, + files->next_fd); + /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. @@ -696,10 +700,31 @@ int get_unused_fd(void) if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur) goto out; - /* Check here for fd > files->max_fds to do dynamic expansion */ + /* Do we need to expand the fdset array? */ + if (fd >= current->files->max_fdset) { + error = expand_fdset(files, 0); + if (!error) { + error = -EMFILE; + goto repeat; + } + goto out; + } + + /* + * Check whether we need to expand the fd array. + */ + if (fd >= files->max_fds) { + error = expand_fd_array(files, 0); + if (!error) { + error = -EMFILE; + goto repeat; + } + goto out; + } - FD_SET(fd, &files->open_fds); - FD_CLR(fd, &files->close_on_exec); + FD_SET(fd, files->open_fds); + FD_CLR(fd, files->close_on_exec); + files->next_fd = fd + 1; #if 1 /* Sanity check */ if (files->fd[fd] != NULL) { @@ -717,11 +742,13 @@ out: inline void put_unused_fd(unsigned int fd) { write_lock(¤t->files->file_lock); - FD_CLR(fd, ¤t->files->open_fds); + FD_CLR(fd, current->files->open_fds); + if (fd < current->files->next_fd) + current->files->next_fd = fd; write_unlock(¤t->files->file_lock); } -asmlinkage int sys_open(const char * filename, int flags, int mode) +asmlinkage long sys_open(const char * filename, int flags, int mode) { char * tmp; int fd, error; @@ -757,7 +784,7 @@ out_error: * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage int sys_creat(const char * pathname, int mode) +asmlinkage long sys_creat(const char * pathname, int mode) { return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } @@ -765,22 +792,6 @@ asmlinkage int sys_creat(const char * pathname, int mode) #endif /* - * Called when retiring the last use of a file pointer. - */ -void __fput(struct file *filp) -{ - struct dentry * dentry = filp->f_dentry; - struct inode * inode = dentry->d_inode; - - if (filp->f_op && filp->f_op->release) - filp->f_op->release(inode, filp); - filp->f_dentry = NULL; - if (filp->f_mode & FMODE_WRITE) - put_write_access(inode); - dput(dentry); -} - -/* * "id" is the POSIX thread ID. We use the * files pointer for this.. */ @@ -806,8 +817,12 @@ int filp_close(struct file *filp, fl_owner_t id) * Careful here! We test whether the file pointer is NULL before * releasing the fd. This ensures that one clone task can't release * an fd while another clone is opening it. + * + * The "release" argument tells us whether or not to mark the fd as free + * or not in the open-files bitmap. dup2 uses this to retain the fd + * without races. */ -asmlinkage int sys_close(unsigned int fd) +int do_close(unsigned int fd, int release) { int error; struct file * filp; @@ -818,9 +833,10 @@ asmlinkage int sys_close(unsigned int fd) filp = frip(fd); if (!filp) goto out_unlock; - FD_CLR(fd, &files->close_on_exec); + FD_CLR(fd, files->close_on_exec); write_unlock(&files->file_lock); - put_unused_fd(fd); + if (release) + put_unused_fd(fd); lock_kernel(); error = filp_close(filp, files); unlock_kernel(); @@ -831,11 +847,16 @@ out_unlock: goto out; } +asmlinkage long sys_close(unsigned int fd) +{ + return do_close(fd, 1); +} + /* * This routine simulates a hangup on the tty, to arrange that users * are given clean terminals at login time. */ -asmlinkage int sys_vhangup(void) +asmlinkage long sys_vhangup(void) { int ret = -EPERM; diff --git a/fs/partitions/.cvsignore b/fs/partitions/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/partitions/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/partitions/Config.in b/fs/partitions/Config.in new file mode 100644 index 000000000..31e5884f4 --- /dev/null +++ b/fs/partitions/Config.in @@ -0,0 +1,67 @@ +# +# Partition configuration +# +bool 'Advanced partition selection' CONFIG_PARTITION_ADVANCED +if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then + bool 'Alpha OSF partition support' CONFIG_OSF_PARTITION + bool 'Macintosh partition map support' CONFIG_MAC_PARTITION + bool 'PC BIOS (MSDOS partition tables) support' CONFIG_MSDOS_PARTITION +else + if [ "$ARCH" = "alpha" ]; then + define_bool CONFIG_OSF_PARTITION y + fi + if [ "$ARCH" = "ppc" -o "$CONFIG_MAC" = "y" ]; then + define_bool CONFIG_MAC_PARTITION y + fi + if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ + "$CONFIG_MAC" != "y" ]; then + define_bool CONFIG_MSDOS_PARTITION y + fi +fi +if [ "$CONFIG_MSDOS_PARTITION" = "y" ]; then + bool ' BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL + bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION + bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL +fi +if [ "$CONFIG_SGI_IP22" != "y" ]; then + bool 'SGI partition support' CONFIG_SGI_PARTITION +else + define_bool CONFIG_SGI_PARTITION y +fi +if [ "$CONFIG_DECSTATION" != "y" ]; then + bool 'Ultrix partition table support' CONFIG_ULTRIX_PARTITION +else + define_bool CONFIG_ULTRIX_PARTITION y +fi +if [ "$ARCH" != "sparc" -a "$ARCH" != "sparc64" ]; then + bool 'Sun partition tables support' CONFIG_SUN_PARTITION +else + define_bool CONFIG_SUN_PARTITION y +fi +if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then + bool 'Amiga partition table support' CONFIG_AMIGA_PARTITION + bool 'Atari partition table support' CONFIG_ATARI_PARTITION + bool 'Acorn partition support' CONFIG_ACORN_PARTITION + if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then + bool ' Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS +# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA + bool ' ICS partition support' CONFIG_ACORN_PARTITION_ICS + bool ' PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC + bool ' RISCiX partition support' CONFIG_ACORN_PARTITION_RISCIX + fi +else + if [ "$CONFIG_AMIGA" = "y" ]; then + define_bool CONFIG_AMIGA_PARTITION y + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + define_bool CONFIG_ACORN_PARTITION y + define_bool CONFIG_ACORN_PARTITION_ADFS y +# define_bool CONFIG_ACORN_PARTITION_CUMANA y + define_bool CONFIG_ACORN_PARTITION_ICS y + define_bool CONFIG_ACORN_PARTITION_POWERTEC y + define_bool CONFIG_ACORN_PARTITION_RISCIX y + fi +fi +if [ "$CONFIG_ATARI" = "y" ]; then + define_bool CONFIG_ATARI_PARTITION y +fi diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile new file mode 100644 index 000000000..2dc58c20d --- /dev/null +++ b/fs/partitions/Makefile @@ -0,0 +1,49 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := partitions.o +O_OBJS := check.o + +ifeq ($(CONFIG_ACORN_PARTITION),y) +O_OBJS += acorn.o +endif + +ifeq ($(CONFIG_AMIGA_PARTITION),y) +O_OBJS += amiga.o +endif + +ifeq ($(CONFIG_ATARI_PARTITION),y) +O_OBJS += atari.o +endif + +ifeq ($(CONFIG_MAC_PARTITION),y) +O_OBJS += mac.o +endif + +ifeq ($(CONFIG_MSDOS_PARTITION),y) +O_OBJS += msdos.o +endif + +ifeq ($(CONFIG_OSF_PARTITION),y) +O_OBJS += osf.o +endif + +ifeq ($(CONFIG_SGI_PARTITION),y) +O_OBJS += sgi.o +endif + +ifeq ($(CONFIG_SUN_PARTITION),y) +O_OBJS += sun.o +endif + +ifeq ($(CONFIG_ULTRIX_PARTITION),y) +O_OBJS += ultrix.o +endif + +include $(TOPDIR)/Rules.make diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c new file mode 100644 index 000000000..bce1d34aa --- /dev/null +++ b/fs/partitions/acorn.c @@ -0,0 +1,472 @@ +/* + * linux/arch/arm/drivers/block/adfspart.c + * + * Copyright (c) 1996,1997 Russell King. + * + * Scan ADFS partitions on hard disk drives. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/genhd.h> +#include <linux/fs.h> + +#include "acorn.h" + +extern void add_gd_partition(struct gendisk *hd, unsigned int minor, unsigned int start, unsigned int size); + +static void +adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads, + unsigned long totalblocks) +{ + extern void xd_set_geometry(kdev_t dev, unsigned char, unsigned char, + unsigned long, unsigned int); + +#ifdef CONFIG_BLK_DEV_MFM + if (MAJOR(dev) == MFM_ACORN_MAJOR) + xd_set_geometry(dev, secspertrack, heads, totalblocks, 1); +#endif +} + +#define LINUX_NATIVE_MAGIC 0xdeafa1de +#define LINUX_SWAP_MAGIC 0xdeafab1e + +struct linux_part { + unsigned long magic; + unsigned long start_sect; + unsigned long nr_sects; +}; + +static struct disc_record *adfs_partition(struct gendisk *hd, char *name, char *data, + unsigned long first_sector, unsigned int minor) +{ + struct disc_record *dr; + unsigned int nr_sects; + + if (adfs_checkbblk(data)) + return NULL; + + dr = (struct disc_record *)(data + 0x1c0); + + if (dr->disc_size == 0 && dr->disc_size_high == 0) + return NULL; + + nr_sects = (dr->disc_size_high << 23) | (dr->disc_size >> 9); + + if (name) + printk(" [%s]", name); + add_gd_partition(hd, minor, first_sector, nr_sects); + return dr; +} + +#ifdef CONFIG_ACORN_PARTITION_RISCIX +static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect, + unsigned int minor, unsigned long nr_sects) +{ + struct buffer_head *bh; + struct riscix_record *rr; + unsigned int riscix_minor; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + printk(" [RISCiX]"); + + add_gd_partition(hd, riscix_minor = minor++, first_sect, nr_sects); + hd->sizes[riscix_minor] = hd->part[riscix_minor].nr_sects >> (BLOCK_SIZE_BITS - 9); + dev = MKDEV(hd->major, riscix_minor); + + if (!(bh = bread(dev, 0, 1024))) + return -1; + + rr = (struct riscix_record *)bh->b_data; + if (rr->magic == RISCIX_MAGIC) { + int part; + + printk(" <"); + + for (part = 0; part < 8; part++) { + if (rr->part[part].one && + memcmp(rr->part[part].name, "All\0", 4)) { + add_gd_partition(hd, minor++, + rr->part[part].start, + rr->part[part].length); + printk("(%s)", rr->part[part].name); + } + } + + printk(" >"); + + if (hd->part[riscix_minor].nr_sects > 2) + hd->part[riscix_minor].nr_sects = 2; + } + + brelse(bh); + return minor; +} +#endif + +static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sect, + unsigned int minor, unsigned long nr_sects) +{ + struct buffer_head *bh; + struct linux_part *linuxp; + unsigned int linux_minor, mask = (1 << hd->minor_shift) - 1; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + printk(" [Linux]"); + + add_gd_partition(hd, linux_minor = minor++, first_sect, nr_sects); + hd->sizes[linux_minor] = hd->part[linux_minor].nr_sects >> (BLOCK_SIZE_BITS - 9); + dev = MKDEV(hd->major, linux_minor); + + if (!(bh = bread(dev, 0, 1024))) + return -1; + + linuxp = (struct linux_part *)bh->b_data; + printk(" <"); + while (linuxp->magic == LINUX_NATIVE_MAGIC || linuxp->magic == LINUX_SWAP_MAGIC) { + if (!(minor & mask)) + break; + add_gd_partition(hd, minor++, first_sect + linuxp->start_sect, + linuxp->nr_sects); + linuxp ++; + } + printk(" >"); + /* + * Prevent someone doing a mkswap or mkfs on this partition + */ + if(hd->part[linux_minor].nr_sects > 2) + hd->part[linux_minor].nr_sects = 2; + + brelse(bh); + return minor; +} + +#ifdef CONFIG_ACORN_PARTITION_CUMANA +static int adfspart_check_CUMANA(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + unsigned int minor) +{ + unsigned int start_blk = 0, mask = (1 << hd->minor_shift) - 1; + struct buffer_head *bh = NULL; + char *name = "CUMANA/ADFS"; + int first = 1; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + /* + * Try Cumana style partitions - sector 3 contains ADFS boot block with pointer + * to next 'drive'. + * + * There are unknowns in this code - is the 'cylinder number' of the next + * partition relative to the start of this one - I'm assuming it is. + * + * Also, which ID did Cumana use? + * + * This is totally unfinished, and will require more work to get it going. + * Hence it is totally untested. + */ + do { + struct disc_record *dr; + unsigned int nr_sects; + + if (!(minor & mask)) + break; + + if (!(bh = bread(dev, start_blk + 3, 1024))) + return -1; + + dr = adfs_partition(hd, name, bh->b_data, first_sector, minor++); + if (!dr) + break; + name = NULL; + + nr_sects = (bh->b_data[0x1fd] + (bh->b_data[0x1fe] << 8)) * + (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) * dr->secspertrack; + + if (!nr_sects) + break; + + first = 0; + first_sector += nr_sects; + start_blk += nr_sects >> (BLOCK_SIZE_BITS - 9); + nr_sects = 0; /* hmm - should be partition size */ + + switch (bh->b_data[0x1fc] & 15) { + case 0: /* No partition / ADFS? */ + break; + +#ifdef CONFIG_ACORN_PARTITION_RISCIX + case PARTITION_RISCIX_SCSI: /* RiscIX - we don't know how to find the next one. */ + minor = riscix_partition(hd, dev, first_sector, minor, nr_sects); + break; +#endif + + case PARTITION_LINUX: + minor = linux_partition(hd, dev, first_sector, minor, nr_sects); + break; + } + brelse(bh); + bh = NULL; + if (minor == -1) + return minor; + } while (1); + if (bh) + brelse(bh); + return first ? 0 : 1; +} +#endif + +#ifdef CONFIG_ACORN_PARTITION_ADFS +/* + * Purpose: allocate ADFS partitions. + * + * Params : hd - pointer to gendisk structure to store partition info. + * dev - device number to access. + * first_sector- first readable sector on the device. + * minor - first available minor on device. + * + * Returns: -1 on error, 0 for no ADFS boot sector, 1 for ok. + * + * Alloc : hda = whole drive + * hda1 = ADFS partition on first drive. + * hda2 = non-ADFS partition. + */ +static int adfspart_check_ADFS(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + unsigned int minor) +{ + unsigned long start_sect, nr_sects, sectscyl, heads; + struct buffer_head *bh; + struct disc_record *dr; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + if (!(bh = bread(dev, 3, 1024))) + return -1; + + dr = adfs_partition(hd, "ADFS", bh->b_data, first_sector, minor++); + if (!dr) { + brelse(bh); + return 0; + } + + heads = dr->heads + ((dr->lowsector >> 6) & 1); + adfspart_setgeometry(dev, dr->secspertrack, heads, hd->part[MINOR(dev)].nr_sects); + sectscyl = dr->secspertrack * heads; + + /* + * Work out start of non-adfs partition. + */ + start_sect = ((bh->b_data[0x1fe] << 8) + bh->b_data[0x1fd]) * sectscyl; + nr_sects = hd->part[MINOR(dev)].nr_sects - start_sect; + + if (start_sect) { + first_sector += start_sect; + /* + * we now have a problem - how to set the origional disk size if the + * disk doesn't report it, since there is no standard way of getting + * that info. + */ + switch (bh->b_data[0x1fc] & 15) { +#ifdef CONFIG_ACORN_PARTITION_RISCIX + case PARTITION_RISCIX_SCSI: + case PARTITION_RISCIX_MFM: + minor = riscix_partition(hd, dev, first_sector, minor, nr_sects); + break; +#endif + + case PARTITION_LINUX: + minor = linux_partition(hd, dev, first_sector, minor, nr_sects); + break; + } + } + brelse(bh); + return 1; +} +#endif + +#ifdef CONFIG_ACORN_PARTITION_ICS +static int adfspart_check_ICSLinux(kdev_t dev, unsigned long block) +{ + struct buffer_head *bh; + unsigned int offset = block & 1 ? 512 : 0; + int result = 0; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + bh = bread(dev, block >> 1, 1024); + + if (bh != NULL) { + if (memcmp(bh->b_data + offset, "LinuxPart", 9) == 0) + result = 1; + + brelse(bh); + } + + return result; +} + +/* + * Purpose: allocate ICS partitions. + * Params : hd - pointer to gendisk structure to store partition info. + * dev - device number to access. + * first_sector- first readable sector on the device. + * minor - first available minor on device. + * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok. + * Alloc : hda = whole drive + * hda1 = ADFS partition 0 on first drive. + * hda2 = ADFS partition 1 on first drive. + * ..etc.. + */ +static int adfspart_check_ICS(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + unsigned int minor) +{ + struct buffer_head *bh; + unsigned long sum; + unsigned int i, mask = (1 << hd->minor_shift) - 1; + struct ics_part { unsigned long start; signed long size; } *p; + + if(get_ptable_blocksize(dev)!=1024) + return 0; + + /* + * Try ICS style partitions - sector 0 contains partition info. + */ + if (!(bh = bread(dev, 0, 1024))) + return -1; + + /* + * check for a valid checksum + */ + for (i = 0, sum = 0x50617274; i < 508; i++) + sum += bh->b_data[i]; + + if (sum != *(unsigned long *)(&bh->b_data[508])) { + brelse(bh); + return 0; /* not ICS partition table */ + } + + printk(" [ICS]"); + + for (p = (struct ics_part *)bh->b_data; p->size; p++) { + if ((minor & mask) == 0) + break; + + if (p->size < 0 && adfspart_check_ICSLinux(dev, p->start)) { + /* + * We use the first sector to identify what type + * this partition is... + */ + if (-p->size > 1) + add_gd_partition(hd, minor, first_sector + p->start + 1, -p->size - 1); + } else + add_gd_partition(hd, minor, first_sector + p->start, p->size); + minor++; + } + + brelse(bh); + return 1; +} +#endif + +#ifdef CONFIG_ACORN_PARTITION_POWERTEC +struct ptec_partition { + u32 unused1; + u32 unused2; + u32 start; + u32 size; + u32 unused5; + char type[8]; +}; + +/* + * Purpose: allocate ICS partitions. + * Params : hd - pointer to gendisk structure to store partition info. + * dev - device number to access. + * first_sector- first readable sector on the device. + * minor - first available minor on device. + * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok. + * Alloc : hda = whole drive + * hda1 = ADFS partition 0 on first drive. + * hda2 = ADFS partition 1 on first drive. + * ..etc.. + */ +static int adfspart_check_POWERTEC(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + unsigned int minor) +{ + struct buffer_head *bh; + struct ptec_partition *p; + unsigned char checksum; + int i; + + if (!(bh = bread(dev, 0, 1024))) + return -1; + + for (checksum = 0x2a, i = 0; i < 511; i++) + checksum += bh->b_data[i]; + + if (checksum != bh->b_data[511]) { + brelse(bh); + return 0; + } + + printk(" [POWERTEC]"); + + for (i = 0, p = (struct ptec_partition *)bh->b_data; i < 12; i++, p++) { + if (p->size) + add_gd_partition(hd, minor, first_sector + p->start, p->size); + minor++; + } + + brelse(bh); + return 1; +} +#endif + +/* + * Purpose: initialise all the partitions on an ADFS drive. + * These may be other ADFS partitions or a Linux/RiscBSD/RiscIX + * partition. + * + * Params : hd - pointer to gendisk structure to store devices partitions. + * dev - device number to access + * first_sector - first available sector on the disk. + * minor - first available minor on this device. + * + * Returns: -1 on error, 0 if not ADFS format, 1 if ok. + */ +int acorn_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor) +{ + int r = 0; + +#ifdef CONFIG_ACORN_PARTITION_ICS + if (r == 0) + r = adfspart_check_ICS(hd, dev, first_sector, first_part_minor); +#endif +#ifdef CONFIG_ACORN_PARTITION_CUMANA + if (r == 0) + r = adfspart_check_CUMANA(hd, dev, first_sector, first_part_minor); +#endif +#ifdef CONFIG_ACORN_PARTITION_ADFS + if (r == 0) + r = adfspart_check_ADFS(hd, dev, first_sector, first_part_minor); +#endif +#ifdef CONFIG_ACORN_PARTITION_POWERTEC + if (r == 0) + r = adfspart_check_POWERTEC(hd, dev, first_sector, first_part_minor); +#endif + if (r < 0) + printk(" unable to read boot sectors / partition sectors\n"); + else if (r) + printk("\n"); + return r; +} diff --git a/fs/partitions/acorn.h b/fs/partitions/acorn.h new file mode 100644 index 000000000..932c305e6 --- /dev/null +++ b/fs/partitions/acorn.h @@ -0,0 +1,68 @@ +/* + * fs/partitions/acorn.h + * + * Copyright (C) 1996-1998 Russell King + */ +#include <linux/adfs_fs.h> + +/* + * Offset in bytes of the boot block on the disk. + */ +#define BOOT_SECTOR_ADDRESS 0xc00 + +/* + * Disc record size + */ +#define RECSIZE 60 + +/* + * Disc record + */ +struct disc_record { + unsigned char log2secsize; + unsigned char secspertrack; + unsigned char heads; + unsigned char density; + unsigned char idlen; + unsigned char log2bpmb; + unsigned char skew; + unsigned char bootoption; + unsigned char lowsector; + unsigned char nzones; + unsigned short zone_spare; + unsigned long root; + unsigned long disc_size; + unsigned short disc_id; + unsigned char disc_name[10]; + unsigned long disc_type; + unsigned long disc_size_high; + unsigned char log2sharesize:4; + unsigned char unused:4; + unsigned char big_flag:1; +}; + +/* + * Partition types. (Oh for reusability) + */ +#define PARTITION_RISCIX_MFM 1 +#define PARTITION_RISCIX_SCSI 2 +#define PARTITION_LINUX 9 + +struct riscix_part { + unsigned long start; + unsigned long length; + unsigned long one; + char name[16]; +}; + +struct riscix_record { + unsigned long magic; +#define RISCIX_MAGIC (0x4a657320) + unsigned long date; + struct riscix_part part[8]; +}; + +int +acorn_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c new file mode 100644 index 000000000..aa8605315 --- /dev/null +++ b/fs/partitions/amiga.c @@ -0,0 +1,120 @@ +/* + * fs/partitions/amiga.c + * + * Code extracted from drivers/block/genhd.c + * + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/byteorder.h> +#include <linux/affs_hardblocks.h> + +#include "check.h" +#include "amiga.h" + +static __inline__ u32 +checksum_block(u32 *m, int size) +{ + u32 sum = 0; + + while (size--) + sum += be32_to_cpu(*m++); + return sum; +} + +int +amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor) +{ + struct buffer_head *bh; + struct RigidDiskBlock *rdb; + struct PartitionBlock *pb; + int start_sect; + int nr_sects; + int blk; + int part, res; + int old_blocksize; + int blocksize; + + old_blocksize = get_ptable_blocksize(dev); + blocksize = get_hardsect_size(dev); + + if (blocksize < 512) + blocksize = 512; + + set_blocksize(dev,blocksize); + res = 0; + + for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) { + if(!(bh = bread(dev,blk,blocksize))) { + printk("Dev %s: unable to read RDB block %d\n", + kdevname(dev),blk); + goto rdb_done; + } + if (*(u32 *)bh->b_data == cpu_to_be32(IDNAME_RIGIDDISK)) { + rdb = (struct RigidDiskBlock *)bh->b_data; + if (checksum_block((u32 *)bh->b_data,be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)) { + /* Try again with 0xdc..0xdf zeroed, Windows might have + * trashed it. + */ + *(u32 *)(&bh->b_data[0xdc]) = 0; + if (checksum_block((u32 *)bh->b_data, + be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)) { + brelse(bh); + printk("Dev %s: RDB in block %d has bad checksum\n", + kdevname(dev),blk); + continue; + } + printk("Warning: Trashed word at 0xd0 in block %d " + "ignored in checksum calculation\n",blk); + } + printk(" RDSK"); + blk = be32_to_cpu(rdb->rdb_PartitionList); + brelse(bh); + for (part = 1; blk > 0 && part <= 16; part++) { + if (!(bh = bread(dev,blk,blocksize))) { + printk("Dev %s: unable to read partition block %d\n", + kdevname(dev),blk); + goto rdb_done; + } + pb = (struct PartitionBlock *)bh->b_data; + blk = be32_to_cpu(pb->pb_Next); + if (pb->pb_ID == cpu_to_be32(IDNAME_PARTITION) && checksum_block( + (u32 *)pb,be32_to_cpu(pb->pb_SummedLongs) & 0x7F) == 0 ) { + + /* Tell Kernel about it */ + + if (!(nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 - + be32_to_cpu(pb->pb_Environment[9])) * + be32_to_cpu(pb->pb_Environment[3]) * + be32_to_cpu(pb->pb_Environment[5]))) { + brelse(bh); + continue; + } + start_sect = be32_to_cpu(pb->pb_Environment[9]) * + be32_to_cpu(pb->pb_Environment[3]) * + be32_to_cpu(pb->pb_Environment[5]); + add_gd_partition(hd,first_part_minor,start_sect,nr_sects); + first_part_minor++; + res = 1; + } + brelse(bh); + } + printk("\n"); + break; + } + else + brelse(bh); + } + +rdb_done: + set_blocksize(dev,old_blocksize); + return res; +} diff --git a/fs/partitions/amiga.h b/fs/partitions/amiga.h new file mode 100644 index 000000000..37c6ec144 --- /dev/null +++ b/fs/partitions/amiga.h @@ -0,0 +1,8 @@ +/* + * fs/partitions/amiga.h + */ + +int +amiga_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c new file mode 100644 index 000000000..ae8596e6a --- /dev/null +++ b/fs/partitions/atari.c @@ -0,0 +1,177 @@ +/* + * fs/partitions/atari.c + * + * Code extracted from drivers/block/genhd.c + * + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> +#include <linux/ctype.h> + +#include <asm/byteorder.h> +#include <asm/system.h> + +#include "check.h" +#include "atari.h" + +/* ++guenther: this should be settable by the user ("make config")?. + */ +#define ICD_PARTS + +/* check if a partition entry looks valid -- Atari format is assumed if at + least one of the primary entries is ok this way */ +#define VALID_PARTITION(pi,hdsiz) \ + (((pi)->flg & 1) && \ + isalnum((pi)->id[0]) && isalnum((pi)->id[1]) && isalnum((pi)->id[2]) && \ + be32_to_cpu((pi)->st) <= (hdsiz) && \ + be32_to_cpu((pi)->st) + be32_to_cpu((pi)->siz) <= (hdsiz)) + +int atari_partition (struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor) +{ + int minor = first_part_minor, m_lim = first_part_minor + hd->max_p; + struct buffer_head *bh; + struct rootsector *rs; + struct partition_info *pi; + u32 extensect; + u32 hd_size; +#ifdef ICD_PARTS + int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */ +#endif + + bh = bread (dev, 0, get_ptable_blocksize(dev)); + if (!bh) { + printk (" unable to read block 0 (partition table)\n"); + return -1; + } + + /* Verify this is an Atari rootsector: */ + rs = (struct rootsector *) bh->b_data; + hd_size = hd->part[minor - 1].nr_sects; + if (!VALID_PARTITION(&rs->part[0], hd_size) && + !VALID_PARTITION(&rs->part[1], hd_size) && + !VALID_PARTITION(&rs->part[2], hd_size) && + !VALID_PARTITION(&rs->part[3], hd_size)) { + /* if there's no valid primary partition, assume that no Atari + format partition table (there's no reliable magic or the like + :-() */ + return 0; + } + + pi = &rs->part[0]; + printk (" AHDI"); + for (; pi < &rs->part[4] && minor < m_lim; minor++, pi++) + { + if (pi->flg & 1) + /* active partition */ + { + if (memcmp (pi->id, "XGM", 3) == 0) + /* extension partition */ + { + struct rootsector *xrs; + struct buffer_head *xbh; + ulong partsect; + +#ifdef ICD_PARTS + part_fmt = 1; +#endif + printk(" XGM<"); + partsect = extensect = be32_to_cpu(pi->st); + while (1) + { + xbh = bread (dev, partsect / 2, get_ptable_blocksize(dev)); + if (!xbh) + { + printk (" block %ld read failed\n", partsect); + brelse(bh); + return 0; + } + if (partsect & 1) + xrs = (struct rootsector *) &xbh->b_data[512]; + else + xrs = (struct rootsector *) &xbh->b_data[0]; + + /* ++roman: sanity check: bit 0 of flg field must be set */ + if (!(xrs->part[0].flg & 1)) { + printk( "\nFirst sub-partition in extended partition is not valid!\n" ); + break; + } + + add_gd_partition(hd, minor, + partsect + be32_to_cpu(xrs->part[0].st), + be32_to_cpu(xrs->part[0].siz)); + + if (!(xrs->part[1].flg & 1)) { + /* end of linked partition list */ + brelse( xbh ); + break; + } + if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) { + printk( "\nID of extended partition is not XGM!\n" ); + brelse( xbh ); + break; + } + + partsect = be32_to_cpu(xrs->part[1].st) + extensect; + brelse (xbh); + minor++; + if (minor >= m_lim) { + printk( "\nMaximum number of partitions reached!\n" ); + break; + } + } + printk(" >"); + } + else + { + /* we don't care about other id's */ + add_gd_partition (hd, minor, be32_to_cpu(pi->st), + be32_to_cpu(pi->siz)); + } + } + } +#ifdef ICD_PARTS + if ( part_fmt!=1 ) /* no extended partitions -> test ICD-format */ + { + pi = &rs->icdpart[0]; + /* sanity check: no ICD format if first partition invalid */ + if (memcmp (pi->id, "GEM", 3) == 0 || + memcmp (pi->id, "BGM", 3) == 0 || + memcmp (pi->id, "LNX", 3) == 0 || + memcmp (pi->id, "SWP", 3) == 0 || + memcmp (pi->id, "RAW", 3) == 0 ) + { + printk(" ICD<"); + for (; pi < &rs->icdpart[8] && minor < m_lim; minor++, pi++) + { + /* accept only GEM,BGM,RAW,LNX,SWP partitions */ + if (pi->flg & 1 && + (memcmp (pi->id, "GEM", 3) == 0 || + memcmp (pi->id, "BGM", 3) == 0 || + memcmp (pi->id, "LNX", 3) == 0 || + memcmp (pi->id, "SWP", 3) == 0 || + memcmp (pi->id, "RAW", 3) == 0) ) + { + part_fmt = 2; + add_gd_partition (hd, minor, be32_to_cpu(pi->st), + be32_to_cpu(pi->siz)); + } + } + printk(" >"); + } + } +#endif + brelse (bh); + + printk ("\n"); + + return 1; +} + diff --git a/fs/partitions/atari.h b/fs/partitions/atari.h new file mode 100644 index 000000000..6d19c93e5 --- /dev/null +++ b/fs/partitions/atari.h @@ -0,0 +1,36 @@ +/* + * fs/partitions/atari.h + * Moved by Russell King from: + * + * linux/include/linux/atari_rootsec.h + * definitions for Atari Rootsector layout + * by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de) + * + * modified for ICD/Supra partitioning scheme restricted to at most 12 + * partitions + * by Guenther Kelleter (guenther@pool.informatik.rwth-aachen.de) + */ + +struct partition_info +{ + u8 flg; /* bit 0: active; bit 7: bootable */ + char id[3]; /* "GEM", "BGM", "XGM", or other */ + u32 st; /* start of partition */ + u32 siz; /* length of partition */ +}; + +struct rootsector +{ + char unused[0x156]; /* room for boot code */ + struct partition_info icdpart[8]; /* info for ICD-partitions 5..12 */ + char unused2[0xc]; + u32 hd_siz; /* size of disk in blocks */ + struct partition_info part[4]; + u32 bsl_st; /* start of bad sector list */ + u32 bsl_cnt; /* length of bad sector list */ + u16 checksum; /* checksum for bootable disks */ +} __attribute__((__packed__)); + +int atari_partition (struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + diff --git a/fs/partitions/check.c b/fs/partitions/check.c new file mode 100644 index 000000000..76476f512 --- /dev/null +++ b/fs/partitions/check.c @@ -0,0 +1,334 @@ +/* + * Code extracted from drivers/block/genhd.c + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + * + * We now have independent partition support from the + * block drivers, which allows all the partition code to + * be grouped in one location, and it to be mostly self + * contained. + * + * Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl} + */ + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/blk.h> +#include <linux/init.h> + +#include "check.h" + +#include "acorn.h" +#include "amiga.h" +#include "atari.h" +#include "mac.h" +#include "msdos.h" +#include "osf.h" +#include "sgi.h" +#include "sun.h" + +extern void device_init(void); +extern void md_setup_drive(void); +extern int *blk_size[]; +extern void rd_load(void); +extern void initrd_load(void); + +struct gendisk *gendisk_head; + +static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = { +#ifdef CONFIG_ACORN_PARTITION + acorn_partition, +#endif +#ifdef CONFIG_MSDOS_PARTITION + msdos_partition, +#endif +#ifdef CONFIG_OSF_PARTITION + osf_partition, +#endif +#ifdef CONFIG_SUN_PARTITION + sun_partition, +#endif +#ifdef CONFIG_AMIGA_PARTITION + amiga_partition, +#endif +#ifdef CONFIG_ATARI_PARTITION + atari_partition, +#endif +#ifdef CONFIG_MAC_PARTITION + mac_partition, +#endif +#ifdef CONFIG_SGI_PARTITION + sgi_partition, +#endif +#ifdef CONFIG_ULTRIX_PARTITION + ultrix_partition, +#endif + NULL +}; + +/* + * disk_name() is used by genhd.c and blkpg.c. + * It formats the devicename of the indicated disk into + * the supplied buffer (of size at least 32), and returns + * a pointer to that same buffer (for convenience). + */ +char *disk_name (struct gendisk *hd, int minor, char *buf) +{ + unsigned int part; + const char *maj = hd->major_name; + int unit = (minor >> hd->minor_shift) + 'a'; + + /* + * IDE devices use multiple major numbers, but the drives + * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}.. + * This requires special handling here. + */ + switch (hd->major) { + case IDE9_MAJOR: + unit += 2; + case IDE8_MAJOR: + unit += 2; + case IDE7_MAJOR: + unit += 2; + case IDE6_MAJOR: + unit += 2; + case IDE5_MAJOR: + unit += 2; + case IDE4_MAJOR: + unit += 2; + case IDE3_MAJOR: + unit += 2; + case IDE2_MAJOR: + unit += 2; + case IDE1_MAJOR: + unit += 2; + case IDE0_MAJOR: + maj = "hd"; + break; + } + part = minor & ((1 << hd->minor_shift) - 1); + if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { + unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; + if (unit > 'z') { + unit -= 'z' + 1; + sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26); + if (part) + sprintf(buf + 4, "%d", part); + return buf; + } + } + if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) { + int ctlr = hd->major - COMPAQ_SMART2_MAJOR; + int disk = minor >> hd->minor_shift; + int part = minor & (( 1 << hd->minor_shift) - 1); + if (part == 0) + sprintf(buf, "%s/c%dd%d", maj, ctlr, disk); + else + sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part); + return buf; + } + if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) { + int ctlr = hd->major - DAC960_MAJOR; + int disk = minor >> hd->minor_shift; + int part = minor & (( 1 << hd->minor_shift) - 1); + if (part == 0) + sprintf(buf, "%s/c%dd%d", maj, ctlr, disk); + else + sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part); + return buf; + } + if (part) + sprintf(buf, "%s%c%d", maj, unit, part); + else + sprintf(buf, "%s%c", maj, unit); + return buf; +} + +/* + * Add a partitions details to the devices partition description. + */ +void add_gd_partition(struct gendisk *hd, int minor, int start, int size) +{ + char buf[40]; + hd->part[minor].start_sect = start; + hd->part[minor].nr_sects = size; + if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) + printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); + else + printk(" %s", disk_name(hd, minor, buf)); +} + +int get_hardsect_size(kdev_t dev) +{ + if (hardsect_size[MAJOR(dev)] != NULL) + return hardsect_size[MAJOR(dev)][MINOR(dev)]; + else + return 512; +} + +unsigned int get_ptable_blocksize(kdev_t dev) +{ + int ret = 1024; + + /* + * See whether the low-level driver has given us a minumum blocksize. + * If so, check to see whether it is larger than the default of 1024. + */ + if (!blksize_size[MAJOR(dev)]) + return ret; + + /* + * Check for certain special power of two sizes that we allow. + * With anything larger than 1024, we must force the blocksize up to + * the natural blocksize for the device so that we don't have to try + * and read partial sectors. Anything smaller should be just fine. + */ + + switch (blksize_size[MAJOR(dev)][MINOR(dev)]) { + case 2048: + ret = 2048; + break; + case 4096: + ret = 4096; + break; + case 8192: + ret = 8192; + break; + case 1024: + case 512: + case 256: + case 0: + /* + * These are all OK. + */ + break; + default: + panic("Strange blocksize for partition table\n"); + } + + return ret; +} + +#ifdef CONFIG_PROC_FS +int get_partition_list(char * page) +{ + struct gendisk *p; + char buf[40]; + int n, len; + + len = sprintf(page, "major minor #blocks name\n\n"); + for (p = gendisk_head; p; p = p->next) { + for (n=0; n < (p->nr_real << p->minor_shift); n++) { + if (p->part[n].nr_sects && len < PAGE_SIZE - 80) { + len += sprintf(page+len, + "%4d %4d %10d %s\n", + p->major, n, p->sizes[n], + disk_name(p, n, buf)); + } + } + } + return len; +} +#endif + +void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor) +{ + static int first_time = 1; + unsigned long first_sector; + char buf[40]; + int i; + + if (first_time) + printk(KERN_INFO "Partition check:\n"); + first_time = 0; + first_sector = hd->part[MINOR(dev)].start_sect; + + /* + * This is a kludge to allow the partition check to be + * skipped for specific drives (e.g. IDE CD-ROM drives) + */ + if ((int)first_sector == -1) { + hd->part[MINOR(dev)].start_sect = 0; + return; + } + + printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); + for (i = 0; check_part[i]; i++) + if (check_part[i](hd, dev, first_sector, first_part_minor)) + return; + + printk(" unknown partition table\n"); +} + +/* + * This function will re-read the partition tables for a given device, + * and set things back up again. There are some important caveats, + * however. You must ensure that no one is using the device, and no one + * can start using the device while this function is being executed. + * + * Much of the cleanup from the old partition tables should have already been + * done + */ +void resetup_one_dev(struct gendisk *dev, int drive) +{ + int i; + int first_minor = drive << dev->minor_shift; + int end_minor = first_minor + dev->max_p; + + blk_size[dev->major] = NULL; + check_partition(dev, MKDEV(dev->major, first_minor), 1 + first_minor); + + /* + * We need to set the sizes array before we will be able to access + * any of the partitions on this device. + */ + if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */ + for (i = first_minor; i < end_minor; i++) + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9); + blk_size[dev->major] = dev->sizes; + } +} + +static inline void setup_dev(struct gendisk *dev) +{ + int i, drive; + int end_minor = dev->max_nr * dev->max_p; + + blk_size[dev->major] = NULL; + for (i = 0; i < end_minor; i++) { + dev->part[i].start_sect = 0; + dev->part[i].nr_sects = 0; + dev->sizes[i] = 0; + } + dev->init(dev); + for (drive = 0 ; drive < dev->nr_real ; drive++) + resetup_one_dev(dev, drive); +} + +int __init partition_setup(void) +{ + struct gendisk *p; + + device_init(); + + for (p = gendisk_head ; p ; p=p->next) + setup_dev(p); + +#ifdef CONFIG_BLK_DEV_RAM +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start && mount_initrd) initrd_load(); + else +#endif + rd_load(); +#endif +#ifdef CONFIG_MD_BOOT + md_setup_drive(); +#endif + + return 0; +} + +__initcall(partition_setup); diff --git a/fs/partitions/check.h b/fs/partitions/check.h new file mode 100644 index 000000000..804d54517 --- /dev/null +++ b/fs/partitions/check.h @@ -0,0 +1,10 @@ +/* + * add_partition adds a partitions details to the devices partition + * description. + */ +void add_gd_partition(struct gendisk *hd, int minor, int start, int size); + +/* + * Get the default block size for this device + */ +unsigned int get_ptable_blocksize(kdev_t dev); diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c new file mode 100644 index 000000000..41364385d --- /dev/null +++ b/fs/partitions/mac.c @@ -0,0 +1,110 @@ +/* + * fs/partitions/mac.c + * + * Code extracted from drivers/block/genhd.c + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + */ + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> +#include <linux/ctype.h> + +#include <asm/system.h> + +#include "check.h" +#include "mac.h" + +#ifdef CONFIG_PPC +extern void note_bootable_part(kdev_t dev, int part); +#endif + +/* + * Code to understand MacOS partition tables. + */ + +int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_part_minor) +{ + struct buffer_head *bh; + int blk, blocks_in_map; + int dev_bsize, dev_pos, pos; + unsigned secsize; +#ifdef CONFIG_PPC + int first_bootable = 1; +#endif + struct mac_partition *part; + struct mac_driver_desc *md; + + dev_bsize = get_ptable_blocksize(dev); + dev_pos = 0; + /* Get 0th block and look at the first partition map entry. */ + if ((bh = bread(dev, 0, dev_bsize)) == 0) { + printk("%s: error reading partition table\n", + kdevname(dev)); + return -1; + } + md = (struct mac_driver_desc *) bh->b_data; + if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) { + brelse(bh); + return 0; + } + secsize = be16_to_cpu(md->block_size); + if (secsize >= dev_bsize) { + brelse(bh); + dev_pos = secsize; + if ((bh = bread(dev, secsize/dev_bsize, dev_bsize)) == 0) { + printk("%s: error reading partition table\n", + kdevname(dev)); + return -1; + } + } + part = (struct mac_partition *) (bh->b_data + secsize - dev_pos); + if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) { + brelse(bh); + return 0; /* not a MacOS disk */ + } + blocks_in_map = be32_to_cpu(part->map_count); + for (blk = 1; blk <= blocks_in_map; ++blk) { + pos = blk * secsize; + if (pos >= dev_pos + dev_bsize) { + brelse(bh); + dev_pos = pos; + if ((bh = bread(dev, pos/dev_bsize, dev_bsize)) == 0) { + printk("%s: error reading partition table\n", + kdevname(dev)); + return -1; + } + } + part = (struct mac_partition *) (bh->b_data + pos - dev_pos); + if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) + break; + blocks_in_map = be32_to_cpu(part->map_count); + add_gd_partition(hd, first_part_minor, + fsec + be32_to_cpu(part->start_block) * (secsize/512), + be32_to_cpu(part->block_count) * (secsize/512)); + +#ifdef CONFIG_PPC + /* + * If this is the first bootable partition, tell the + * setup code, in case it wants to make this the root. + */ + if ( (_machine == _MACH_Pmac) && first_bootable + && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) + && strcasecmp(part->processor, "powerpc") == 0) { + note_bootable_part(dev, blk); + first_bootable = 0; + } +#endif /* CONFIG_PPC */ + + ++first_part_minor; + } + brelse(bh); + printk("\n"); + return 1; +} + diff --git a/fs/partitions/mac.h b/fs/partitions/mac.h new file mode 100644 index 000000000..1a3fd68de --- /dev/null +++ b/fs/partitions/mac.h @@ -0,0 +1,44 @@ +/* + * fs/partitions/mac.h + */ + +#define MAC_PARTITION_MAGIC 0x504d + +/* type field value for A/UX or other Unix partitions */ +#define APPLE_AUX_TYPE "Apple_UNIX_SVR2" + +struct mac_partition { + __u16 signature; /* expected to be MAC_PARTITION_MAGIC */ + __u16 res1; + __u32 map_count; /* # blocks in partition map */ + __u32 start_block; /* absolute starting block # of partition */ + __u32 block_count; /* number of blocks in partition */ + char name[32]; /* partition name */ + char type[32]; /* string type description */ + __u32 data_start; /* rel block # of first data block */ + __u32 data_count; /* number of data blocks */ + __u32 status; /* partition status bits */ + __u32 boot_start; + __u32 boot_size; + __u32 boot_load; + __u32 boot_load2; + __u32 boot_entry; + __u32 boot_entry2; + __u32 boot_cksum; + char processor[16]; /* identifies ISA of boot */ + /* there is more stuff after this that we don't need */ +}; + +#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */ + +#define MAC_DRIVER_MAGIC 0x4552 + +/* Driver descriptor structure, in block 0 */ +struct mac_driver_desc { + __u16 signature; /* expected to be MAC_DRIVER_MAGIC */ + __u16 block_size; + __u32 block_count; + /* ... more stuff */ +}; + +int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_part_minor); diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c new file mode 100644 index 000000000..a4ba9253b --- /dev/null +++ b/fs/partitions/msdos.c @@ -0,0 +1,490 @@ +/* + * fs/partitions/msdos.c + * + * Code extracted from drivers/block/genhd.c + * Copyright (C) 1991-1998 Linus Torvalds + * + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug + * in the early extended-partition checks and added DM partitions + * + * Support for DiskManager v6.0x added by Mark Lord, + * with information provided by OnTrack. This now works for linux fdisk + * and LILO, as well as loadlin and bootln. Note that disks other than + * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1). + * + * More flexible handling of extended partitions - aeb, 950831 + * + * Check partition table on IDE disks for common CHS translations + * + * Re-organised Feb 1998 Russell King + */ + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/system.h> + +#include "check.h" +#include "msdos.h" + +static int current_minor; + +/* + * Many architectures don't like unaligned accesses, which is + * frequently the case with the nr_sects and start_sect partition + * table entries. + */ +#include <asm/unaligned.h> + +#define SYS_IND(p) (get_unaligned(&p->sys_ind)) +#define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \ + get_unaligned(&p->nr_sects); \ + le32_to_cpu(__a); \ + }) + +#define START_SECT(p) ({ __typeof__(p->start_sect) __a = \ + get_unaligned(&p->start_sect); \ + le32_to_cpu(__a); \ + }) + +static inline int is_extended_partition(struct partition *p) +{ + return (SYS_IND(p) == DOS_EXTENDED_PARTITION || + SYS_IND(p) == WIN98_EXTENDED_PARTITION || + SYS_IND(p) == LINUX_EXTENDED_PARTITION); +} + +/* + * Create devices for each logical partition in an extended partition. + * The logical partitions form a linked list, with each entry being + * a partition table with two entries. The first entry + * is the real data partition (with a start relative to the partition + * table start). The second is a pointer to the next logical partition + * (with a start relative to the entire extended partition). + * We do not create a Linux partition for the partition tables, but + * only for the actual data partitions. + */ + +static void extended_partition(struct gendisk *hd, kdev_t dev) +{ + struct buffer_head *bh; + struct partition *p; + unsigned long first_sector, first_size, this_sector, this_size; + int mask = (1 << hd->minor_shift) - 1; + int sector_size = get_hardsect_size(dev) / 512; + int loopct = 0; /* number of links followed + without finding a data partition */ + int i; + + first_sector = hd->part[MINOR(dev)].start_sect; + first_size = hd->part[MINOR(dev)].nr_sects; + this_sector = first_sector; + + while (1) { + if (++loopct > 100) + return; + if ((current_minor & mask) == 0) + return; + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) + return; + + if ((*(__u16 *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC)) + goto done; + + p = (struct partition *) (0x1BE + bh->b_data); + + this_size = hd->part[MINOR(dev)].nr_sects; + + /* + * Usually, the first entry is the real data partition, + * the 2nd entry is the next extended partition, or empty, + * and the 3rd and 4th entries are unused. + * However, DRDOS sometimes has the extended partition as + * the first entry (when the data partition is empty), + * and OS/2 seems to use all four entries. + */ + + /* + * First process the data partition(s) + */ + for (i=0; i<4; i++, p++) { + if (!NR_SECTS(p) || is_extended_partition(p)) + continue; + + /* Check the 3rd and 4th entries - + these sometimes contain random garbage */ + if (i >= 2 + && START_SECT(p) + NR_SECTS(p) > this_size + && (this_sector + START_SECT(p) < first_sector || + this_sector + START_SECT(p) + NR_SECTS(p) > + first_sector + first_size)) + continue; + + add_gd_partition(hd, current_minor, + this_sector+START_SECT(p)*sector_size, + NR_SECTS(p)*sector_size); + current_minor++; + loopct = 0; + if ((current_minor & mask) == 0) + goto done; + } + /* + * Next, process the (first) extended partition, if present. + * (So far, there seems to be no reason to make + * extended_partition() recursive and allow a tree + * of extended partitions.) + * It should be a link to the next logical partition. + * Create a minor for this just long enough to get the next + * partition table. The minor will be reused for the next + * data partition. + */ + p -= 4; + for (i=0; i<4; i++, p++) + if(NR_SECTS(p) && is_extended_partition(p)) + break; + if (i == 4) + goto done; /* nothing left to do */ + + hd->part[current_minor].nr_sects = NR_SECTS(p) * sector_size; /* JSt */ + hd->part[current_minor].start_sect = first_sector + START_SECT(p) * sector_size; + this_sector = first_sector + START_SECT(p) * sector_size; + dev = MKDEV(hd->major, current_minor); + + /* Use bforget(), as we have changed the disk geometry */ + bforget(bh); + } +done: + bforget(bh); +} + +#ifdef CONFIG_SOLARIS_X86_PARTITION +static void +solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) { + + struct buffer_head *bh; + struct solaris_x86_vtoc *v; + struct solaris_x86_slice *s; + int i; + + if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) + return; + v = (struct solaris_x86_vtoc *)(bh->b_data + 512); + if(v->v_sanity != SOLARIS_X86_VTOC_SANE) { + brelse(bh); + return; + } + printk(" <solaris:"); + if(v->v_version != 1) { + printk(" cannot handle version %ld vtoc>", v->v_version); + brelse(bh); + return; + } + for(i=0; i<SOLARIS_X86_NUMSLICE; i++) { + s = &v->v_slice[i]; + + if (s->s_size == 0) + continue; + printk(" [s%d]", i); + /* solaris partitions are relative to current MS-DOS + * one but add_gd_partition starts relative to sector + * zero of the disk. Therefore, must add the offset + * of the current partition */ + add_gd_partition(hd, current_minor, s->s_start+offset, s->s_size); + current_minor++; + } + brelse(bh); + printk(" >"); +} +#endif + +#ifdef CONFIG_BSD_DISKLABEL +static void check_and_add_bsd_partition(struct gendisk *hd, + struct bsd_partition *bsd_p, kdev_t dev) +{ + struct hd_struct *lin_p; + /* check relative position of partitions. */ + for (lin_p = hd->part + 1 + MINOR(dev); + lin_p - hd->part - MINOR(dev) < current_minor; lin_p++) { + /* no relationship -> try again */ + if (lin_p->start_sect + lin_p->nr_sects <= bsd_p->p_offset + || lin_p->start_sect >= bsd_p->p_offset + bsd_p->p_size) + continue; + /* equal -> no need to add */ + if (lin_p->start_sect == bsd_p->p_offset && + lin_p->nr_sects == bsd_p->p_size) + return; + /* bsd living within dos partition */ + if (lin_p->start_sect <= bsd_p->p_offset && lin_p->start_sect + + lin_p->nr_sects >= bsd_p->p_offset + bsd_p->p_size) { +#ifdef DEBUG_BSD_DISKLABEL + printk("w: %d %ld+%ld,%d+%d", + lin_p - hd->part, + lin_p->start_sect, lin_p->nr_sects, + bsd_p->p_offset, bsd_p->p_size); +#endif + break; + } + /* ouch: bsd and linux overlap. Don't even try for that partition */ +#ifdef DEBUG_BSD_DISKLABEL + printk("???: %d %ld+%ld,%d+%d", + lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects, + bsd_p->p_offset, bsd_p->p_size); +#endif + printk("???"); + return; + } /* if the bsd partition is not currently known to linux, we end + * up here + */ + add_gd_partition(hd, current_minor, bsd_p->p_offset, bsd_p->p_size); + current_minor++; +} +/* + * Create devices for BSD partitions listed in a disklabel, under a + * dos-like partition. See extended_partition() for more information. + */ +static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev, + int max_partitions) +{ + struct buffer_head *bh; + struct bsd_disklabel *l; + struct bsd_partition *p; + int mask = (1 << hd->minor_shift) - 1; + + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) + return; + l = (struct bsd_disklabel *) (bh->b_data+512); + if (l->d_magic != BSD_DISKMAGIC) { + brelse(bh); + return; + } + + if (l->d_npartitions < max_partitions) + max_partitions = l->d_npartitions; + for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { + if ((current_minor & mask) >= (4 + hd->max_p)) + break; + + if (p->p_fstype != BSD_FS_UNUSED) + check_and_add_bsd_partition(hd, p, dev); + } + + /* Use bforget(), as we have changed the disk setup */ + bforget(bh); + +} +#endif + +#ifdef CONFIG_UNIXWARE_DISKLABEL +/* + * Create devices for Unixware partitions listed in a disklabel, under a + * dos-like partition. See extended_partition() for more information. + */ +static void unixware_partition(struct gendisk *hd, kdev_t dev) +{ + struct buffer_head *bh; + struct unixware_disklabel *l; + struct unixware_slice *p; + int mask = (1 << hd->minor_shift) - 1; + + if (!(bh = bread(dev, 14, get_ptable_blocksize(dev)))) + return; + l = (struct unixware_disklabel *) (bh->b_data+512); + if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC || + le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) { + brelse(bh); + return; + } + printk(" <unixware:"); + p = &l->vtoc.v_slice[1]; + /* I omit the 0th slice as it is the same as whole disk. */ + while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) { + if ((current_minor & mask) == 0) + break; + + if (p->s_label != UNIXWARE_FS_UNUSED) { + add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p)); + current_minor++; + } + p++; + } + /* Use bforget, as we have changed the disk setup */ + bforget(bh); + printk(" >"); +} +#endif + +int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor) +{ + int i, minor = current_minor = first_part_minor; + struct buffer_head *bh; + struct partition *p; + unsigned char *data; + int mask = (1 << hd->minor_shift) - 1; + int sector_size = get_hardsect_size(dev) / 512; +#ifdef CONFIG_BSD_DISKLABEL + /* no bsd disklabel as a default */ + kdev_t bsd_kdev = 0; + int bsd_maxpart = BSD_MAXPARTITIONS; +#endif +#ifdef CONFIG_BLK_DEV_IDE + int tested_for_xlate = 0; + +read_mbr: +#endif + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { + printk(" unable to read partition table\n"); + return -1; + } + data = bh->b_data; +#ifdef CONFIG_BLK_DEV_IDE +check_table: +#endif + /* Use bforget(), because we have potentially changed the disk geometry */ + if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) { + bforget(bh); + return 0; + } + p = (struct partition *) (0x1be + data); + +#ifdef CONFIG_BLK_DEV_IDE + if (!tested_for_xlate++) { /* Do this only once per disk */ + /* + * Look for various forms of IDE disk geometry translation + */ + extern int ide_xlate_1024(kdev_t, int, int, const char *); + unsigned int sig = le16_to_cpu(*(unsigned short *)(data + 2)); + int heads = 0; + /* + * The i386 partition handling programs very often + * make partitions end on cylinder boundaries. + * There is no need to do so, and Linux fdisk doesnt always + * do this, and Windows NT on Alpha doesnt do this either, + * but still, this helps to guess #heads. + */ + for (i = 0; i < 4; i++) { + struct partition *q = &p[i]; + if (NR_SECTS(q)) { + if ((q->sector & 63) == 1 && + (q->end_sector & 63) == 63) + heads = q->end_head + 1; + break; + } + } + if (SYS_IND(p) == EZD_PARTITION) { + /* + * Accesses to sector 0 must go to sector 1 instead. + */ + if (ide_xlate_1024(dev, -1, heads, " [EZD]")) { + data += 512; + goto check_table; + } + } else if (SYS_IND(p) == DM6_PARTITION) { + + /* + * Everything on the disk is offset by 63 sectors, + * including a "new" MBR with its own partition table. + */ + if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]")) { + bforget(bh); + goto read_mbr; /* start over with new MBR */ + } + } else if (sig <= 0x1ae && + data[sig] == 0xAA && data[sig+1] == 0x55 && + (data[sig+2] & 1)) { + /* DM6 signature in MBR, courtesy of OnTrack */ + (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]"); + } else if (SYS_IND(p) == DM6_AUX1PARTITION || + SYS_IND(p) == DM6_AUX3PARTITION) { + /* + * DM6 on other than the first (boot) drive + */ + (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]"); + } else { + (void) ide_xlate_1024(dev, 2, heads, " [PTBL]"); + } + } +#endif /* CONFIG_BLK_DEV_IDE */ + + current_minor += 4; /* first "extra" minor (for extended partitions) */ + for (i=1 ; i<=4 ; minor++,i++,p++) { + if (!NR_SECTS(p)) + continue; + add_gd_partition(hd, minor, first_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size); + if (is_extended_partition(p)) { + printk(" <"); + /* + * If we are rereading the partition table, we need + * to set the size of the partition so that we will + * be able to bread the block containing the extended + * partition info. + */ + hd->sizes[minor] = hd->part[minor].nr_sects + >> (BLOCK_SIZE_BITS - 9); + extended_partition(hd, MKDEV(hd->major, minor)); + printk(" >"); + /* prevent someone doing mkfs or mkswap on an + extended partition, but leave room for LILO */ + if (hd->part[minor].nr_sects > 2) + hd->part[minor].nr_sects = 2; + } +#ifdef CONFIG_BSD_DISKLABEL + /* tag first disklabel for late recognition */ + if (SYS_IND(p) == BSD_PARTITION || SYS_IND(p) == NETBSD_PARTITION) { + printk("!"); + if (!bsd_kdev) + bsd_kdev = MKDEV(hd->major, minor); + } else if (SYS_IND(p) == OPENBSD_PARTITION) { + printk("!"); + if (!bsd_kdev) { + bsd_kdev = MKDEV(hd->major, minor); + bsd_maxpart = OPENBSD_MAXPARTITIONS; + } + } +#endif +#ifdef CONFIG_UNIXWARE_DISKLABEL + if (SYS_IND(p) == UNIXWARE_PARTITION) + unixware_partition(hd, MKDEV(hd->major, minor)); +#endif +#ifdef CONFIG_SOLARIS_X86_PARTITION + + /* james@bpgc.com: Solaris has a nasty indicator: 0x82 + * which also means linux swap. For that reason, all + * of the prints are done inside the + * solaris_x86_partition routine */ + + if(SYS_IND(p) == SOLARIS_X86_PARTITION) { + solaris_x86_partition(hd, MKDEV(hd->major, minor), + first_sector+START_SECT(p)); + } +#endif + } +#ifdef CONFIG_BSD_DISKLABEL + if (bsd_kdev) { + printk(" <"); + bsd_disklabel_partition(hd, bsd_kdev, bsd_maxpart); + printk(" >"); + } +#endif + /* + * Check for old-style Disk Manager partition table + */ + if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) { + p = (struct partition *) (0x1be + data); + for (i = 4 ; i < 16 ; i++, current_minor++) { + p--; + if ((current_minor & mask) == 0) + break; + if (!(START_SECT(p) && NR_SECTS(p))) + continue; + add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p)); + } + } + printk("\n"); + bforget(bh); + return 1; +} diff --git a/fs/partitions/msdos.h b/fs/partitions/msdos.h new file mode 100644 index 000000000..e24740c5e --- /dev/null +++ b/fs/partitions/msdos.h @@ -0,0 +1,9 @@ +/* + * fs/partitions/msdos.h + */ + +#define MSDOS_LABEL_MAGIC 0xAA55 + +int msdos_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c new file mode 100644 index 000000000..6cd1540c0 --- /dev/null +++ b/fs/partitions/osf.c @@ -0,0 +1,86 @@ +/* + * fs/partitions/osf.c + * + * Code extracted from drivers/block/genhd.c + * + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include "check.h" +#include "osf.h" + +int osf_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + int current_minor) +{ + int i; + int mask = (1 << hd->minor_shift) - 1; + struct buffer_head *bh; + struct disklabel { + u32 d_magic; + u16 d_type,d_subtype; + u8 d_typename[16]; + u8 d_packname[16]; + u32 d_secsize; + u32 d_nsectors; + u32 d_ntracks; + u32 d_ncylinders; + u32 d_secpercyl; + u32 d_secprtunit; + u16 d_sparespertrack; + u16 d_sparespercyl; + u32 d_acylinders; + u16 d_rpm, d_interleave, d_trackskew, d_cylskew; + u32 d_headswitch, d_trkseek, d_flags; + u32 d_drivedata[5]; + u32 d_spare[5]; + u32 d_magic2; + u16 d_checksum; + u16 d_npartitions; + u32 d_bbsize, d_sbsize; + struct d_partition { + u32 p_size; + u32 p_offset; + u32 p_fsize; + u8 p_fstype; + u8 p_frag; + u16 p_cpg; + } d_partitions[8]; + } * label; + struct d_partition * partition; + + if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { + printk("unable to read partition table\n"); + return -1; + } + label = (struct disklabel *) (bh->b_data+64); + partition = label->d_partitions; + if (label->d_magic != DISKLABELMAGIC) { + brelse(bh); + return 0; + } + if (label->d_magic2 != DISKLABELMAGIC) { + brelse(bh); + return 0; + } + for (i = 0 ; i < label->d_npartitions; i++, partition++) { + if ((current_minor & mask) == 0) + break; + if (partition->p_size) + add_gd_partition(hd, current_minor, + first_sector+partition->p_offset, + partition->p_size); + current_minor++; + } + printk("\n"); + brelse(bh); + return 1; +} + diff --git a/fs/partitions/osf.h b/fs/partitions/osf.h new file mode 100644 index 000000000..ce1415056 --- /dev/null +++ b/fs/partitions/osf.h @@ -0,0 +1,9 @@ +/* + * fs/partitions/osf.h + */ + +#define DISKLABELMAGIC (0x82564557UL) + +int osf_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + int current_minor); + diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c new file mode 100644 index 000000000..8ae97eca0 --- /dev/null +++ b/fs/partitions/sgi.c @@ -0,0 +1,86 @@ +/* + * fs/partitions/sgi.c + * + * Code extracted from drivers/block/genhd.c + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/byteorder.h> +#include <asm/system.h> + +#include "check.h" +#include "sgi.h" + +int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor) +{ + int i, csum, magic, current_minor = first_part_minor; + unsigned int *ui, start, blocks, cs; + struct buffer_head *bh; + struct sgi_disklabel { + int magic_mushroom; /* Big fat spliff... */ + short root_part_num; /* Root partition number */ + short swap_part_num; /* Swap partition number */ + char boot_file[16]; /* Name of boot file for ARCS */ + unsigned char _unused0[48]; /* Device parameter useless crapola.. */ + struct sgi_volume { + char name[8]; /* Name of volume */ + int block_num; /* Logical block number */ + int num_bytes; /* How big, in bytes */ + } volume[15]; + struct sgi_partition { + int num_blocks; /* Size in logical blocks */ + int first_block; /* First logical block */ + int type; /* Type of this partition */ + } partitions[16]; + int csum; /* Disk label checksum */ + int _unused1; /* Padding */ + } *label; + struct sgi_partition *p; + + if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { + printk("Dev %s: unable to read partition table\n", kdevname(dev)); + return -1; + } + label = (struct sgi_disklabel *) bh->b_data; + p = &label->partitions[0]; + magic = label->magic_mushroom; + if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) { + printk("Dev %s SGI disklabel: bad magic %08x\n", + kdevname(dev), magic); + brelse(bh); + return 0; + } + ui = ((unsigned int *) (label + 1)) - 1; + for(csum = 0; ui >= ((unsigned int *) label);) { + cs = *ui--; + csum += be32_to_cpu(cs); + } + if(csum) { + printk("Dev %s SGI disklabel: csum bad, label corrupted\n", + kdevname(dev)); + brelse(bh); + return 0; + } + /* All SGI disk labels have 16 partitions, disks under Linux only + * have 15 minor's. Luckily there are always a few zero length + * partitions which we don't care about so we never overflow the + * current_minor. + */ + for(i = 0; i < 16; i++, p++) { + blocks = be32_to_cpu(p->num_blocks); + start = be32_to_cpu(p->first_block); + if(!blocks) + continue; + add_gd_partition(hd, current_minor, start, blocks); + current_minor++; + } + printk("\n"); + brelse(bh); + return 1; +} diff --git a/fs/partitions/sgi.h b/fs/partitions/sgi.h new file mode 100644 index 000000000..6e4ac1ac4 --- /dev/null +++ b/fs/partitions/sgi.h @@ -0,0 +1,9 @@ +/* + * fs/partitions/sgi.h + */ + +extern int sgi_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + +#define SGI_LABEL_MAGIC 0x0be5a941 + diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c new file mode 100644 index 000000000..5d82a70c6 --- /dev/null +++ b/fs/partitions/sun.c @@ -0,0 +1,89 @@ +/* + * fs/partitions/sun.c + * + * Code extracted from drivers/block/genhd.c + * + * Copyright (C) 1991-1998 Linus Torvalds + * Re-organised Feb 1998 Russell King + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/blk.h> + +#include <asm/system.h> + +#include "check.h" +#include "sun.h" + +int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor) +{ + int i, csum; + unsigned short *ush; + struct buffer_head *bh; + struct sun_disklabel { + unsigned char info[128]; /* Informative text string */ + unsigned char spare[292]; /* Boot information etc. */ + unsigned short rspeed; /* Disk rotational speed */ + unsigned short pcylcount; /* Physical cylinder count */ + unsigned short sparecyl; /* extra sects per cylinder */ + unsigned char spare2[4]; /* More magic... */ + unsigned short ilfact; /* Interleave factor */ + unsigned short ncyl; /* Data cylinder count */ + unsigned short nacyl; /* Alt. cylinder count */ + unsigned short ntrks; /* Tracks per cylinder */ + unsigned short nsect; /* Sectors per track */ + unsigned char spare3[4]; /* Even more magic... */ + struct sun_partition { + __u32 start_cylinder; + __u32 num_sectors; + } partitions[8]; + unsigned short magic; /* Magic number */ + unsigned short csum; /* Label xor'd checksum */ + } * label; + struct sun_partition *p; + unsigned long spc; + + if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { + printk("Dev %s: unable to read partition table\n", + kdevname(dev)); + return -1; + } + label = (struct sun_disklabel *) bh->b_data; + p = label->partitions; + if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) { + printk("Dev %s Sun disklabel: bad magic %04x\n", + kdevname(dev), be16_to_cpu(label->magic)); + brelse(bh); + return 0; + } + /* Look at the checksum */ + ush = ((unsigned short *) (label+1)) - 1; + for(csum = 0; ush >= ((unsigned short *) label);) + csum ^= *ush--; + if(csum) { + printk("Dev %s Sun disklabel: Csum bad, label corrupted\n", + kdevname(dev)); + brelse(bh); + return 0; + } + /* All Sun disks have 8 partition entries */ + spc = be16_to_cpu(label->ntrks) * be16_to_cpu(label->nsect); + for(i=0; i < 8; i++, p++) { + unsigned long st_sector; + int num_sectors; + + st_sector = first_sector + be32_to_cpu(p->start_cylinder) * spc; + num_sectors = be32_to_cpu(p->num_sectors); + if (num_sectors) + add_gd_partition(hd, first_part_minor, st_sector, num_sectors); + first_part_minor++; + } + printk("\n"); + brelse(bh); + return 1; +} + diff --git a/fs/partitions/sun.h b/fs/partitions/sun.h new file mode 100644 index 000000000..3426d4aa2 --- /dev/null +++ b/fs/partitions/sun.h @@ -0,0 +1,9 @@ +/* + * fs/partitions/sun.h + */ + +#define SUN_LABEL_MAGIC 0xDABE + +int sun_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int first_part_minor); + diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c new file mode 100644 index 000000000..fcfaf0d22 --- /dev/null +++ b/fs/partitions/ultrix.c @@ -0,0 +1,60 @@ +/* + * fs/partitions/ultrix.c + * + * Code extracted from drivers/block/genhd.c + * + * Re-organised Jul 1999 Russell King + */ + +#include <linux/fs.h> +#include <linux/genhd.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/blk.h> + +#include "check.h" + +static int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) +{ + int i, minor = current_minor; + struct buffer_head *bh; + struct ultrix_disklabel { + s32 pt_magic; /* magic no. indicating part. info exits */ + s32 pt_valid; /* set by driver if pt is current */ + struct pt_info { + s32 pi_nblocks; /* no. of sectors */ + u32 pi_blkoff; /* block offset for start */ + } pt_part[8]; + } *label; + +#define PT_MAGIC 0x032957 /* Partition magic number */ +#define PT_VALID 1 /* Indicates if struct is valid */ + +#define SBLOCK ((unsigned long)((16384 - sizeof(struct ultrix_disklabel)) \ + /get_ptable_blocksize(dev))) + + bh = bread (dev, SBLOCK, get_ptable_blocksize(dev)); + if (!bh) { + printk (" unable to read block 0x%lx\n", SBLOCK); + return -1; + } + + label = (struct ultrix_disklabel *)(bh->b_data + + get_ptable_blocksize(dev) + - sizeof(struct ultrix_disklabel)); + + if (label->pt_magic == PT_MAGIC && label->pt_valid == PT_VALID) { + for (i=0; i<8; i++, minor++) + if (label->pt_part[i].pi_nblocks) + add_gd_partition(hd, minor, + label->pt_part[i].pi_blkoff, + label->pt_part[i].pi_nblocks); + brelse(bh); + printk ("\n"); + return 1; + } else { + brelse(bh); + return 0; + } +} + @@ -1,7 +1,7 @@ /* * linux/fs/pipe.c * - * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1991, 1992, 1999 Linus Torvalds */ #include <linux/mm.h> @@ -22,187 +22,258 @@ #undef FIFO_SUNOS_BRAINDAMAGE #endif -/* We don't use the head/tail construction any more. Now we use the start/len*/ -/* construction providing full use of PIPE_BUF (multiple of PAGE_SIZE) */ -/* Florian Coosmann (FGC) ^ current = 1 */ -/* Additionally, we now use locking technique. This prevents race condition */ -/* in case of paging and multiple read/write on the same pipe. (FGC) */ -/* Reads with count = 0 should always return 0. Julian Bradfield 1999-06-07. */ +/* + * We use a start+len construction, which provides full use of the + * allocated memory. + * -- Florian Coosmann (FGC) + * + * Reads with count = 0 should always return 0. + * -- Julian Bradfield 1999-06-07. + */ -static ssize_t do_pipe_read(struct file * filp, char * buf, size_t count) +/* Drop the inode semaphore and wait for a pipe event, atomically */ +static void pipe_wait(struct inode * inode) { - struct inode * inode = filp->f_dentry->d_inode; - ssize_t chars = 0, size = 0, read = 0; - char *pipebuf; + DECLARE_WAITQUEUE(wait, current); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(PIPE_WAIT(*inode), &wait); + up(PIPE_SEM(*inode)); + schedule(); + remove_wait_queue(PIPE_WAIT(*inode), &wait); + current->state = TASK_RUNNING; +} - if (filp->f_flags & O_NONBLOCK) { - if (PIPE_LOCK(*inode)) - return -EAGAIN; - if (PIPE_EMPTY(*inode)) { - if (PIPE_WRITERS(*inode)) - return -EAGAIN; - else - return 0; - } - } else while (PIPE_EMPTY(*inode) || PIPE_LOCK(*inode)) { - if (PIPE_EMPTY(*inode)) { - if (!PIPE_WRITERS(*inode) || !count) - return 0; +static ssize_t +pipe_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + ssize_t size, read, ret; + + /* Seeks are not allowed on pipes. */ + ret = -ESPIPE; + if (ppos != &filp->f_pos) + goto out_nolock; + + /* Always return 0 on null read. */ + ret = 0; + if (count == 0) + goto out_nolock; + + /* Get the pipe semaphore */ + ret = -ERESTARTSYS; + if (down_interruptible(PIPE_SEM(*inode))) + goto out_nolock; + + if (PIPE_EMPTY(*inode)) { + ret = 0; + if (!PIPE_WRITERS(*inode)) + goto out; + + ret = -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + goto out; + + for (;;) { + pipe_wait(inode); + ret = -ERESTARTSYS; + if (signal_pending(current)) + goto out_nolock; + if (down_interruptible(PIPE_SEM(*inode))) + goto out_nolock; + ret = 0; + if (!PIPE_EMPTY(*inode)) + break; + if (!PIPE_WRITERS(*inode)) + goto out; } - if (signal_pending(current)) - return -ERESTARTSYS; - interruptible_sleep_on(&PIPE_WAIT(*inode)); } - PIPE_LOCK(*inode)++; - while (count>0 && (size = PIPE_SIZE(*inode))) { - chars = PIPE_MAX_RCHUNK(*inode); + + /* Read what data is available. */ + ret = -EFAULT; + read = 0; + while (count > 0 && (size = PIPE_LEN(*inode))) { + char *pipebuf = PIPE_BASE(*inode) + PIPE_START(*inode); + ssize_t chars = PIPE_MAX_RCHUNK(*inode); + if (chars > count) chars = count; if (chars > size) chars = size; + + if (copy_to_user(buf, pipebuf, chars)) + goto out; + read += chars; - pipebuf = PIPE_BASE(*inode)+PIPE_START(*inode); PIPE_START(*inode) += chars; - PIPE_START(*inode) &= (PIPE_BUF-1); + PIPE_START(*inode) &= (PIPE_SIZE - 1); PIPE_LEN(*inode) -= chars; count -= chars; - copy_to_user(buf, pipebuf, chars ); buf += chars; } - PIPE_LOCK(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - if (read) { + + /* Cache behaviour optimization */ + if (!PIPE_LEN(*inode)) + PIPE_START(*inode) = 0; + + /* Signal writers there is more room. */ + wake_up_interruptible(PIPE_WAIT(*inode)); + + if (read) UPDATE_ATIME(inode); - return read; - } - if (PIPE_WRITERS(*inode)) - return -EAGAIN; - return 0; + ret = read; + +out: + up(PIPE_SEM(*inode)); +out_nolock: + return ret; } -static ssize_t do_pipe_write(struct file * filp, const char * buf, size_t count) +static ssize_t +pipe_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) { - struct inode * inode = filp->f_dentry->d_inode; - ssize_t chars = 0, free = 0, written = 0, err=0; - char *pipebuf; + struct inode *inode = filp->f_dentry->d_inode; + ssize_t free, written, ret; - if (!PIPE_READERS(*inode)) { /* no readers */ - send_sig(SIGPIPE,current,0); - return -EPIPE; - } - /* if count <= PIPE_BUF, we have to make it atomic */ - if (count <= PIPE_BUF) - free = count; - else - free = 1; /* can't do it atomically, wait for any free space */ - if (down_interruptible(&inode->i_sem)) { - return -ERESTARTSYS; - } - while (count>0) { - while ((PIPE_FREE(*inode) < free) || PIPE_LOCK(*inode)) { - if (!PIPE_READERS(*inode)) { /* no readers */ - send_sig(SIGPIPE,current,0); - err = -EPIPE; - goto errout; - } - if (signal_pending(current)) { - err = -ERESTARTSYS; - goto errout; - } - if (filp->f_flags & O_NONBLOCK) { - err = -EAGAIN; - goto errout; - } - interruptible_sleep_on(&PIPE_WAIT(*inode)); + /* Seeks are not allowed on pipes. */ + ret = -ESPIPE; + if (ppos != &filp->f_pos) + goto out_nolock; + + /* Null write succeeds. */ + ret = 0; + if (count == 0) + goto out_nolock; + + ret = -ERESTARTSYS; + if (down_interruptible(PIPE_SEM(*inode))) + goto out_nolock; + + /* No readers yields SIGPIPE. */ + if (!PIPE_READERS(*inode)) + goto sigpipe; + + /* If count <= PIPE_BUF, we have to make it atomic. */ + free = (count <= PIPE_BUF ? count : 1); + written = 0; + + /* Wait, or check for, available space. */ + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (PIPE_FREE(*inode) < free) + goto out; + } else { + while (PIPE_FREE(*inode) < free) { + pipe_wait(inode); + ret = -ERESTARTSYS; + if (signal_pending(current)) + goto out_nolock; + if (down_interruptible(PIPE_SEM(*inode))) + goto out_nolock; + + if (!PIPE_READERS(*inode)) + goto sigpipe; } - PIPE_LOCK(*inode)++; - while (count>0 && (free = PIPE_FREE(*inode))) { - chars = PIPE_MAX_WCHUNK(*inode); + } + + /* Copy into available space. */ + ret = -EFAULT; + while (count > 0) { + int space; + char *pipebuf = PIPE_BASE(*inode) + PIPE_END(*inode); + ssize_t chars = PIPE_MAX_WCHUNK(*inode); + + if ((space = PIPE_FREE(*inode)) != 0) { if (chars > count) chars = count; - if (chars > free) - chars = free; - pipebuf = PIPE_BASE(*inode)+PIPE_END(*inode); + if (chars > space) + chars = space; + + if (copy_from_user(pipebuf, buf, chars)) + goto out; + written += chars; PIPE_LEN(*inode) += chars; count -= chars; - copy_from_user(pipebuf, buf, chars ); buf += chars; + space = PIPE_FREE(*inode); + continue; } - PIPE_LOCK(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - free = 1; - } - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty(inode); -errout: - up(&inode->i_sem); - return written ? written : err; -} -static ssize_t pipe_read(struct file * filp, char * buf, size_t count, loff_t *ppos) -{ - ssize_t retval; - - - if (ppos != &filp->f_pos) - return -ESPIPE; - - if ( !count ) return 0; + ret = written; + if (filp->f_flags & O_NONBLOCK) + break; + + do { + /* This should be a synchronous wake-up: don't do idle reschedules! */ + wake_up_interruptible(PIPE_WAIT(*inode)); + pipe_wait(inode); + if (signal_pending(current)) + goto out_nolock; + if (down_interruptible(PIPE_SEM(*inode))) + goto out_nolock; + if (!PIPE_READERS(*inode)) + goto sigpipe; + } while (!PIPE_FREE(*inode)); + ret = -EFAULT; + } - lock_kernel(); - retval = do_pipe_read(filp, buf, count); - unlock_kernel(); - return retval; -} + /* Signal readers there is more data. */ + wake_up_interruptible(PIPE_WAIT(*inode)); -static ssize_t pipe_write(struct file * filp, const char * buf, size_t count, loff_t *ppos) -{ - ssize_t retval; + ret = (written ? written : -EAGAIN); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); - if (ppos != &filp->f_pos) - return -ESPIPE; +out: + up(PIPE_SEM(*inode)); +out_nolock: + return ret; - lock_kernel(); - retval = do_pipe_write(filp, buf, count); - unlock_kernel(); - return retval; +sigpipe: + up(PIPE_SEM(*inode)); + send_sig(SIGPIPE, current, 0); + return -EPIPE; } -static long long pipe_lseek(struct file * file, long long offset, int orig) +static loff_t +pipe_lseek(struct file *file, loff_t offset, int orig) { return -ESPIPE; } -static ssize_t bad_pipe_r(struct file * filp, char * buf, - size_t count, loff_t *ppos) +static ssize_t +bad_pipe_r(struct file *filp, char *buf, size_t count, loff_t *ppos) { return -EBADF; } -static ssize_t bad_pipe_w(struct file * filp, const char * buf, - size_t count, loff_t *ppos) +static ssize_t +bad_pipe_w(struct file *filp, const char *buf, size_t count, loff_t *ppos) { return -EBADF; } -static int pipe_ioctl(struct inode *pino, struct file * filp, - unsigned int cmd, unsigned long arg) +static int +pipe_ioctl(struct inode *pino, struct file *filp, + unsigned int cmd, unsigned long arg) { switch (cmd) { case FIONREAD: - return put_user(PIPE_SIZE(*pino),(int *) arg); + return put_user(PIPE_LEN(*pino), (int *)arg); default: return -EINVAL; } } -static unsigned int pipe_poll(struct file * filp, poll_table * wait) +static unsigned int +pipe_poll(struct file *filp, poll_table *wait) { unsigned int mask; - struct inode * inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; + + poll_wait(filp, PIPE_WAIT(*inode), wait); - poll_wait(filp, &PIPE_WAIT(*inode), wait); + /* Reading only -- no need for aquiring the semaphore. */ mask = POLLIN | POLLRDNORM; if (PIPE_EMPTY(*inode)) mask = POLLOUT | POLLWRNORM; @@ -210,6 +281,7 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait) mask |= POLLHUP; if (!PIPE_READERS(*inode)) mask |= POLLERR; + return mask; } @@ -218,17 +290,21 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait) * Argh! Why does SunOS have to have different select() behaviour * for pipes and FIFOs? Hate, hate, hate! SunOS lacks POLLHUP. */ -static unsigned int fifo_poll(struct file * filp, poll_table * wait) +static unsigned int +fifo_poll(struct file *filp, poll_table *wait) { unsigned int mask; - struct inode * inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; + + poll_wait(filp, PIPE_WAIT(*inode), wait); - poll_wait(filp, &PIPE_WAIT(*inode), wait); + /* Reading only -- no need for aquiring the semaphore. */ mask = POLLIN | POLLRDNORM; if (PIPE_EMPTY(*inode)) mask = POLLOUT | POLLWRNORM; if (!PIPE_READERS(*inode)) mask |= POLLERR; + return mask; } #else @@ -242,82 +318,112 @@ static unsigned int fifo_poll(struct file * filp, poll_table * wait) * the open() code hasn't guaranteed a connection (O_NONBLOCK), * and we need to act differently until we do get a writer.. */ -static ssize_t connect_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) +static ssize_t +connect_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { - struct inode * inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; + + /* Reading only -- no need for aquiring the semaphore. */ if (PIPE_EMPTY(*inode) && !PIPE_WRITERS(*inode)) return 0; + filp->f_op = &read_fifo_fops; - return pipe_read(filp,buf,count,ppos); + return pipe_read(filp, buf, count, ppos); } -static unsigned int connect_poll(struct file * filp, poll_table * wait) +static unsigned int +connect_poll(struct file *filp, poll_table *wait) { - struct inode * inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; + unsigned int mask = 0; + + poll_wait(filp, PIPE_WAIT(*inode), wait); - poll_wait(filp, &PIPE_WAIT(*inode), wait); + /* Reading only -- no need for aquiring the semaphore. */ if (!PIPE_EMPTY(*inode)) { filp->f_op = &read_fifo_fops; - return POLLIN | POLLRDNORM; - } - if (PIPE_WRITERS(*inode)) + mask = POLLIN | POLLRDNORM; + } else if (PIPE_WRITERS(*inode)) { filp->f_op = &read_fifo_fops; - return POLLOUT | POLLWRNORM; + mask = POLLOUT | POLLWRNORM; + } + + return mask; } -static int pipe_release(struct inode * inode) +static int +pipe_release(struct inode *inode, int decr, int decw) { + down(PIPE_SEM(*inode)); + PIPE_READERS(*inode) -= decr; + PIPE_WRITERS(*inode) -= decw; if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { struct pipe_inode_info *info = inode->i_pipe; inode->i_pipe = NULL; free_page((unsigned long) info->base); kfree(info); - return 0; + } else { + wake_up_interruptible(PIPE_WAIT(*inode)); } - wake_up_interruptible(&PIPE_WAIT(*inode)); + up(PIPE_SEM(*inode)); + return 0; } -static int pipe_read_release(struct inode * inode, struct file * filp) +static int +pipe_read_release(struct inode *inode, struct file *filp) { - PIPE_READERS(*inode)--; - return pipe_release(inode); + return pipe_release(inode, 1, 0); } -static int pipe_write_release(struct inode * inode, struct file * filp) +static int +pipe_write_release(struct inode *inode, struct file *filp) { - PIPE_WRITERS(*inode)--; - return pipe_release(inode); + return pipe_release(inode, 0, 1); } -static int pipe_rdwr_release(struct inode * inode, struct file * filp) +static int +pipe_rdwr_release(struct inode *inode, struct file *filp) { - if (filp->f_mode & FMODE_READ) - PIPE_READERS(*inode)--; - if (filp->f_mode & FMODE_WRITE) - PIPE_WRITERS(*inode)--; - return pipe_release(inode); + int decr, decw; + + decr = (filp->f_mode & FMODE_READ) != 0; + decw = (filp->f_mode & FMODE_WRITE) != 0; + return pipe_release(inode, decr, decw); } -static int pipe_read_open(struct inode * inode, struct file * filp) +static int +pipe_read_open(struct inode *inode, struct file *filp) { + /* We could have perhaps used atomic_t, but this and friends + below are the only places. So it doesn't seem worthwhile. */ + down(PIPE_SEM(*inode)); PIPE_READERS(*inode)++; + up(PIPE_SEM(*inode)); + return 0; } -static int pipe_write_open(struct inode * inode, struct file * filp) +static int +pipe_write_open(struct inode *inode, struct file *filp) { + down(PIPE_SEM(*inode)); PIPE_WRITERS(*inode)++; + up(PIPE_SEM(*inode)); + return 0; } -static int pipe_rdwr_open(struct inode * inode, struct file * filp) +static int +pipe_rdwr_open(struct inode *inode, struct file *filp) { + down(PIPE_SEM(*inode)); if (filp->f_mode & FMODE_READ) PIPE_READERS(*inode)++; if (filp->f_mode & FMODE_WRITE) PIPE_WRITERS(*inode)++; + up(PIPE_SEM(*inode)); + return 0; } @@ -433,22 +539,20 @@ static struct inode * get_pipe_inode(void) goto fail_inode; page = __get_free_page(GFP_USER); - if (!page) goto fail_iput; - /* XXX */ inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); if (!inode->i_pipe) goto fail_page; - PIPE_BASE(*inode) = (char *) page; inode->i_op = &pipe_inode_operations; - init_waitqueue_head(&PIPE_WAIT(*inode)); + + init_waitqueue_head(PIPE_WAIT(*inode)); + PIPE_BASE(*inode) = (char *) page; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - PIPE_LOCK(*inode) = 0; + /* * Mark the inode dirty from the very beginning, * that way it will never be moved to the dirty diff --git a/fs/proc/array.c b/fs/proc/array.c index f108511fb..e2775ae0d 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -42,6 +42,10 @@ * Alan Cox : security fixes. * <Alan.Cox@linux.org> * + * Al Viro : safe handling of mm_struct + * + * Gerhard Wichert : added BIGMEM support + * Siemens AG <Gerhard.Wichert@pdb.siemens.de> */ #include <linux/types.h> @@ -76,6 +80,11 @@ int get_malloc(char * buffer); #endif +static int open_kcore(struct inode * inode, struct file * filp) +{ + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + static ssize_t read_core(struct file * file, char * buf, size_t count, loff_t *ppos) { @@ -93,6 +102,9 @@ static ssize_t read_core(struct file * file, char * buf, memset(&dump, 0, sizeof(struct user)); dump.magic = CMAGIC; dump.u_dsize = max_mapnr; +#if defined (__i386__) + dump.start_code = PAGE_OFFSET; +#endif #ifdef __alpha__ dump.start_data = PAGE_OFFSET; #endif @@ -137,6 +149,12 @@ static ssize_t read_core(struct file * file, char * buf, static struct file_operations proc_kcore_operations = { NULL, /* lseek */ read_core, + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + open_kcore }; struct inode_operations proc_kcore_inode_operations = { @@ -223,7 +241,7 @@ static int get_loadavg(char * buffer) LOAD_INT(a), LOAD_FRAC(a), LOAD_INT(b), LOAD_FRAC(b), LOAD_INT(c), LOAD_FRAC(c), - nr_running, nr_tasks, last_pid); + nr_running, nr_threads, last_pid); } static int get_kstat(char * buffer) @@ -231,9 +249,8 @@ static int get_kstat(char * buffer) int i, len; unsigned sum = 0; extern unsigned long total_forks; - unsigned long ticks; + unsigned long jif = HZ_TO_STD(jiffies); - ticks = HZ_TO_STD(jiffies) * smp_num_cpus; for (i = 0 ; i < NR_IRQS ; i++) sum += kstat_irqs(i); @@ -243,14 +260,14 @@ static int get_kstat(char * buffer) kstat.cpu_user, kstat.cpu_nice, kstat.cpu_system, - jiffies*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system)); + jif*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), for (i = 0 ; i < smp_num_cpus; i++) len += sprintf(buffer + len, "cpu%d %u %u %u %lu\n", i, kstat.per_cpu_user[cpu_logical_map(i)], kstat.per_cpu_nice[cpu_logical_map(i)], kstat.per_cpu_system[cpu_logical_map(i)], - jiffies - ( kstat.per_cpu_user[cpu_logical_map(i)] \ + jif - ( kstat.per_cpu_user[cpu_logical_map(i)] \ + kstat.per_cpu_nice[cpu_logical_map(i)] \ + kstat.per_cpu_system[cpu_logical_map(i)])); len += sprintf(buffer + len, @@ -276,7 +293,7 @@ static int get_kstat(char * buffer) HZ_TO_STD(kstat.cpu_user), HZ_TO_STD(kstat.cpu_nice), HZ_TO_STD(kstat.cpu_system), - ticks - HZ_TO_STD(kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), + jif*smp_num_cpus - HZ_TO_STD(kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), #endif kstat.dk_drive[0], kstat.dk_drive[1], kstat.dk_drive[2], kstat.dk_drive[3], @@ -300,7 +317,7 @@ static int get_kstat(char * buffer) "btime %lu\n" "processes %lu\n", kstat.context_swtch, - xtime.tv_sec - jiffies / HZ, + xtime.tv_sec - jif / HZ, total_forks); return len; } @@ -312,7 +329,7 @@ static int get_uptime(char * buffer) unsigned long idle; uptime = jiffies; - idle = task[0]->times.tms_utime + task[0]->times.tms_stime; + idle = init_tasks[0]->times.tms_utime + init_tasks[0]->times.tms_stime; /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but that would overflow about every five days at HZ == 100. @@ -348,7 +365,7 @@ static int get_meminfo(char * buffer) len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" "Swap: %8lu %8lu %8lu\n", - i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, atomic_read(&page_cache_size)*PAGE_SIZE, + i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, (unsigned long) atomic_read(&page_cache_size)*PAGE_SIZE, i.totalswap, i.totalswap-i.freeswap, i.freeswap); /* * Tagged format, for easy grepping and expansion. The above will go away @@ -360,6 +377,8 @@ static int get_meminfo(char * buffer) "MemShared: %8lu kB\n" "Buffers: %8lu kB\n" "Cached: %8u kB\n" + "BigTotal: %8lu kB\n" + "BigFree: %8lu kB\n" "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n", i.totalram >> 10, @@ -367,6 +386,8 @@ static int get_meminfo(char * buffer) i.sharedram >> 10, i.bufferram >> 10, atomic_read(&page_cache_size) << (PAGE_SHIFT - 10), + i.totalbig >> 10, + i.freebig >> 10, i.totalswap >> 10, i.freeswap >> 10); } @@ -386,21 +407,15 @@ static int get_cmdline(char * buffer) return sprintf(buffer, "%s\n", saved_command_line); } -static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) +static unsigned long get_phys_addr(struct mm_struct * mm, unsigned long ptr) { pgd_t *page_dir; pmd_t *page_middle; pte_t pte; - if (!p || !p->mm || ptr >= TASK_SIZE) + if (ptr >= TASK_SIZE) return 0; - /* Check for NULL pgd .. shouldn't happen! */ - if (!p->mm->pgd) { - printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid); - return 0; - } - - page_dir = pgd_offset(p->mm,ptr); + page_dir = pgd_offset(mm,ptr); if (pgd_none(*page_dir)) return 0; if (pgd_bad(*page_dir)) { @@ -422,7 +437,9 @@ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) return pte_page(pte) + (ptr & ~PAGE_MASK); } -static int get_array(struct task_struct *p, unsigned long start, unsigned long end, char * buffer) +#include <linux/bigmem.h> + +static int get_array(struct mm_struct *mm, unsigned long start, unsigned long end, char * buffer) { unsigned long addr; int size = 0, result = 0; @@ -431,49 +448,68 @@ static int get_array(struct task_struct *p, unsigned long start, unsigned long e if (start >= end) return result; for (;;) { - addr = get_phys_addr(p, start); + addr = get_phys_addr(mm, start); if (!addr) return result; + addr = kmap(addr, KM_READ); do { c = *(char *) addr; if (!c) result = size; if (size < PAGE_SIZE) buffer[size++] = c; - else + else { + kunmap(addr, KM_READ); return result; + } addr++; start++; - if (!c && start >= end) + if (!c && start >= end) { + kunmap(addr, KM_READ); return result; + } } while (addr & ~PAGE_MASK); + kunmap(addr, KM_READ); } return result; } -static int get_env(int pid, char * buffer) +static struct mm_struct *get_mm(int pid) { struct task_struct *p; + struct mm_struct *mm = NULL; read_lock(&tasklist_lock); p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + if (p) + mm = p->mm; + if (mm) + atomic_inc(&mm->mm_users); + read_unlock(&tasklist_lock); + return mm; +} - if (!p || !p->mm) - return 0; - return get_array(p, p->mm->env_start, p->mm->env_end, buffer); + +static int get_env(int pid, char * buffer) +{ + struct mm_struct *mm = get_mm(pid); + int res = 0; + if (mm) { + res = get_array(mm, mm->env_start, mm->env_end, buffer); + mmput(mm); + } + return res; } static int get_arg(int pid, char * buffer) { - struct task_struct *p; - - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - if (!p || !p->mm) - return 0; - return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer); + struct mm_struct *mm = get_mm(pid); + int res = 0; + if (mm) { + res = get_array(mm, mm->arg_start, mm->arg_end, buffer); + mmput(mm); + } + return res; } /* @@ -495,13 +531,13 @@ static unsigned long get_wchan(struct task_struct *p) int count = 0; stack_page = (unsigned long)p; - esp = p->tss.esp; - if (!stack_page || esp < stack_page || esp >= 8188+stack_page) + esp = p->thread.esp; + if (!stack_page || esp < stack_page || esp > 8188+stack_page) return 0; /* include/asm-i386/system.h:switch_to() pushes ebp last. */ ebp = *(unsigned long *) esp; do { - if (ebp < stack_page || ebp >= 8188+stack_page) + if (ebp < stack_page || ebp > 8184+stack_page) return 0; eip = *(unsigned long *) (ebp+4); if (eip < first_sched || eip >= last_sched) @@ -523,9 +559,9 @@ static unsigned long get_wchan(struct task_struct *p) unsigned long schedule_frame; unsigned long pc; - pc = thread_saved_pc(&p->tss); + pc = thread_saved_pc(&p->thread); if (pc >= first_sched && pc < last_sched) { - schedule_frame = ((unsigned long *)p->tss.ksp)[6]; + schedule_frame = ((unsigned long *)p->thread.ksp)[6]; return ((unsigned long *)schedule_frame)[12]; } return pc; @@ -538,9 +574,9 @@ static unsigned long get_wchan(struct task_struct *p) unsigned long schedule_frame; unsigned long pc; - pc = thread_saved_pc(&p->tss); + pc = thread_saved_pc(&p->thread); if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) { - schedule_frame = ((unsigned long *)(long)p->tss.reg30)[16]; + schedule_frame = ((unsigned long *)(long)p->thread.reg30)[16]; return (unsigned long)((unsigned long *)schedule_frame)[11]; } return pc; @@ -552,7 +588,7 @@ static unsigned long get_wchan(struct task_struct *p) int count = 0; stack_page = (unsigned long)p; - fp = ((struct switch_stack *)p->tss.ksp)->a6; + fp = ((struct switch_stack *)p->thread.ksp)->a6; do { if (fp < stack_page+sizeof(struct task_struct) || fp >= 8184+stack_page) @@ -570,7 +606,7 @@ static unsigned long get_wchan(struct task_struct *p) unsigned long stack_page = (unsigned long) p; int count = 0; - sp = p->tss.ksp; + sp = p->thread.ksp; do { sp = *(unsigned long *)sp; if (sp < stack_page || sp >= stack_page + 8188) @@ -609,7 +645,7 @@ static unsigned long get_wchan(struct task_struct *p) #ifdef __sparc_v9__ bias = STACK_BIAS; #endif - fp = p->tss.ksp + bias; + fp = p->thread.ksp + bias; do { /* Bogus frame pointer? */ if (fp < (task_base + sizeof(struct task_struct)) || @@ -638,7 +674,7 @@ static unsigned long get_wchan(struct task_struct *p) + (long)&((struct pt_regs *)0)->reg) # define KSTK_EIP(tsk) \ (*(unsigned long *)(PT_REG(pc) + PAGE_SIZE + (unsigned long)(tsk))) -# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) +# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) #elif defined(__arm__) # define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) # define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020]) @@ -646,26 +682,29 @@ static unsigned long get_wchan(struct task_struct *p) #define KSTK_EIP(tsk) \ ({ \ unsigned long eip = 0; \ - if ((tsk)->tss.esp0 > PAGE_SIZE && \ - MAP_NR((tsk)->tss.esp0) < max_mapnr) \ - eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc; \ + if ((tsk)->thread.esp0 > PAGE_SIZE && \ + MAP_NR((tsk)->thread.esp0) < max_mapnr) \ + eip = ((struct pt_regs *) (tsk)->thread.esp0)->pc; \ eip; }) -#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) +#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) #elif defined(__powerpc__) -#define KSTK_EIP(tsk) ((tsk)->tss.regs->nip) -#define KSTK_ESP(tsk) ((tsk)->tss.regs->gpr[1]) +#define KSTK_EIP(tsk) ((tsk)->thread.regs->nip) +#define KSTK_ESP(tsk) ((tsk)->thread.regs->gpr[1]) #elif defined (__sparc_v9__) -# define KSTK_EIP(tsk) ((tsk)->tss.kregs->tpc) -# define KSTK_ESP(tsk) ((tsk)->tss.kregs->u_regs[UREG_FP]) +# define KSTK_EIP(tsk) ((tsk)->thread.kregs->tpc) +# define KSTK_ESP(tsk) ((tsk)->thread.kregs->u_regs[UREG_FP]) #elif defined(__sparc__) -# define KSTK_EIP(tsk) ((tsk)->tss.kregs->pc) -# define KSTK_ESP(tsk) ((tsk)->tss.kregs->u_regs[UREG_FP]) +# define KSTK_EIP(tsk) ((tsk)->thread.kregs->pc) +# define KSTK_ESP(tsk) ((tsk)->thread.kregs->u_regs[UREG_FP]) #elif defined(__mips__) # define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg \ - sizeof(struct pt_regs)) #define KSTK_TOS(tsk) ((unsigned long)(tsk) + KERNEL_STACK_SIZE - 32) # define KSTK_EIP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(cp0_epc))) # define KSTK_ESP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(regs[29]))) +#elif defined(__sh__) +# define KSTK_EIP(tsk) ((tsk)->thread.pc) +# define KSTK_ESP(tsk) ((tsk)->thread.sp) #endif /* Gcc optimizes away "strlen(x)" for constant x */ @@ -747,11 +786,13 @@ static inline char * task_state(struct task_struct *p, char *buffer) "PPid:\t%d\n" "Uid:\t%d\t%d\t%d\t%d\n" "Gid:\t%d\t%d\t%d\t%d\n" + "FDSize:\t%d\n" "Groups:\t", get_task_state(p), p->pid, p->p_pptr->pid, p->uid, p->euid, p->suid, p->fsuid, - p->gid, p->egid, p->sgid, p->fsgid); + p->gid, p->egid, p->sgid, p->fsgid, + p->files ? p->files->max_fds : 0); for (g = 0; g < p->ngroups; g++) buffer += sprintf(buffer, "%d ", p->groups[g]); @@ -760,46 +801,44 @@ static inline char * task_state(struct task_struct *p, char *buffer) return buffer; } -static inline char * task_mem(struct task_struct *p, char *buffer) +static inline char * task_mem(struct mm_struct *mm, char *buffer) { - struct mm_struct * mm = p->mm; - - if (mm && mm != &init_mm) { - struct vm_area_struct * vma = mm->mmap; - unsigned long data = 0, stack = 0; - unsigned long exec = 0, lib = 0; - - for (vma = mm->mmap; vma; vma = vma->vm_next) { - unsigned long len = (vma->vm_end - vma->vm_start) >> 10; - if (!vma->vm_file) { - data += len; - if (vma->vm_flags & VM_GROWSDOWN) - stack += len; - continue; - } - if (vma->vm_flags & VM_WRITE) + struct vm_area_struct * vma; + unsigned long data = 0, stack = 0; + unsigned long exec = 0, lib = 0; + + down(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + unsigned long len = (vma->vm_end - vma->vm_start) >> 10; + if (!vma->vm_file) { + data += len; + if (vma->vm_flags & VM_GROWSDOWN) + stack += len; + continue; + } + if (vma->vm_flags & VM_WRITE) + continue; + if (vma->vm_flags & VM_EXEC) { + exec += len; + if (vma->vm_flags & VM_EXECUTABLE) continue; - if (vma->vm_flags & VM_EXEC) { - exec += len; - if (vma->vm_flags & VM_EXECUTABLE) - continue; - lib += len; - } - } - buffer += sprintf(buffer, - "VmSize:\t%8lu kB\n" - "VmLck:\t%8lu kB\n" - "VmRSS:\t%8lu kB\n" - "VmData:\t%8lu kB\n" - "VmStk:\t%8lu kB\n" - "VmExe:\t%8lu kB\n" - "VmLib:\t%8lu kB\n", - mm->total_vm << (PAGE_SHIFT-10), - mm->locked_vm << (PAGE_SHIFT-10), - mm->rss << (PAGE_SHIFT-10), - data - stack, stack, - exec - lib, lib); + lib += len; + } } + buffer += sprintf(buffer, + "VmSize:\t%8lu kB\n" + "VmLck:\t%8lu kB\n" + "VmRSS:\t%8lu kB\n" + "VmData:\t%8lu kB\n" + "VmStk:\t%8lu kB\n" + "VmExe:\t%8lu kB\n" + "VmLib:\t%8lu kB\n", + mm->total_vm << (PAGE_SHIFT-10), + mm->locked_vm << (PAGE_SHIFT-10), + mm->rss << (PAGE_SHIFT-10), + data - stack, stack, + exec - lib, lib); + up(&mm->mmap_sem); return buffer; } @@ -860,44 +899,61 @@ static int get_status(int pid, char * buffer) { char * orig = buffer; struct task_struct *tsk; + struct mm_struct *mm = NULL; read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); + if (tsk) + mm = tsk->mm; + if (mm) + atomic_inc(&mm->mm_users); read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!tsk) return 0; buffer = task_name(tsk, buffer); buffer = task_state(tsk, buffer); - buffer = task_mem(tsk, buffer); + if (mm) + buffer = task_mem(mm, buffer); buffer = task_sig(tsk, buffer); buffer = task_cap(tsk, buffer); + if (mm) + mmput(mm); return buffer - orig; } static int get_stat(int pid, char * buffer) { struct task_struct *tsk; + struct mm_struct *mm = NULL; unsigned long vsize, eip, esp, wchan; long priority, nice; int tty_pgrp; sigset_t sigign, sigcatch; char state; + int res; read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); + if (tsk) + mm = tsk->mm; + if (mm) + atomic_inc(&mm->mm_users); read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!tsk) return 0; state = *get_task_state(tsk); vsize = eip = esp = 0; - if (tsk->mm && tsk->mm != &init_mm) { - struct vm_area_struct *vma = tsk->mm->mmap; + if (mm) { + struct vm_area_struct *vma; + down(&mm->mmap_sem); + vma = mm->mmap; while (vma) { vsize += vma->vm_end - vma->vm_start; vma = vma->vm_next; } eip = KSTK_EIP(tsk); esp = KSTK_ESP(tsk); + up(&mm->mmap_sem); } wchan = get_wchan(tsk); @@ -916,7 +972,7 @@ static int get_stat(int pid, char * buffer) nice = tsk->priority; nice = 20 - (nice * 20 + DEF_PRIORITY / 2) / DEF_PRIORITY; - return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ + res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %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 %d %d\n", pid, @@ -942,11 +998,11 @@ static int get_stat(int pid, char * buffer) tsk->it_real_value, tsk->start_time, vsize, - tsk->mm ? tsk->mm->rss : 0, /* you might want to shift this left 3 */ + mm ? mm->rss : 0, /* you might want to shift this left 3 */ tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0, - tsk->mm ? tsk->mm->start_code : 0, - tsk->mm ? tsk->mm->end_code : 0, - tsk->mm ? tsk->mm->start_stack : 0, + mm ? mm->start_code : 0, + mm ? mm->end_code : 0, + mm ? mm->start_stack : 0, esp, eip, /* The signal information here is obsolete. @@ -962,6 +1018,9 @@ static int get_stat(int pid, char * buffer) tsk->cnswap, tsk->exit_signal, tsk->processor); + if (mm) + mmput(mm); + return res; } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -1039,19 +1098,15 @@ static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long en static int get_statm(int pid, char * buffer) { - struct task_struct *tsk; + struct mm_struct *mm = get_mm(pid); int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0; - read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - if (!tsk) - return 0; - if (tsk->mm && tsk->mm != &init_mm) { - struct vm_area_struct * vma = tsk->mm->mmap; - + if (mm) { + struct vm_area_struct * vma; + down(&mm->mmap_sem); + vma = mm->mmap; while (vma) { - pgd_t *pgd = pgd_offset(tsk->mm, vma->vm_start); + pgd_t *pgd = pgd_offset(mm, 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); @@ -1069,6 +1124,8 @@ static int get_statm(int pid, char * buffer) drs += pages; vma = vma->vm_next; } + up(&mm->mmap_sem); + mmput(mm); } return sprintf(buffer,"%d %d %d %d %d %d %d\n", size, resident, share, trs, lrs, drs, dt); @@ -1133,11 +1190,11 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, if (!p) goto freepage_out; - if (!p->mm || p->mm == &init_mm || count == 0) + if (!p->mm || count == 0) goto getlen_out; /* Check whether the mmaps could change if we sleep */ - volatile_task = (p != current || atomic_read(&p->mm->count) > 1); + volatile_task = (p != current || atomic_read(&p->mm->mm_users) > 1); /* decode f_pos */ lineno = *ppos >> MAPS_LINE_SHIFT; @@ -1296,7 +1353,7 @@ static long get_root_array(char * page, int type, char **start, case PROC_MEMINFO: return get_meminfo(page); -#ifdef CONFIG_PCI_OLD_PROC +#ifdef CONFIG_PCI case PROC_PCI: return get_pci_list(page); #endif @@ -1404,8 +1461,6 @@ static int process_unauthorized(int type, int pid) ok = p->dumpable; if(!cap_issubset(p->cap_permitted, current->cap_permitted)) ok=0; - if(!p->mm) /* Scooby scooby doo where are you ? */ - p=NULL; } read_unlock(&tasklist_lock); @@ -1413,8 +1468,7 @@ static int process_unauthorized(int type, int pid) if (!p) return 1; - switch(type) - { + switch(type) { case PROC_PID_STATUS: case PROC_PID_STATM: case PROC_PID_STAT: @@ -1481,8 +1535,7 @@ static ssize_t array_read(struct file * file, char * buf, start = NULL; dp = (struct proc_dir_entry *) inode->u.generic_ip; - if (pid && process_unauthorized(type, pid)) - { + if (pid && process_unauthorized(type, pid)) { free_page(page); return -EIO; } diff --git a/fs/proc/base.c b/fs/proc/base.c index 118e94956..f0a0febf8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -178,7 +178,7 @@ static struct proc_dir_entry proc_pid_cpu = { }; #endif -__initfunc(void proc_base_init(void)) +void __init proc_base_init(void) { #if CONFIG_AP1000 proc_register(&proc_pid, &proc_pid_ringbuf); diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 195ca41b8..1862f6f5f 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -90,6 +90,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) fd = 0; len = dentry->d_name.len; name = dentry->d_name.name; + if (len > 1 && *name == '0') goto out; while (len-- > 0) { c = *name - '0'; name++; @@ -133,7 +134,7 @@ out: static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; - struct task_struct * p, **tarrayp; + struct task_struct *p, *tmp; unsigned int fd, pid, ino; int retval; char buf[NUMBUF]; @@ -157,7 +158,6 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) p = find_task_by_pid(pid); if (!p) goto out_unlock; - tarrayp = p->tarray_ptr; for (fd -= 2 ; p->files && fd < p->files->max_fds; fd++, filp->f_pos++) { @@ -182,8 +182,13 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) goto out; read_lock(&tasklist_lock); - /* filldir() might have slept, so we must re-validate "p" */ - if (p != *tarrayp || p->pid != pid) + /* + * filldir() might have slept, so we must + * re-validate "p". This is fast enough due + * to the pidhash + */ + tmp = find_task_by_pid(pid); + if (p != tmp) break; } out_unlock: diff --git a/fs/proc/link.c b/fs/proc/link.c index 6f5c63ec3..69d435600 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -82,60 +82,70 @@ static struct dentry * proc_follow_link(struct dentry *dentry, ino &= 0x0000ffff; result = ERR_PTR(-ENOENT); - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (!p) - goto out_unlock; switch (ino) { case PROC_PID_CWD: - if (!p->fs || !p->fs->pwd) - goto out_unlock; - result = p->fs->pwd; - goto out_dget; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (p && p->fs && p->fs->pwd) + result = dget(p->fs->pwd); + read_unlock(&tasklist_lock); + break; case PROC_PID_ROOT: - if (!p->fs || !p->fs->root) - goto out_unlock; - result = p->fs->root; - goto out_dget; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (p && p->fs && p->fs->root) + result = dget(p->fs->root); + read_unlock(&tasklist_lock); + break; case PROC_PID_EXE: { + struct mm_struct *mm = NULL; struct vm_area_struct * vma; - if (!p->mm) - goto out_unlock; - vma = p->mm->mmap; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (p) + mm = p->mm; + if (mm) + atomic_inc(&mm->mm_users); + read_unlock(&tasklist_lock); + if (!mm) + break; + down(&mm->mmap_sem); + vma = mm->mmap; while (vma) { if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { - result = vma->vm_file->f_dentry; - goto out_dget; + result = dget(vma->vm_file->f_dentry); + break; } vma = vma->vm_next; } - goto out_unlock; + up(&mm->mmap_sem); + mmput(mm); + break; } default: if (ino & PROC_PID_FD_DIR) { struct file * file; + struct files_struct *files = NULL; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (p) + files = p->files; + read_unlock(&tasklist_lock); + if (!files) + break; ino &= 0x7fff; - if (!p->files) /* shouldn't happen here */ - goto out_unlock; - read_lock(&p->files->file_lock); - file = fcheck_task(p, ino); - if (!file || !file->f_dentry) - goto out_unlock; - result = file->f_dentry; + read_lock(&files->file_lock); + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + if (ino < files->max_fds && + (file = files->fd[ino]) && file->f_dentry) + result = dget(file->f_dentry); read_unlock(&p->files->file_lock); - goto out_dget; } } -out_dget: - result = dget(result); - -out_unlock: - read_unlock(&tasklist_lock); - out: return result; } diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 4d599c77b..f9fcb0970 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/proc_fs.h> +#include <linux/bigmem.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -120,7 +121,9 @@ static ssize_t mem_read(struct file * file, char * buf, i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > scount) i = scount; + page = (char *) kmap((unsigned long) page, KM_READ); copy_to_user(tmp, page, i); + kunmap((unsigned long) page, KM_READ); addr += i; tmp += i; scount -= i; @@ -177,7 +180,9 @@ static ssize_t mem_write(struct file * file, char * buf, i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; + page = (unsigned long) kmap((unsigned long) page, KM_WRITE); copy_from_user(page, tmp, i); + kunmap((unsigned long) page, KM_WRITE); addr += i; tmp += i; count -= i; diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index 72bb194cf..c9553ea24 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,4 +1,4 @@ -/* $Id: openpromfs.c,v 1.33 1999/04/28 11:57:33 davem Exp $ +/* $Id: openpromfs.c,v 1.36 1999/08/31 07:01:03 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -990,7 +990,7 @@ static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) /* {{{ init section */ #ifndef MODULE -__initfunc(static int check_space (u16 n)) +static int __init check_space (u16 n) #else static int check_space (u16 n) #endif @@ -1014,7 +1014,7 @@ static int check_space (u16 n) } #ifndef MODULE -__initfunc(static u16 get_nodes (u16 parent, u32 node)) +static u16 __init get_nodes (u16 parent, u32 node) #else static u16 get_nodes (u16 parent, u32 node) #endif @@ -1132,7 +1132,7 @@ void openpromfs_use (struct inode *inode, int inc) #ifndef MODULE #define RET(x) -__initfunc(void openpromfs_init (void)) +void __init openpromfs_init (void) #else EXPORT_NO_SYMBOLS; diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 5ba245615..4b4ca41b8 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -170,7 +170,7 @@ void proc_tty_unregister_driver(struct tty_driver *driver) /* * Called by proc_root_init() to initialize the /proc/tty subtree */ -__initfunc(void proc_tty_init(void)) +void __init proc_tty_init(void) { struct proc_dir_entry *ent; diff --git a/fs/proc/root.c b/fs/proc/root.c index df3c0b049..65ef929e7 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -641,6 +641,14 @@ struct proc_dir_entry proc_root_fs = { NULL, NULL, NULL }; +struct proc_dir_entry proc_root_driver = { + PROC_DRIVER, 6, "driver", + S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, + 0, &proc_dir_inode_operations, + NULL, NULL, + NULL, + NULL, NULL +}; static struct proc_dir_entry proc_root_dma = { PROC_DMA, 3, "dma", S_IFREG | S_IRUGO, 1, 0, 0, @@ -651,8 +659,8 @@ static struct proc_dir_entry proc_root_ioports = { S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; -static struct proc_dir_entry proc_root_memory = { - PROC_MEMORY, 6, "memory", +static struct proc_dir_entry proc_root_iomem = { + PROC_MEMORY, 5, "iomem", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; @@ -711,7 +719,7 @@ static struct proc_dir_entry proc_root_ppc_htab = { }; #endif -__initfunc(void proc_root_init(void)) +void __init proc_root_init(void) { proc_base_init(); proc_register(&proc_root, &proc_root_loadavg); @@ -745,13 +753,14 @@ __initfunc(void proc_root_init(void)) #endif proc_register(&proc_root, &proc_root_stat); proc_register(&proc_root, &proc_root_devices); + proc_register(&proc_root, &proc_root_driver); proc_register(&proc_root, &proc_root_partitions); proc_register(&proc_root, &proc_root_interrupts); proc_register(&proc_root, &proc_root_filesystems); proc_register(&proc_root, &proc_root_fs); proc_register(&proc_root, &proc_root_dma); proc_register(&proc_root, &proc_root_ioports); - proc_register(&proc_root, &proc_root_memory); + proc_register(&proc_root, &proc_root_iomem); proc_register(&proc_root, &proc_root_cmdline); #ifdef CONFIG_RTC proc_register(&proc_root, &proc_root_rtc); @@ -859,14 +868,29 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr int len; if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */ - dir->i_nlink = proc_root.nlink; - - read_lock(&tasklist_lock); - for_each_task(p) { - if (p->pid) - dir->i_nlink++; + extern unsigned long total_forks; + static int last_timestamp = 0; + + /* + * this one can be a serious 'ps' performance problem if + * there are many threads running - thus we do 'lazy' + * link-recalculation - we change it only if the number + * of threads has increased. + */ + if (total_forks != last_timestamp) { + int nlink = proc_root.nlink; + + read_lock(&tasklist_lock); + last_timestamp = total_forks; + for_each_task(p) + nlink++; + read_unlock(&tasklist_lock); + /* + * subtract the # of idle threads which + * do not show up in /proc: + */ + dir->i_nlink = nlink - smp_num_cpus; } - read_unlock(&tasklist_lock); } if (!proc_lookup(dir, dentry)) diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 0f5262301..3acd5e028 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -27,7 +27,6 @@ #define QNX4_VERSION 4 #define QNX4_BMNAME ".bitmap" -#define CHECK_BOOT_SIGNATURE 0 static struct super_operations qnx4_sops; @@ -257,9 +256,6 @@ static const char *qnx4_checkroot(struct super_block *s) int i, j; int found = 0; - if (s == NULL) { - return "no qnx4 filesystem (null superblock)."; - } if (*(s->u.qnx4_sb.sb->RootDir.di_fname) != '/') { return "no qnx4 filesystem (no root dir)."; } else { @@ -282,6 +278,8 @@ static const char *qnx4_checkroot(struct super_block *s) } } } + /* WAIT! s->u.qnx4_sb.BitMap points into bh->b_data + and now we release bh?? */ brelse(bh); if (found != 0) { break; @@ -299,9 +297,8 @@ static struct super_block *qnx4_read_super(struct super_block *s, { struct buffer_head *bh; kdev_t dev = s->s_dev; -#if CHECK_BOOT_SIGNATURE + struct inode *root; char *tmpc; -#endif const char *errmsg; MOD_INC_USE_COUNT; @@ -311,7 +308,9 @@ static struct super_block *qnx4_read_super(struct super_block *s, s->s_blocksize_bits = 9; s->s_dev = dev; -#if CHECK_BOOT_SIGNATURE + /* Check the boot signature. Since the qnx4 code is + dangerous, we should leave as quickly as possible + if we don't belong here... */ bh = bread(dev, 0, QNX4_BLOCK_SIZE); if (!bh) { printk("qnx4: unable to read the boot sector\n"); @@ -320,11 +319,12 @@ static struct super_block *qnx4_read_super(struct super_block *s, tmpc = (char *) bh->b_data; if (tmpc[4] != 'Q' || tmpc[5] != 'N' || tmpc[6] != 'X' || tmpc[7] != '4' || tmpc[8] != 'F' || tmpc[9] != 'S') { - printk("qnx4: wrong fsid in boot sector.\n"); + if (!silent) + printk("qnx4: wrong fsid in boot sector.\n"); goto out; } brelse(bh); -#endif + bh = bread(dev, 1, QNX4_BLOCK_SIZE); if (!bh) { printk("qnx4: unable to read the superblock\n"); @@ -337,23 +337,35 @@ static struct super_block *qnx4_read_super(struct super_block *s, #endif s->u.qnx4_sb.sb_buf = bh; s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data; - s->s_root = - d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK)); - if (s->s_root == NULL) { - printk("qnx4: get inode failed\n"); - goto out; - } + + + /* check before allocating dentries, inodes, .. */ errmsg = qnx4_checkroot(s); if (errmsg != NULL) { - printk("qnx4: %s\n", errmsg); + if (!silent) + printk("qnx4: %s\n", errmsg); goto out; } + + /* does root not have inode number QNX4_ROOT_INO ?? */ + root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); + if (!root) { + printk("qnx4: get inode failed\n"); + goto out; + } + + s->s_root = d_alloc_root(root); + if (s->s_root == NULL) + goto outi; + brelse(bh); unlock_super(s); s->s_dirt = 1; return s; + outi: + iput(root); out: brelse(bh); outnobh: @@ -430,7 +442,7 @@ static struct file_system_type qnx4_fs_type = NULL }; -__initfunc(int init_qnx4_fs(void)) +int __init init_qnx4_fs(void) { printk("QNX4 filesystem v0.2 registered.\n"); return register_filesystem(&qnx4_fs_type); diff --git a/fs/read_write.c b/fs/read_write.c index cf207fed0..81f6d1141 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -13,7 +13,7 @@ #include <asm/uaccess.h> -static loff_t default_llseek(struct file *file, loff_t offset, int origin) +loff_t default_llseek(struct file *file, loff_t offset, int origin) { long long retval; @@ -73,9 +73,9 @@ bad: } #if !defined(__alpha__) -asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, - unsigned long offset_low, loff_t * result, - unsigned int origin) +asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, + unsigned long offset_low, loff_t * result, + unsigned int origin) { int retval; struct file * file; diff --git a/fs/readdir.c b/fs/readdir.c index 305b1cc74..31bc56cc2 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -142,7 +142,7 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in return 0; } -asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) +asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count) { struct file * file; struct dentry * dentry; diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index e16802e97..a30f382a6 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -405,7 +405,7 @@ romfs_readpage(struct file * file, struct page * page) buf = page_address(page); /* hack? */ - page->owner = (int)current; + page->owner = current; offset = page->offset; if (offset < inode->i_size) { @@ -692,7 +692,7 @@ static struct file_system_type romfs_fs_type = { NULL }; -__initfunc(int init_romfs_fs(void)) +int __init init_romfs_fs(void) { return register_filesystem(&romfs_fs_type); } diff --git a/fs/select.c b/fs/select.c index 3f278187e..674cfdaa2 100644 --- a/fs/select.c +++ b/fs/select.c @@ -106,7 +106,7 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds) /* handle last in-complete long-word first */ set = ~(~0UL << (n & (__NFDBITS-1))); n /= __NFDBITS; - open_fds = current->files->open_fds.fds_bits+n; + open_fds = current->files->open_fds->fds_bits+n; max = 0; if (set) { set &= BITS(fds, n); @@ -174,7 +174,7 @@ int do_select(int n, fd_set_bits *fds, long *timeout) n = retval; retval = 0; for (;;) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); for (i = 0 ; i < n; i++) { unsigned long bit = BIT(i); unsigned long mask; @@ -237,7 +237,7 @@ out: #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -asmlinkage int +asmlinkage long sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { fd_set_bits fds; @@ -268,8 +268,8 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) if (n < 0) goto out_nofds; - if (n > KFDS_NR) - n = KFDS_NR; + if (n > current->files->max_fdset) + n = current->files->max_fdset; /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), @@ -337,7 +337,7 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, unsigned int j; struct pollfd * fdpnt; - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) { int fd; unsigned int mask; @@ -371,7 +371,7 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, return count; } -asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) +asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) { int i, fdcount, err, size; struct pollfd * fds, *fds1; @@ -380,7 +380,7 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) lock_kernel(); /* Do a sanity check on nfds ... */ err = -EINVAL; - if (nfds > NR_OPEN) + if (nfds > current->files->max_fds) goto out; if (timeout) { diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index cf3539823..419adf970 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -575,7 +575,7 @@ static struct file_system_type smb_fs_type = { NULL }; -__initfunc(int init_smb_fs(void)) +int __init init_smb_fs(void) { return register_filesystem(&smb_fs_type); } @@ -119,7 +119,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) +asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf) { struct dentry * dentry; int error; @@ -140,7 +140,7 @@ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) } #endif -asmlinkage int sys_newstat(char * filename, struct stat * statbuf) +asmlinkage long sys_newstat(char * filename, struct stat * statbuf) { struct dentry * dentry; int error; @@ -166,7 +166,7 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf) * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) +asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf) { struct dentry * dentry; int error; @@ -188,7 +188,7 @@ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) #endif -asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) +asmlinkage long sys_newlstat(char * filename, struct stat * statbuf) { struct dentry * dentry; int error; @@ -214,7 +214,7 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) +asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) { struct file * f; int err = -EBADF; @@ -235,7 +235,7 @@ asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) #endif -asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf) +asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf) { struct file * f; int err = -EBADF; @@ -254,7 +254,7 @@ asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf) return err; } -asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) +asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz) { struct dentry * dentry; int error; diff --git a/fs/super.c b/fs/super.c index f70815094..efd0a5972 100644 --- a/fs/super.c +++ b/fs/super.c @@ -256,7 +256,7 @@ static int fs_maxindex(void) /* * Whee.. Weird sysv syscall. */ -asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2) +asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2) { int retval = -EINVAL; @@ -417,7 +417,7 @@ void __wait_on_super(struct super_block * sb) add_wait_queue(&sb->s_wait, &wait); repeat: - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); if (sb->s_lock) { schedule(); goto repeat; @@ -474,7 +474,7 @@ restart: return NULL; } -asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) +asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf) { struct super_block *s; struct ustat tmp; @@ -768,7 +768,7 @@ out: * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD */ -asmlinkage int sys_umount(char * name, int flags) +asmlinkage long sys_umount(char * name, int flags) { struct dentry * dentry; int retval; @@ -808,7 +808,7 @@ asmlinkage int sys_umount(char * name, int flags) * The 2.0 compatible umount. No flags. */ -asmlinkage int sys_oldumount(char * name) +asmlinkage long sys_oldumount(char * name) { return sys_umount(name,0); } @@ -1018,8 +1018,8 @@ static int copy_mount_options (const void * data, unsigned long *where) * aren't used, as the syscall assumes we are talking to an older * version that didn't understand them. */ -asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, - unsigned long new_flags, void * data) +asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void * data) { struct file_system_type * fstype; struct dentry * dentry = NULL; @@ -1166,6 +1166,7 @@ void __init mount_root(void) if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; + extern void rd_load_secondary(void); #endif floppy_eject(); #ifndef CONFIG_BLK_DEV_RAM diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index ff10e778d..4e70122c3 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -1174,7 +1174,7 @@ static struct file_system_type sysv_fs_type[3] = { {"coherent", FS_REQUIRES_DEV, sysv_read_super, NULL} }; -__initfunc(int init_sysv_fs(void)) +int __init init_sysv_fs(void) { int i; int ouch; diff --git a/fs/udf/.cvsignore b/fs/udf/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/udf/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/udf/Makefile b/fs/udf/Makefile new file mode 100644 index 000000000..b1fcbed1f --- /dev/null +++ b/fs/udf/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the linux udf-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .o file). +# +# Note 2! The CFLAGS definitions are now in the main makefile.. + +O_TARGET := udf.o +O_OBJS := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \ + partition.o super.o truncate.o symlink.o fsync.o \ + crc.o directory.o misc.o udftime.o unicode.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c new file mode 100644 index 000000000..3bcda123c --- /dev/null +++ b/fs/udf/balloc.c @@ -0,0 +1,441 @@ +/* + * balloc.c + * + * PURPOSE + * Block allocation handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/udf_fs.h> + +#include <asm/bitops.h> + +#include "udf_i.h" +#include "udf_sb.h" + +static int read_block_bitmap(struct super_block * sb, unsigned int block, + unsigned long bitmap_nr) +{ + struct buffer_head *bh = NULL; + int retval = 0; + lb_addr loc; + + loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap; + loc.partitionReferenceNum = UDF_SB_PARTITION(sb); + + bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block), sb->s_blocksize); + if (!bh) + { + retval = -EIO; + } + UDF_SB_BLOCK_BITMAP_NUMBER(sb, bitmap_nr) = block; + UDF_SB_BLOCK_BITMAP(sb, bitmap_nr) = bh; + return retval; +} + +static int load__block_bitmap(struct super_block * sb, unsigned int block_group) +{ + int i, j, retval = 0; + unsigned long block_bitmap_number; + struct buffer_head * block_bitmap = NULL; + int nr_groups = (UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)) + + (sizeof(struct SpaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); + + if (block_group >= nr_groups) + { + udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, nr_groups); + } + + if (nr_groups <= UDF_MAX_BLOCK_LOADED) + { + if (UDF_SB_BLOCK_BITMAP(sb, block_group)) + { + if (UDF_SB_BLOCK_BITMAP_NUMBER(sb, block_group) == block_group) + return block_group; + } + retval = read_block_bitmap(sb, block_group, block_group); + if (retval < 0) + return retval; + return block_group; + } + + for (i=0; i<UDF_SB_LOADED_BLOCK_BITMAPS(sb) && + UDF_SB_BLOCK_BITMAP_NUMBER(sb, i) != block_group; i++) + { + ; + } + if (i < UDF_SB_LOADED_BLOCK_BITMAPS(sb) && + UDF_SB_BLOCK_BITMAP_NUMBER(sb, i) == block_group) + { + block_bitmap_number = UDF_SB_BLOCK_BITMAP_NUMBER(sb, i); + block_bitmap = UDF_SB_BLOCK_BITMAP(sb, i); + for (j=i; j>0; j--) + { + UDF_SB_BLOCK_BITMAP_NUMBER(sb, j) = UDF_SB_BLOCK_BITMAP_NUMBER(sb, j-1); + UDF_SB_BLOCK_BITMAP(sb, j) = UDF_SB_BLOCK_BITMAP(sb, j-1); + } + UDF_SB_BLOCK_BITMAP_NUMBER(sb, 0) = block_bitmap_number; + UDF_SB_BLOCK_BITMAP(sb, 0) = block_bitmap; + + if (!block_bitmap) + retval = read_block_bitmap(sb, block_group, 0); + } + else + { + if (UDF_SB_LOADED_BLOCK_BITMAPS(sb) < UDF_MAX_BLOCK_LOADED) + UDF_SB_LOADED_BLOCK_BITMAPS(sb) ++; + else + brelse(UDF_SB_BLOCK_BITMAP(sb, UDF_MAX_BLOCK_LOADED-1)); + for (j=UDF_SB_LOADED_BLOCK_BITMAPS(sb)-1; j>0; j--) + { + UDF_SB_BLOCK_BITMAP_NUMBER(sb, j) = UDF_SB_BLOCK_BITMAP_NUMBER(sb, j-1); + UDF_SB_BLOCK_BITMAP(sb, j) = UDF_SB_BLOCK_BITMAP(sb, j-1); + } + retval = read_block_bitmap(sb, block_group, 0); + } + return retval; +} + +static inline int load_block_bitmap(struct super_block *sb, + unsigned int block_group) +{ + int slot; + int nr_groups = (UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)) + + (sizeof(struct SpaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); + + if (UDF_SB_LOADED_BLOCK_BITMAPS(sb) > 0 && + UDF_SB_BLOCK_BITMAP_NUMBER(sb, 0) == block_group && + UDF_SB_BLOCK_BITMAP(sb, block_group)) + { + return 0; + } + else if (nr_groups <= UDF_MAX_BLOCK_LOADED && + UDF_SB_BLOCK_BITMAP_NUMBER(sb, block_group) == block_group && + UDF_SB_BLOCK_BITMAP(sb, block_group)) + { + slot = block_group; + } + else + { + slot = load__block_bitmap(sb, block_group); + } + + if (slot < 0) + return slot; + + if (!UDF_SB_BLOCK_BITMAP(sb, slot)) + return -EIO; + + return slot; +} + +void udf_free_blocks(const struct inode * inode, lb_addr bloc, Uint32 offset, + Uint32 count) +{ + struct buffer_head * bh = NULL; + unsigned long block; + unsigned long block_group; + unsigned long bit; + unsigned long i; + int bitmap_nr; + unsigned long overflow; + struct super_block * sb; + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device"); + return; + } + + if (UDF_SB_PARTMAPS(sb)[bloc.partitionReferenceNum].s_uspace_bitmap == 0xFFFFFFFF) + return; + + lock_super(sb); + if (bloc.logicalBlockNum < 0 || + (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) + { + udf_debug("%d < %d || %d + %d > %d\n", + bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, + UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); + goto error_return; + } + + block = bloc.logicalBlockNum + offset + (sizeof(struct SpaceBitmapDesc) << 3); + +do_more: + overflow = 0; + block_group = block >> (sb->s_blocksize_bits + 3); + bit = block % (sb->s_blocksize << 3); + + /* + * Check to see if we are freeing blocks across a group boundary. + */ + if (bit + count > (sb->s_blocksize << 3)) + { + overflow = bit + count - (sb->s_blocksize << 3); + count -= overflow; + } + bitmap_nr = load_block_bitmap(sb, block_group); + if (bitmap_nr < 0) + goto error_return; + + bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); + for (i=0; i < count; i++) + { + if (udf_set_bit(bit + i, bh->b_data)) + { + udf_debug("bit %ld already set\n", bit + i); + udf_debug("byte=%2x\n", ((char *)bh->b_data)[(bit + i) >> 3]); + } + else if (UDF_SB_LVIDBH(sb)) + { + UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1); + } + } + mark_buffer_dirty(bh, 1); + if (overflow) + { + block += count; + count = overflow; + goto do_more; + } +error_return: + sb->s_dirt = 1; + if (UDF_SB_LVIDBH(sb)) + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + unlock_super(sb); + return; +} + +int udf_alloc_blocks(const struct inode * inode, Uint16 partition, + Uint32 first_block, Uint32 block_count) +{ + int alloc_count = 0; + int bit, block, block_group, group_start; + int nr_groups, bitmap_nr; + struct buffer_head *bh; + struct super_block *sb; + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return 0; + } + lock_super(sb); + + if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) + goto out; + +repeat: + nr_groups = (UDF_SB_PARTLEN(sb, partition) + + (sizeof(struct SpaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); + block = first_block + (sizeof(struct SpaceBitmapDesc) << 3); + block_group = block >> (sb->s_blocksize_bits + 3); + group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); + + bitmap_nr = load_block_bitmap(sb, block_group); + if (bitmap_nr < 0) + goto out; + bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); + + bit = block % (sb->s_blocksize << 3); + + while (bit < (sb->s_blocksize << 3) && block_count > 0) + { + if (!udf_test_bit(bit, bh->b_data)) + goto out; + if (!udf_clear_bit(bit, bh->b_data)) + { + udf_debug("bit already cleared for block %d\n", bit); + goto out; + } + block_count --; + alloc_count ++; + bit ++; + block ++; + + } + mark_buffer_dirty(bh, 1); + if (block_count > 0) + goto repeat; +out: + if (UDF_SB_LVIDBH(sb)) + { + UDF_SB_LVID(sb)->freeSpaceTable[partition] = + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } + sb->s_dirt = 1; + unlock_super(sb); + return alloc_count; +} + +int udf_new_block(const struct inode * inode, Uint16 partition, Uint32 goal, int *err) +{ + int tmp, newbit, bit=0, block, block_group, group_start; + int end_goal, nr_groups, bitmap_nr, i; + struct buffer_head *bh = NULL; + struct super_block *sb; + char *ptr; + int newblock = 0; + + *err = -ENOSPC; + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return newblock; + } + lock_super(sb); + +repeat: + if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) + goal = 0; + + nr_groups = (UDF_SB_PARTLEN(sb, partition) + + (sizeof(struct SpaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); + block = goal + (sizeof(struct SpaceBitmapDesc) << 3); + block_group = block >> (sb->s_blocksize_bits + 3); + group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); + + bitmap_nr = load_block_bitmap(sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); + ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); + + if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) + { + bit = block % (sb->s_blocksize << 3); + + if (udf_test_bit(bit, bh->b_data)) + { + goto got_block; + } + end_goal = (bit + 63) & ~63; + bit = udf_find_next_one_bit(bh->b_data, end_goal, bit); + if (bit < end_goal) + goto got_block; + ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3)); + newbit = (ptr - ((char *)bh->b_data)) << 3; + if (newbit < sb->s_blocksize << 3) + { + bit = newbit; + goto search_back; + } + newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit); + if (newbit < sb->s_blocksize << 3) + { + bit = newbit; + goto got_block; + } + } + + for (i=0; i<(nr_groups*2); i++) + { + block_group ++; + if (block_group >= nr_groups) + block_group = 0; + group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); + + bitmap_nr = load_block_bitmap(sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); + if (i < nr_groups) + { + ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); + if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) + { + bit = (ptr - ((char *)bh->b_data)) << 3; + break; + } + } + else + { + bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3); + if (bit < sb->s_blocksize << 3) + break; + } + } + if (i >= (nr_groups*2)) + { + unlock_super(sb); + return newblock; + } + if (bit < sb->s_blocksize << 3) + goto search_back; + else + bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3); + if (bit >= sb->s_blocksize << 3) + { + unlock_super(sb); + return 0; + } + +search_back: + for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--); + +got_block: + newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - + (group_start << 3); + + tmp = udf_get_pblock(sb, newblock, partition, 0); + if (!udf_clear_bit(bit, bh->b_data)) + { + udf_debug("bit already cleared for block %d\n", bit); + goto repeat; + } + + mark_buffer_dirty(bh, 1); + if (!(bh = getblk(sb->s_dev, tmp, sb->s_blocksize))) + { + udf_debug("cannot get block %d\n", tmp); + unlock_super(sb); + return 0; + } + memset(bh->b_data, 0, sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + udf_release_data(bh); + + if (UDF_SB_LVIDBH(sb)) + { + UDF_SB_LVID(sb)->freeSpaceTable[partition] = + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } + sb->s_dirt = 1; + unlock_super(sb); + *err = 0; + return newblock; + +error_return: + *err = -EIO; + unlock_super(sb); + return 0; +} diff --git a/fs/udf/crc.c b/fs/udf/crc.c new file mode 100644 index 000000000..3e24c7c35 --- /dev/null +++ b/fs/udf/crc.c @@ -0,0 +1,178 @@ +/* + * crc.c + * + * PURPOSE + * Routines to generate, calculate, and test a 16-bit CRC. + * + * DESCRIPTION + * The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories + * and Ned W. Rhodes of Software Systems Group. It has been published in + * "Design and Validation of Computer Protocols", Prentice Hall, + * Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4. + * + * Copyright is held by AT&T. + * + * AT&T gives permission for the free use of the CRC source code. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + */ + +#include "udfdecl.h" + +static Uint16 crc_table[256] = { + 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U, + 0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU, + 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U, + 0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU, + 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U, + 0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU, + 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U, + 0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU, + 0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, + 0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU, + 0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U, + 0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU, + 0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U, + 0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U, + 0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U, + 0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U, + 0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU, + 0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U, + 0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU, + 0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U, + 0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU, + 0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, + 0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU, + 0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, + 0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU, + 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U, + 0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU, + 0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U, + 0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U, + 0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U, + 0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U, + 0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U +}; + +/* + * udf_crc + * + * PURPOSE + * Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial. + * + * DESCRIPTION + * The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory. + * The polynomial used is: x^16 + x^12 + x^15 + 1 + * + * PRE-CONDITIONS + * data Pointer to the data block. + * size Size of the data block. + * + * POST-CONDITIONS + * <return> CRC of the data block. + * + * HISTORY + * July 21, 1997 - Andrew E. Mileski + * Adapted from OSTA-UDF(tm) 1.50 standard. + */ +extern Uint16 +udf_crc(Uint8 *data, Uint32 size, Uint16 crc) +{ + while (size--) + crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8); + + return crc; +} + +/****************************************************************************/ +#if defined(TEST) + +/* + * PURPOSE + * Test udf_crc() + * + * HISTORY + * July 21, 1997 - Andrew E. Mileski + * Adapted from OSTA-UDF(tm) 1.50 standard. + */ + +unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U }; + +int main(void) +{ + unsigned short x; + + x = udf_crc16(bytes, sizeof bytes); + printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); + + return 0; +} + +#endif /* defined(TEST) */ + +/****************************************************************************/ +#if defined(GENERATE) + +/* + * PURPOSE + * Generate a table for fast 16-bit CRC calculations (any polynomial). + * + * DESCRIPTION + * The ITU-T V.41 polynomial is 010041. + * + * HISTORY + * July 21, 1997 - Andrew E. Mileski + * Adapted from OSTA-UDF(tm) 1.50 standard. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + unsigned long crc, poly; + int n, i; + + /* Get the polynomial */ + sscanf(argv[1], "%lo", &poly); + if (poly & 0xffff0000U){ + fprintf(stderr, "polynomial is too large\en"); + exit(1); + } + + printf("/* CRC 0%o */\n", poly); + + /* Create a table */ + printf("static unsigned short crc_table[256] = {\n"); + for (n = 0; n < 256; n++){ + if (n % 8 == 0) + printf("\t"); + crc = n << 8; + for (i = 0; i < 8; i++){ + if(crc & 0x8000U) + crc = (crc << 1) ^ poly; + else + crc <<= 1; + crc &= 0xFFFFU; + } + if (n == 255) + printf("0x%04xU ", crc); + else + printf("0x%04xU, ", crc); + if(n % 8 == 7) + printf("\n"); + } + printf("};\n"); + + return 0; +} + +#endif /* defined(GENERATE) */ diff --git a/fs/udf/dir.c b/fs/udf/dir.c new file mode 100644 index 000000000..50762e5f4 --- /dev/null +++ b/fs/udf/dir.c @@ -0,0 +1,296 @@ +/* + * dir.c + * + * PURPOSE + * Directory handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Ben Fennema + * + * HISTORY + * + * 10/05/98 dgb Split directory operations into it's own file + * Implemented directory reads via do_udf_readdir + * 10/06/98 Made directory operations work! + * 11/17/98 Rewrote directory to support ICB_FLAG_AD_LONG + * 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading + * across blocks. + * 12/12/98 Split out the lookup code to namei.c. bulk of directory + * code now in directory.c:udf_fileident_read. + */ + +#include "udfdecl.h" + +#if defined(__linux__) && defined(__KERNEL__) +#include <linux/version.h> +#include "udf_i.h" +#include "udf_sb.h" +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/udf_fs.h> +#endif + +/* Prototypes for file operations */ +static int udf_readdir(struct file *, void *, filldir_t); +static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); + +/* readdir and lookup functions */ + +static struct file_operations udf_dir_operations = { + NULL, /* lllseek */ + NULL, /* read */ + NULL, /* write */ + udf_readdir, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* flush */ + NULL, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations udf_dir_inode_operations = { + &udf_dir_operations, +#ifdef CONFIG_UDF_RW + udf_create, /* create */ +#else + NULL, /* create */ +#endif + udf_lookup, /* lookup */ +#ifdef CONFIG_UDF_RW + udf_link, /* link */ + udf_unlink, /* unlink */ + udf_symlink, /* symlink */ + udf_mkdir, /* mkdir */ + udf_rmdir, /* rmdir */ + udf_mknod, /* mknod */ + udf_rename, /* rename */ +#else + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ +#endif + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* get_block */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + +/* + * udf_readdir + * + * PURPOSE + * Read a directory entry. + * + * DESCRIPTION + * Optional - sys_getdents() will return -ENOTDIR if this routine is not + * available. + * + * Refer to sys_getdents() in fs/readdir.c + * sys_getdents() -> . + * + * PRE-CONDITIONS + * filp Pointer to directory file. + * buf Pointer to directory entry buffer. + * filldir Pointer to filldir function. + * + * POST-CONDITIONS + * <return> >=0 on success. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *dir = filp->f_dentry->d_inode; + int result; + + if (!dir) + return -EBADF; + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if ( filp->f_pos == 0 ) + { + if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino) < 0) + return 0; + } + + result = do_udf_readdir(dir, filp, filldir, dirent); + UPDATE_ATIME(dir); + return result; +} + +static int +do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent) +{ + struct udf_fileident_bh fibh; + struct FileIdentDesc *fi=NULL; + struct FileIdentDesc cfi; + int block, iblock; + int nf_pos = filp->f_pos; + int flen; + char fname[255]; + char *nameptr; + Uint16 liu; + Uint8 lfi; + int size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + struct buffer_head * bh = NULL; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + + if (nf_pos >= size) + return 1; + + if (nf_pos == 0) + nf_pos = (UDF_I_EXT0OFFS(dir) >> 2); + + fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; + if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if (++offset < (elen >> dir->i_sb->s_blocksize_bits)) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return 0; + } + + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + { + udf_release_data(bh); + return 0; + } + + while ( nf_pos < size ) + { + filp->f_pos = nf_pos; + + fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &offset, &bh); + liu = le16_to_cpu(cfi.lengthOfImpUse); + lfi = cfi.lengthFileIdent; + + if (!fi) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; + } + + if (fibh.sbh == fibh.ebh) + nameptr = fi->fileIdent + liu; + else + { + int poffset; /* Unpaded ending offset */ + + poffset = fibh.soffset + sizeof(struct FileIdentDesc) + liu + lfi; + + if (poffset >= lfi) + nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); + else + { + nameptr = fname; + memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); + memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); + } + } + + if ( (cfi.fileCharacteristics & FILE_DELETED) != 0 ) + { + if ( !IS_UNDELETE(dir->i_sb) ) + continue; + } + + if ( (cfi.fileCharacteristics & FILE_HIDDEN) != 0 ) + { + if ( !IS_UNHIDE(dir->i_sb) ) + continue; + } + + iblock = udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0); + + if (!lfi) /* parent directory */ + { + if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino) < 0) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; + } + } + else + { + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (filldir(dirent, fname, flen, filp->f_pos, iblock) < 0) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; /* halt enum */ + } + } + else + { + udf_debug("size=%d, nf_pos=%d, liu=%d, lfi=%d\n", size, nf_pos, liu, lfi); + } + } + } /* end while */ + + filp->f_pos = nf_pos; + + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + + if ( filp->f_pos >= size) + return 1; + else + return 0; +} diff --git a/fs/udf/directory.c b/fs/udf/directory.c new file mode 100644 index 000000000..bc2ced009 --- /dev/null +++ b/fs/udf/directory.c @@ -0,0 +1,332 @@ +/* + * directory.c + * + * PURPOSE + * Directory related functions + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + */ + +#include "udfdecl.h" +#include "udf_sb.h" + +#if defined(__linux__) && defined(__KERNEL__) + +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/udf_fs.h> + +#else + +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> + +#endif + +#ifdef __KERNEL__ + +Uint8 * udf_filead_read(struct inode *dir, Uint8 *tmpad, Uint8 ad_size, + lb_addr fe_loc, int *pos, int *offset, struct buffer_head **bh, int *error) +{ + int loffset = *offset; + int block; + Uint8 *ad; + int remainder; + + *error = 0; + + ad = (Uint8 *)(*bh)->b_data + *offset; + *offset += ad_size; + + if (!ad) + { + udf_release_data(*bh); + *error = 1; + return NULL; + } + + if (*offset == dir->i_sb->s_blocksize) + { + udf_release_data(*bh); + block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); + if (!block) + return NULL; + if (!(*bh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return NULL; + } + else if (*offset > dir->i_sb->s_blocksize) + { + ad = tmpad; + + remainder = dir->i_sb->s_blocksize - loffset; + memcpy((Uint8 *)ad, (*bh)->b_data + loffset, remainder); + + udf_release_data(*bh); + block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); + if (!block) + return NULL; + if (!((*bh) = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return NULL; + + memcpy((Uint8 *)ad + remainder, (*bh)->b_data, ad_size - remainder); + *offset = ad_size - remainder; + } + return ad; +} + +struct FileIdentDesc * +udf_fileident_read(struct inode *dir, int *nf_pos, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi, + lb_addr *bloc, Uint32 *extoffset, + Uint32 *offset, struct buffer_head **bh) +{ + struct FileIdentDesc *fi; + lb_addr eloc; + Uint32 elen; + int block; + + fibh->soffset = fibh->eoffset; + + if (fibh->eoffset == dir->i_sb->s_blocksize) + { + int lextoffset = *extoffset; + + if (udf_next_aext(dir, bloc, extoffset, &eloc, &elen, bh, 1) != + EXTENT_RECORDED_ALLOCATED) + { + return NULL; + } + + block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); + + (*offset) ++; + + if (*offset >= (elen >> dir->i_sb->s_blocksize_bits)) + *offset = 0; + else + *extoffset = lextoffset; + + udf_release_data(fibh->sbh); + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return NULL; + fibh->soffset = fibh->eoffset = 0; + } + else if (fibh->sbh != fibh->ebh) + { + udf_release_data(fibh->sbh); + fibh->sbh = fibh->ebh; + } + + fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, + &(fibh->eoffset)); + + if (!fi) + return NULL; + + *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); + + if (fibh->eoffset <= dir->i_sb->s_blocksize) + { + memcpy((Uint8 *)cfi, (Uint8 *)fi, sizeof(struct FileIdentDesc)); + } + else if (fibh->eoffset > dir->i_sb->s_blocksize) + { + int lextoffset = *extoffset; + + if (udf_next_aext(dir, bloc, extoffset, &eloc, &elen, bh, 1) != + EXTENT_RECORDED_ALLOCATED) + { + return NULL; + } + + block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); + + (*offset) ++; + + if (*offset >= (elen >> dir->i_sb->s_blocksize_bits)) + *offset = 0; + else + *extoffset = lextoffset; + + fibh->soffset -= dir->i_sb->s_blocksize; + fibh->eoffset -= dir->i_sb->s_blocksize; + + if (!(fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return NULL; + + if (sizeof(struct FileIdentDesc) > - fibh->soffset) + { + int fi_len; + + memcpy((Uint8 *)cfi, (Uint8 *)fi, - fibh->soffset); + memcpy((Uint8 *)cfi - fibh->soffset, fibh->ebh->b_data, + sizeof(struct FileIdentDesc) + fibh->soffset); + + fi_len = (sizeof(struct FileIdentDesc) + cfi->lengthFileIdent + + le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; + + *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2); + fibh->eoffset = fibh->soffset + fi_len; + } + else + { + memcpy((Uint8 *)cfi, (Uint8 *)fi, sizeof(struct FileIdentDesc)); + } + } + return fi; +} +#endif + +struct FileIdentDesc * +udf_get_fileident(void * buffer, int bufsize, int * offset) +{ + struct FileIdentDesc *fi; + int lengthThisIdent; + Uint8 * ptr; + int padlen; + + if ( (!buffer) || (!offset) ) { +#ifdef __KERNEL__ + udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); +#endif + return NULL; + } + + ptr = buffer; + + if ( (*offset > 0) && (*offset < bufsize) ) { + ptr += *offset; + } + fi=(struct FileIdentDesc *)ptr; + if (le16_to_cpu(fi->descTag.tagIdent) != TID_FILE_IDENT_DESC) + { +#ifdef __KERNEL__ + udf_debug("0x%x != TID_FILE_IDENT_DESC\n", + le16_to_cpu(fi->descTag.tagIdent)); + udf_debug("offset: %u sizeof: %u bufsize: %u\n", + *offset, sizeof(struct FileIdentDesc), bufsize); +#endif + return NULL; + } + if ( (*offset + sizeof(struct FileIdentDesc)) > bufsize ) + { + lengthThisIdent = sizeof(struct FileIdentDesc); + } + else + lengthThisIdent = sizeof(struct FileIdentDesc) + + fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); + + /* we need to figure padding, too! */ + padlen = lengthThisIdent % UDF_NAME_PAD; + if (padlen) + lengthThisIdent += (UDF_NAME_PAD - padlen); + *offset = *offset + lengthThisIdent; + + return fi; +} + +extent_ad * +udf_get_fileextent(void * buffer, int bufsize, int * offset) +{ + extent_ad * ext; + struct FileEntry *fe; + Uint8 * ptr; + + if ( (!buffer) || (!offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); +#endif + return NULL; + } + + fe = (struct FileEntry *)buffer; + + if ( le16_to_cpu(fe->descTag.tagIdent) != TID_FILE_ENTRY ) + { +#ifdef __KERNEL__ + udf_debug("0x%x != TID_FILE_ENTRY\n", + le16_to_cpu(fe->descTag.tagIdent)); +#endif + return NULL; + } + + ptr=(Uint8 *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); + + if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) ) + { + ptr += *offset; + } + + ext = (extent_ad *)ptr; + + *offset = *offset + sizeof(extent_ad); + return ext; +} + +short_ad * +udf_get_fileshortad(void * buffer, int maxoffset, int *offset, int inc) +{ + short_ad * sa; + Uint8 * ptr; + + if ( (!buffer) || (!offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); +#endif + return NULL; + } + + ptr = (Uint8 *)buffer; + + if ( (*offset > 0) && (*offset < maxoffset) ) + ptr += *offset; + else + return NULL; + + if ((sa = (short_ad *)ptr)->extLength == 0) + return NULL; + else if (inc) + (*offset) += sizeof(short_ad); + return sa; +} + +long_ad * +udf_get_filelongad(void * buffer, int maxoffset, int * offset, int inc) +{ + long_ad * la; + Uint8 * ptr; + + if ( (!buffer) || !(offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); +#endif + return NULL; + } + + ptr = (Uint8 *)buffer; + + if ( (*offset > 0) && (*offset < maxoffset) ) + ptr += *offset; + else + return NULL; + + if ((la = (long_ad *)ptr)->extLength == 0) + return NULL; + else if (inc) + (*offset) += sizeof(long_ad); + return la; +} diff --git a/fs/udf/file.c b/fs/udf/file.c new file mode 100644 index 000000000..282466841 --- /dev/null +++ b/fs/udf/file.c @@ -0,0 +1,440 @@ +/* + * file.c + * + * PURPOSE + * File handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Dave Boynton + * (C) 1998-1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 10/02/98 dgb Attempt to integrate into udf.o + * 10/07/98 Switched to using generic_readpage, etc., like isofs + * And it works! + * 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but + * ICB_FLAG_AD_IN_ICB. + * 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c + * 05/12/99 Preliminary file write support + */ + +#include "udfdecl.h" +#include <linux/fs.h> +#include <linux/udf_fs.h> +#include <asm/uaccess.h> +#include <linux/kernel.h> +#include <linux/string.h> /* memset */ +#include <linux/errno.h> +#include <linux/locks.h> + +#include "udf_i.h" +#include "udf_sb.h" + +#define NBUF 32 + +typedef void * poll_table; + +static long long udf_file_llseek(struct file *, long long, int); +static ssize_t udf_file_read_adinicb (struct file *, char *, size_t, loff_t *); +static ssize_t udf_file_write (struct file *, const char *, size_t, loff_t *); +#if BITS_PER_LONG < 64 +static int udf_open_file(struct inode *, struct file *); +#endif +static int udf_release_file(struct inode *, struct file *); + +static struct file_operations udf_file_operations = { + udf_file_llseek, /* llseek */ + generic_file_read, /* read */ + udf_file_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 + NULL, /* open */ +#else + udf_open_file, /* open */ +#endif + NULL, /* flush */ + udf_release_file, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations udf_file_inode_operations = { + &udf_file_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + udf_get_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ + block_flushpage, /* flushpage */ +#ifdef CONFIG_UDF_RW + udf_truncate, /* truncate */ +#else + NULL, /* truncate */ +#endif + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + +static struct file_operations udf_file_operations_adinicb = { + udf_file_llseek, /* llseek */ + udf_file_read_adinicb,/* read */ + udf_file_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* flush */ + udf_release_file, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations udf_file_inode_operations_adinicb = { + &udf_file_operations_adinicb, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + udf_get_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ + block_flushpage, /* flushpage */ +#ifdef CONFIG_UDF_RW + udf_truncate, /* truncate */ +#else + NULL, /* truncate */ +#endif + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + +/* + * Make sure the offset never goes beyond the 32-bit mark.. + */ +static long long udf_file_llseek(struct file * file, long long offset, int origin) +{ + struct inode * inode = file->f_dentry->d_inode; + + switch (origin) + { + case 2: + { + offset += inode->i_size; + break; + } + case 1: + { + offset += file->f_pos; + break; + } + } +#if BITS_PER_LONG < 64 + if (((unsigned long long) offset >> 32) != 0) + { + return -EINVAL; + } +#endif + if (offset != file->f_pos) + { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; + } + return offset; +} + +static inline void remove_suid(struct inode * inode) +{ + unsigned int mode; + + /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ + mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; + + /* was any of the uid bits set? */ + mode &= inode->i_mode; + if (mode && !capable(CAP_FSETID)) + { + inode->i_mode &= ~mode; + mark_inode_dirty(inode); + } +} + +static ssize_t udf_file_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + ssize_t retval; + struct inode *inode = file->f_dentry->d_inode; + remove_suid(inode); + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + int i, err; + struct buffer_head *bh; + + if ((bh = udf_expand_adinicb(inode, &i, 0, &err))) + udf_release_data(bh); + } + + retval = generic_file_write(file, buf, count, ppos, block_write_partial_page); + + if (retval > 0) + { + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; + } + mark_inode_dirty(inode); + return retval; +} + +/* + * udf_file_read + * + * PURPOSE + * Read from an open file. + * + * DESCRIPTION + * Optional - sys_read() will return -EINVAL if this routine is not + * available. + * + * Refer to sys_read() in fs/read_write.c + * sys_read() -> . + * + * Note that you can use generic_file_read() instead, which requires that + * udf_readpage() be available, but you can use generic_readpage(), which + * requires that udf_block_map() be available. Reading will then be done by + * memory-mapping the file a page at a time. This is not suitable for + * devices that don't handle read-ahead [example: CD-R/RW that may have + * blank sectors that shouldn't be read]. + * + * Refer to generic_file_read() in mm/filemap.c and to generic_readpage() + * in fs/buffer.c + * + * Block devices can use block_read() instead. Refer to fs/block_dev.c + * + * PRE-CONDITIONS + * inode Pointer to inode to read from (never NULL). + * filp Pointer to file to read from (never NULL). + * buf Point to read buffer (validated). + * bufsize Size of read buffer. + * + * POST-CONDITIONS + * <return> Bytes read (>=0) or an error code (<0) that + * sys_read() will return. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static ssize_t udf_file_read_adinicb(struct file * filp, char * buf, + size_t bufsize, loff_t * loff) +{ + struct inode *inode = filp->f_dentry->d_inode; + Uint32 size, left, pos, block; + struct buffer_head *bh = NULL; + + size = inode->i_size; + if (*loff > size) + left = 0; + else + left = size - *loff; + if (left > bufsize) + left = bufsize; + + if (left <= 0) + return 0; + + pos = *loff + UDF_I_EXT0OFFS(inode); + block = udf_block_map(inode, 0); + if (!(bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0), + inode->i_sb->s_blocksize))) + { + return 0; + } + if (!copy_to_user(buf, bh->b_data + pos, left)) + *loff += left; + else + return -EFAULT; + + return left; +} + +/* + * udf_ioctl + * + * PURPOSE + * Issue an ioctl. + * + * DESCRIPTION + * Optional - sys_ioctl() will return -ENOTTY if this routine is not + * available, and the ioctl cannot be handled without filesystem help. + * + * sys_ioctl() handles these ioctls that apply only to regular files: + * FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD + * These ioctls are also handled by sys_ioctl(): + * FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC + * All other ioctls are passed to the filesystem. + * + * Refer to sys_ioctl() in fs/ioctl.c + * sys_ioctl() -> . + * + * PRE-CONDITIONS + * inode Pointer to inode that ioctl was issued on. + * filp Pointer to file that ioctl was issued on. + * cmd The ioctl command. + * arg The ioctl argument [can be interpreted as a + * user-space pointer if desired]. + * + * POST-CONDITIONS + * <return> Success (>=0) or an error code (<=0) that + * sys_ioctl() will return. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int result=-1; + int size; + struct buffer_head *bh = NULL; + struct FileEntry *fe; + Uint16 ident; + + if ( permission(inode, MAY_READ) != 0 ) + { + udf_debug("no permission to access inode %lu\n", + inode->i_ino); + return -EPERM; + } + + if ( !arg ) + { + udf_debug("invalid argument to udf_ioctl\n"); + return -EINVAL; + } + + /* first, do ioctls that don't need to udf_read */ + switch (cmd) + { + case UDF_GETVOLIDENT: + if ( (result == verify_area(VERIFY_WRITE, (char *)arg, 32)) == 0) + result = copy_to_user((char *)arg, UDF_SB_VOLIDENT(inode->i_sb), 32); + return result; + + } + + /* ok, we need to read the inode */ + bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); + + if (!bh || ident != TID_FILE_ENTRY) + { + udf_debug("bread failed (ino=%ld) or ident (%d) != TID_FILE_ENTRY", + inode->i_ino, ident); + return -EFAULT; + } + + fe = (struct FileEntry *)bh->b_data; + size = le32_to_cpu(fe->lengthExtendedAttr); + + switch (cmd) + { + case UDF_GETEASIZE: + if ( (result = verify_area(VERIFY_WRITE, (char *)arg, 4)) == 0) + result= put_user(size, (int *)arg); + break; + + case UDF_GETEABLOCK: + if ( (result = verify_area(VERIFY_WRITE, (char *)arg, size)) == 0) + result= copy_to_user((char *)arg, fe->extendedAttr, size); + break; + + default: + udf_debug("ino=%ld, cmd=%d\n", inode->i_ino, cmd); + break; + } + + udf_release_data(bh); + return result; +} + +/* + * udf_release_file + * + * PURPOSE + * Called when all references to the file are closed + * + * DESCRIPTION + * Discard prealloced blocks + * + * HISTORY + * + */ +static int udf_release_file(struct inode * inode, struct file * filp) +{ + if (filp->f_mode & FMODE_WRITE) + udf_discard_prealloc(inode); + return 0; +} + +#if BITS_PER_LONG < 64 +/* + * udf_open_file + * + * PURPOSE + * Called when an inode is about to be open. + * + * DESCRIPTION + * Use this to disallow opening RW large files on 32 bit systems. + * + * HISTORY + * + */ +static int udf_open_file(struct inode * inode, struct file * filp) +{ + if (inode->i_size == (Uint32)-1 && (filp->f_mode & FMODE_WRITE)) + return -EFBIG; + return 0; +} +#endif diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c new file mode 100644 index 000000000..3e2b95c19 --- /dev/null +++ b/fs/udf/fsync.c @@ -0,0 +1,110 @@ +/* + * fsync.c + * + * PURPOSE + * Fsync handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 05/22/99 blf Created. + * + */ + +#include "udfdecl.h" + +#include <linux/fs.h> +#include <linux/udf_fs.h> +#include "udf_i.h" + +static int sync_extent_block (struct inode * inode, Uint32 block, int wait) +{ + struct buffer_head * bh; + + if (!block) + return 0; + bh = get_hash_table (inode->i_dev, block, inode->i_sb->s_blocksize); + if (!bh) + return 0; + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse (bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + brelse (bh); + return 0; + } + ll_rw_block (WRITE, 1, &bh); + atomic_dec(&bh->b_count); + return 0; +} + +static int sync_all_extents(struct inode * inode, int wait) +{ + lb_addr bloc, eloc; + Uint32 extoffset, lextoffset, elen, offset, block; + int err = 0, etype; + struct buffer_head *bh = NULL; + + if ((etype = inode_bmap(inode, 0, &bloc, &extoffset, &eloc, &elen, &offset, &bh)) != -1) + { + block = udf_get_lb_pblock(inode->i_sb, bloc, 0); + err |= sync_extent_block(inode, block, wait); + lextoffset = extoffset; + + while ((etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) + { + if (lextoffset > extoffset) + { + block = udf_get_lb_pblock(inode->i_sb, bloc, 0); + err |= sync_extent_block(inode, block, wait); + } + lextoffset = extoffset; + } + } + udf_release_data(bh); + return err; +} + +/* + * File may be NULL when we are called. Perhaps we shouldn't + * even pass file to fsync ? + */ + +int udf_sync_file(struct file * file, struct dentry *dentry) +{ + int wait, err = 0; + struct inode *inode = dentry->d_inode; + + if ((S_ISLNK(inode->i_mode) && !(inode->i_blocks)) || + UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + /* + * Don't sync fast links! or ICB_FLAG_AD_IN_ICB + */ + goto skip; + } + + err = generic_buffer_fdatasync(inode, 0, ~0UL); + + for (wait=0; wait<=1; wait++) + { + err |= sync_all_extents (inode, wait); + } +skip: + err |= udf_sync_inode (inode); + return err ? -EIO : 0; +} diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c new file mode 100644 index 000000000..32f60fef5 --- /dev/null +++ b/fs/udf/ialloc.c @@ -0,0 +1,169 @@ +/* + * ialloc.c + * + * PURPOSE + * Inode allocation handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Ben Fennema + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/udf_fs.h> + +#include "udf_i.h" +#include "udf_sb.h" + +void udf_free_inode(struct inode * inode) +{ + struct super_block * sb = inode->i_sb; + int is_directory; + unsigned long ino; + + if (!inode->i_dev) + { + udf_debug("inode has no device\n"); + return; + } + if (inode->i_count > 1) + { + udf_debug("inode has count=%d\n", inode->i_count); + return; + } + if (inode->i_nlink) + { + udf_debug("inode has nlink=%d\n", inode->i_nlink); + return; + } + if (!sb) + { + udf_debug("inode on nonexistent device\n"); + return; + } + + ino = inode->i_ino; + + lock_super(sb); + + is_directory = S_ISDIR(inode->i_mode); + + clear_inode(inode); + + if (UDF_SB_LVIDBH(sb)) + { + if (is_directory) + UDF_SB_LVIDIU(sb)->numDirs = + cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1); + else + UDF_SB_LVIDIU(sb)->numFiles = + cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); + + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } + + unlock_super(sb); + + udf_free_blocks(inode, UDF_I_LOCATION(inode), 0, 1); +} + +struct inode * udf_new_inode (const struct inode *dir, int mode, int * err) +{ + struct super_block *sb; + struct inode * inode; + int block; + Uint32 start = UDF_I_LOCATION(dir).logicalBlockNum; + + inode = get_empty_inode(); + if (!inode) + { + *err = -ENOMEM; + return NULL; + } + sb = dir->i_sb; + inode->i_sb = sb; + inode->i_flags = 0; + *err = -ENOSPC; + + block = udf_new_block(dir, UDF_I_LOCATION(dir).partitionReferenceNum, + start, err); + if (*err) + { + iput(inode); + return NULL; + } + lock_super(sb); + + if (UDF_SB_LVIDBH(sb)) + { + struct LogicalVolHeaderDesc *lvhd; + Uint64 uniqueID; + lvhd = (struct LogicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse); + if (S_ISDIR(mode)) + UDF_SB_LVIDIU(sb)->numDirs = + cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1); + else + UDF_SB_LVIDIU(sb)->numFiles = + cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1); + UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID); + if (!(++uniqueID & 0x00000000FFFFFFFFUL)) + uniqueID += 16; + lvhd->uniqueID = cpu_to_le64(uniqueID); + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } + inode->i_mode = mode; + inode->i_sb = sb; + inode->i_nlink = 1; + inode->i_dev = sb->s_dev; + inode->i_uid = current->fsuid; + if (dir->i_mode & S_ISGID) + { + inode->i_gid = dir->i_gid; + if (S_ISDIR(mode)) + mode |= S_ISGID; + } + else + inode->i_gid = current->fsgid; + UDF_I_LOCATION(inode).logicalBlockNum = block; + UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; + inode->i_ino = udf_get_lb_pblock(sb, UDF_I_LOCATION(inode), 0); + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = 0; + inode->i_size = 0; + UDF_I_LENEATTR(inode) = 0; + UDF_I_LENALLOC(inode) = 0; + UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); + UDF_I_EXT0LEN(inode) = 0; +#if 1 + UDF_I_EXT0OFFS(inode) = sizeof(struct FileEntry); + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_IN_ICB; +#else + UDF_I_EXT0OFFS(inode) = 0; + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; +#endif + + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + UDF_I_UMTIME(inode) = UDF_I_UATIME(inode) = UDF_I_UCTIME(inode) = CURRENT_UTIME; + inode->i_op = NULL; + insert_inode_hash(inode); + mark_inode_dirty(inode); + unlock_super(sb); + *err = 0; + return inode; +} diff --git a/fs/udf/inode.c b/fs/udf/inode.c new file mode 100644 index 000000000..5e0aa576e --- /dev/null +++ b/fs/udf/inode.c @@ -0,0 +1,1944 @@ +/* + * inode.c + * + * PURPOSE + * Inode handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998 Dave Boynton + * (C) 1998-1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 10/04/98 dgb Added rudimentary directory functions + * 10/07/98 Fully working udf_block_map! It works! + * 11/25/98 bmap altered to better support extents + * 12/06/98 blf partition support in udf_iget, udf_block_map and udf_read_inode + * 12/12/98 rewrote udf_block_map to handle next extents and descs across + * block boundaries (which is not actually allowed) + * 12/20/98 added support for strategy 4096 + * 03/07/99 rewrote udf_block_map (again) + * New funcs, inode_bmap, udf_next_aext + * 04/19/99 Support for writing device EA's for major/minor # + * + */ + +#include "udfdecl.h" +#include <linux/locks.h> +#include <linux/mm.h> +#include <linux/smp_lock.h> + +#include "udf_i.h" +#include "udf_sb.h" + +#define EXTENT_MERGE_SIZE 5 + +static mode_t udf_convert_permissions(struct FileEntry *); +static int udf_update_inode(struct inode *, int); +static void udf_fill_inode(struct inode *, struct buffer_head *); +static struct buffer_head *inode_getblk(struct inode *, long, int *, long *, int *); +static void udf_split_extents(struct inode *, int *, int, int, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_prealloc_extents(struct inode *, int, int, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_merge_extents(struct inode *, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_update_extents(struct inode *, + long_ad [EXTENT_MERGE_SIZE], int, int, + lb_addr, Uint32, struct buffer_head **); + +static DECLARE_MUTEX(read_semaphore); + +/* + * udf_put_inode + * + * PURPOSE + * + * DESCRIPTION + * This routine is called whenever the kernel no longer needs the inode. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * Called at each iput() + */ +void udf_put_inode(struct inode * inode) +{ + udf_discard_prealloc(inode); +} + +/* + * udf_delete_inode + * + * PURPOSE + * Clean-up before the specified inode is destroyed. + * + * DESCRIPTION + * This routine is called when the kernel destroys an inode structure + * ie. when iput() finds i_count == 0. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * Called at the last iput() if i_nlink is zero. + */ +void udf_delete_inode(struct inode * inode) +{ + inode->i_size = 0; + if (inode->i_blocks) + udf_truncate(inode); + udf_free_inode(inode); +} + +void udf_discard_prealloc(struct inode * inode) +{ +#ifdef UDF_PREALLOCATE +#if 0 + unsigned short total; + lb_addr loc = UDF_I_LOCATION(inode); + + if (UDF_I_PREALLOC_COUNT(inode)) + { + total = UDF_I_PREALLOC_COUNT(inode); + UDF_I_PREALLOC_COUNT(inode) = 0; + loc.logicalBlockNum = UDF_I_PREALLOC_BLOCK(inode); + udf_free_blocks(inode, loc, 0, total); + } +#endif +#endif +} + +static int udf_alloc_block(struct inode *inode, Uint16 partition, + Uint32 goal, int *err) +{ + int result = 0; + wait_on_super(inode->i_sb); + + result = udf_new_block(inode, partition, goal, err); + + return result; +} + +struct buffer_head * udf_expand_adinicb(struct inode *inode, int *block, int isdir, int *err) +{ + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + long_ad newad; + int newblock; + struct buffer_head *sbh = NULL, *dbh = NULL; + + if (!UDF_I_LENALLOC(inode)) + { + UDF_I_EXT0OFFS(inode) = 0; + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + return NULL; + } + + /* alloc block, and copy data to it */ + *block = udf_alloc_block(inode, + UDF_I_LOCATION(inode).partitionReferenceNum, + UDF_I_LOCATION(inode).logicalBlockNum, err); + + if (!(*block)) + return NULL; + newblock = udf_get_pblock(inode->i_sb, *block, + UDF_I_LOCATION(inode).partitionReferenceNum, 0); + if (!newblock) + return NULL; + sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + if (!sbh) + return NULL; + dbh = udf_tread(inode->i_sb, newblock, inode->i_sb->s_blocksize); + if (!dbh) + return NULL; + + if (isdir) + { + struct udf_fileident_bh sfibh, dfibh; + int f_pos = UDF_I_EXT0OFFS(inode) >> 2; + int size = (UDF_I_EXT0OFFS(inode) + inode->i_size) >> 2; + struct FileIdentDesc cfi, *sfi, *dfi; + + sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; + sfibh.sbh = sfibh.ebh = sbh; + dfibh.soffset = dfibh.eoffset = 0; + dfibh.sbh = dfibh.ebh = dbh; + while ( (f_pos < size) ) + { + sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL); + if (!sfi) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + sfi->descTag.tagLocation = *block; + dfibh.soffset = dfibh.eoffset; + dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); + dfi = (struct FileIdentDesc *)(dbh->b_data + dfibh.soffset); + if (udf_write_fi(sfi, dfi, &dfibh, sfi->impUse, + sfi->fileIdent + sfi->lengthOfImpUse)) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + } + } + else + { + memcpy(dbh->b_data, sbh->b_data + udf_file_entry_alloc_offset(inode), + UDF_I_LENALLOC(inode)); + } + mark_buffer_dirty(dbh, 1); + + memset(sbh->b_data + udf_file_entry_alloc_offset(inode), + 0, UDF_I_LENALLOC(inode)); + + newad.extLength = UDF_I_EXT0LEN(inode) = inode->i_size; + newad.extLocation.logicalBlockNum = *block; + newad.extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + UDF_I_EXT0LOC(inode) = newad.extLocation; + /* UniqueID stuff */ + + memcpy(sbh->b_data + udf_file_entry_alloc_offset(inode), + &newad, sizeof(newad)); + + UDF_I_LENALLOC(inode) = sizeof(newad); + UDF_I_EXT0OFFS(inode) = 0; + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + inode->i_blocks += inode->i_sb->s_blocksize / 512; + udf_release_data(sbh); + mark_inode_dirty(inode); + inode->i_version ++; + if (inode->i_op == &udf_file_inode_operations_adinicb) + inode->i_op = &udf_file_inode_operations; + return dbh; + } + else + return NULL; +} + +struct buffer_head * udf_getblk(struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head dummy; + int error; + + dummy.b_state = 0; + dummy.b_blocknr = -1000; + error = udf_get_block(inode, block, &dummy, create); + *err = error; + if (!error & buffer_mapped(&dummy)) + { + struct buffer_head *bh; + bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); + if (buffer_new(&dummy)) + { + memset(bh->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + } + return bh; + } + return NULL; +} + +int udf_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) +{ + int err, new; + struct buffer_head *bh; + unsigned long phys; + + if (!create) + { + phys = udf_block_map(inode, block); + if (phys) + { + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + } + return 0; + } + + err = -EIO; + + lock_kernel(); + + if (block < 0) + goto abort_negative; + + if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) + { + UDF_I_NEXT_ALLOC_BLOCK(inode) ++; + UDF_I_NEXT_ALLOC_GOAL(inode) ++; + } + + err = 0; + + bh = inode_getblk(inode, block, &err, &phys, &new); + if (bh) + BUG(); + if (err) + goto abort; + if (!phys) + BUG(); + + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = phys; + bh_result->b_state |= (1UL << BH_Mapped); + if (new) + bh_result->b_state |= (1UL << BH_New); +abort: + unlock_kernel(); + return err; + +abort_negative: + udf_warning(inode->i_sb, "udf_get_block", "block < 0"); + goto abort; +} + +static struct buffer_head * inode_getblk(struct inode * inode, long block, + int *err, long *phys, int *new) +{ + struct buffer_head *pbh = NULL, *cbh = NULL, *result = NULL; + long_ad laarr[EXTENT_MERGE_SIZE]; + Uint32 pextoffset = 0, cextoffset = 0, nextoffset = 0; + int count = 0, startnum = 0, endnum = 0; + Uint32 elen = 0; + lb_addr eloc, pbloc = UDF_I_LOCATION(inode), cbloc = UDF_I_LOCATION(inode); + int c = 1; + int lbcount = 0, b_off = 0, offset = 0; + Uint32 newblocknum, newblock; + char etype; + int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum; + char lastblock = 0; + + pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode); + b_off = block << inode->i_sb->s_blocksize_bits; + pbloc = cbloc = UDF_I_LOCATION(inode); + + /* find the extent which contains the block we are looking for. + alternate between laarr[0] and laarr[1] for locations of the + current extent, and the previous extent */ + do + { + if (pbh != cbh) + { + udf_release_data(pbh); + atomic_inc(&cbh->b_count); + pbloc = cbloc; + } + + lbcount += elen; + + pextoffset = cextoffset; + cextoffset = nextoffset; + + if ((etype = udf_next_aext(inode, &cbloc, &nextoffset, &eloc, &elen, &cbh, 1)) == -1) + break; + + c = !c; + + laarr[c].extLength = (etype << 30) | elen; + laarr[c].extLocation = eloc; + + if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) + pgoal = eloc.logicalBlockNum + + ((elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize); + + count ++; + } while (lbcount + elen <= b_off); + + b_off -= lbcount; + offset = b_off >> inode->i_sb->s_blocksize_bits; + + /* if the extent is allocated and recorded, return the block + if the extent is not a multiple of the blocksize, round up */ + + if (etype == EXTENT_RECORDED_ALLOCATED) + { + if (elen & (inode->i_sb->s_blocksize - 1)) + { + elen = (EXTENT_RECORDED_ALLOCATED << 30) | + ((elen + inode->i_sb->s_blocksize - 1) & + ~(inode->i_sb->s_blocksize - 1)); + etype = udf_write_aext(inode, cbloc, &cextoffset, eloc, elen, &cbh, 1); + } + udf_release_data(pbh); + udf_release_data(cbh); + newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset); + *phys = newblock; + return NULL; + } + + if (etype == -1) + { + endnum = startnum = ((count > 1) ? 1 : count); + c = !c; + laarr[c].extLength = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | + ((offset + 1) << inode->i_sb->s_blocksize_bits); + memset(&laarr[c].extLocation, 0x00, sizeof(lb_addr)); + count ++; + endnum ++; + lastblock = 1; + } + else + endnum = startnum = ((count > 2) ? 2 : count); + + /* if the current extent is in position 0, swap it with the previous */ + if (!c && count != 1) + { + laarr[2] = laarr[0]; + laarr[0] = laarr[1]; + laarr[1] = laarr[2]; + c = 1; + } + + /* if the current block is located in a extent, read the next extent */ + if (etype != -1) + { + if ((etype = udf_next_aext(inode, &cbloc, &nextoffset, &eloc, &elen, &cbh, 0)) != -1) + { + laarr[c+1].extLength = (etype << 30) | elen; + laarr[c+1].extLocation = eloc; + count ++; + startnum ++; + endnum ++; + } + else + lastblock = 1; + } + udf_release_data(cbh); + + *err = -EFBIG; + + /* Check file limits.. */ + { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) + { + limit >>= inode->i_sb->s_blocksize_bits; + if (block >= limit) + { + send_sig(SIGXFSZ, current, 0); + *err = -EFBIG; + return NULL; + } + } + } + + /* if the current extent is not recorded but allocated, get the + block in the extent corresponding to the requested block */ + if ((laarr[c].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) + newblocknum = laarr[c].extLocation.logicalBlockNum + offset; + else /* otherwise, allocate a new block */ + { + if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block) + goal = UDF_I_NEXT_ALLOC_GOAL(inode); + + if (!goal) + { + if (!(goal = pgoal)) + goal = UDF_I_LOCATION(inode).logicalBlockNum + 1; + } + + if (!(newblocknum = udf_alloc_block(inode, + UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) + { + udf_release_data(pbh); + udf_release_data(cbh); + *err = -ENOSPC; + return NULL; + } + } + + /* if the extent the requsted block is located in contains multiple blocks, + split the extent into at most three extents. blocks prior to requested + block, requested block, and blocks after requested block */ + udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum); + +#ifdef UDF_PREALLOCATE + /* preallocate blocks */ + udf_prealloc_extents(inode, c, lastblock, laarr, &endnum); +#endif + + /* merge any continuous blocks in laarr */ + udf_merge_extents(inode, laarr, &endnum); + + /* write back the new extents, inserting new extents if the new number + of extents is greater than the old number, and deleting extents if + the new number of extents is less than the old number */ + udf_update_extents(inode, laarr, startnum, endnum, pbloc, pextoffset, &pbh); + + udf_release_data(pbh); + + if (c == 0 || c == 1) + { + UDF_I_EXT0LEN(inode) = laarr[0].extLength; + UDF_I_EXT0LOC(inode) = laarr[0].extLocation; + } + + if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum, + UDF_I_LOCATION(inode).partitionReferenceNum, 0))) + { + return NULL; + } + *phys = newblock; + *err = 0; + *new = 1; + UDF_I_NEXT_ALLOC_BLOCK(inode) = block; + UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; + inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(inode) = CURRENT_UTIME; + inode->i_blocks += inode->i_sb->s_blocksize / 512; +#if 0 + if (IS_SYNC(inode) || UDF_I_OSYNC(inode)) + udf_sync_inode(inode); + else +#endif + mark_inode_dirty(inode); + return result; +} + +static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum, + long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +{ + if ((laarr[*c].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED || + (laarr[*c].extLength >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + { + int curr = *c; + int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + int type = laarr[curr].extLength & ~UDF_EXTENT_LENGTH_MASK; + + if (blen == 1) + ; + else if (!offset || blen == offset + 1) + { + laarr[curr+2] = laarr[curr+1]; + laarr[curr+1] = laarr[curr]; + } + else + { + laarr[curr+3] = laarr[curr+1]; + laarr[curr+2] = laarr[curr+1] = laarr[curr]; + } + + if (offset) + { + laarr[curr].extLength = type | + (offset << inode->i_sb->s_blocksize_bits); + curr ++; + (*c) ++; + (*endnum) ++; + } + + laarr[curr].extLocation.logicalBlockNum = newblocknum; + if ((type >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + laarr[curr].extLocation.partitionReferenceNum = + UDF_I_LOCATION(inode).partitionReferenceNum; + laarr[curr].extLength = (EXTENT_RECORDED_ALLOCATED << 30) | + inode->i_sb->s_blocksize; + curr ++; + + if (blen != offset + 1) + { + if ((type >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) + laarr[curr].extLocation.logicalBlockNum += (offset + 1); + laarr[curr].extLength = type | + ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); + curr ++; + (*endnum) ++; + } + } +} + +static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, + long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +{ + int start, length = 0, currlength = 0, i; + + if (*endnum == (c+1) && !lastblock) + return; + + if ((laarr[c+1].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) + { + start = c+1; + length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + } + else + start = c; + + for (i=start+1; i<=*endnum; i++) + { + if (i == *endnum) + { + if (lastblock) + length += UDF_DEFAULT_PREALLOC_BLOCKS; + } + else if ((laarr[i].extLength >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + else + break; + } + + if (length) + { + int next = laarr[start].extLocation.logicalBlockNum + + (((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + int numalloc = udf_alloc_blocks(inode, + laarr[start].extLocation.partitionReferenceNum, + next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : + UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); + + if (numalloc) + { + if (start == (c+1)) + laarr[start].extLength += + (numalloc << inode->i_sb->s_blocksize_bits); + else + { + memmove(&laarr[c+2], &laarr[c+1], + sizeof(long_ad) * (*endnum - (c+1))); + (*endnum) ++; + laarr[c+1].extLocation.logicalBlockNum = next; + laarr[c+1].extLocation.partitionReferenceNum = + laarr[c].extLocation.partitionReferenceNum; + laarr[c+1].extLength = (EXTENT_NOT_RECORDED_ALLOCATED << 30) | + (numalloc << inode->i_sb->s_blocksize_bits); + start = c+1; + } + + for (i=start+1; numalloc && i<*endnum; i++) + { + int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + + if (elen > numalloc) + { + laarr[c+1].extLength -= + (numalloc << inode->i_sb->s_blocksize_bits); + numalloc = 0; + } + else + { + numalloc -= elen; + if (*endnum > (i+1)) + memmove(&laarr[i], &laarr[i+1], + sizeof(long_ad) * (*endnum - (i+1))); + i --; + (*endnum) --; + } + } + } + } +} + +static void udf_merge_extents(struct inode *inode, + long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +{ + int i; + + for (i=0; i<(*endnum-1); i++) + { + if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30)) + { + if (((laarr[i].extLength >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) || + ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) + { + laarr[i].extLength = laarr[i+1].extLength + + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); + if (*endnum > (i+2)) + memmove(&laarr[i+1], &laarr[i+2], + sizeof(long_ad) * (*endnum - (i+2))); + i --; + (*endnum) --; + } + } + } +} + +static void udf_update_extents(struct inode *inode, + long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, + lb_addr pbloc, Uint32 pextoffset, struct buffer_head **pbh) +{ + int start = 0, i; + lb_addr tmploc; + Uint32 tmplen; + + if (startnum > endnum) + { + for (i=0; i<(startnum-endnum); i++) + { + udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation, + laarr[i].extLength, *pbh); + } + } + else if (startnum < endnum) + { + for (i=0; i<(endnum-startnum); i++) + { + udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation, + laarr[i].extLength, *pbh); + udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation, + &laarr[i].extLength, pbh, 1); + start ++; + } + } + + for (i=start; i<endnum; i++) + { + udf_next_aext(inode, &pbloc, &pextoffset, &tmploc, &tmplen, pbh, 0); + udf_write_aext(inode, pbloc, &pextoffset, laarr[i].extLocation, + laarr[i].extLength, pbh, 1); + } +} + +struct buffer_head * udf_bread(struct inode * inode, int block, + int create, int * err) +{ + struct buffer_head * bh = NULL; + int prev_blocks; + + prev_blocks = inode->i_blocks; + + bh = udf_getblk(inode, block, create, err); + if (!bh) + return NULL; + +#if 0 + if (create && + S_ISDIR(inode->i_mode) && + inode->i_blocks > prev_blocks) + { + int i; + struct buffer_head *tmp_bh = NULL; + + for (i=1; + i < UDF_DEFAULT_PREALLOC_DIR_BLOCKS; + i++) + { + tmp_bh = udf_getblk(inode, block+i, create, err); + if (!tmp_bh) + { + udf_release_data(bh); + return 0; + } + udf_release_data(tmp_bh); + } + } +#endif + + if (buffer_uptodate(bh)) + return bh; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (buffer_uptodate(bh)) + return bh; + brelse(bh); + *err = -EIO; + return NULL; +} + +/* + * udf_read_inode + * + * PURPOSE + * Read an inode. + * + * DESCRIPTION + * This routine is called by iget() [which is called by udf_iget()] + * (clean_inode() will have been called first) + * when an inode is first read into memory. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * 12/19/98 dgb Updated to fix size problems. + */ +void +udf_read_inode(struct inode *inode) +{ + struct buffer_head *bh = NULL; + struct FileEntry *fe; + Uint16 ident; + + /* + * Set defaults, but the inode is still incomplete! + * Note: get_new_inode() sets the following on a new inode: + * i_sb = sb + * i_dev = sb->s_dev; + * i_no = ino + * i_flags = sb->s_flags + * i_state = 0 + * clean_inode(): zero fills and sets + * i_count = 1 + * i_nlink = 1 + * i_op = NULL; + */ + + inode->i_blksize = inode->i_sb->s_blocksize; + inode->i_version = 1; + + UDF_I_EXT0LEN(inode)=0; + UDF_I_EXT0LOC(inode).logicalBlockNum = 0xFFFFFFFF; + UDF_I_EXT0LOC(inode).partitionReferenceNum = 0xFFFF; + UDF_I_EXT0OFFS(inode)=0; + UDF_I_ALLOCTYPE(inode)=0; + + memcpy(&UDF_I_LOCATION(inode), &UDF_SB_LOCATION(inode->i_sb), sizeof(lb_addr)); + + bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); + + if (!bh) + { + printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n", + inode->i_ino); + make_bad_inode(inode); + return; + } + + if (ident != TID_FILE_ENTRY && ident != TID_EXTENDED_FILE_ENTRY) + { + printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n", + inode->i_ino, ident); + udf_release_data(bh); + make_bad_inode(inode); + return; + } + + fe = (struct FileEntry *)bh->b_data; + + if (le16_to_cpu(fe->icbTag.strategyType) == 4096) + { + struct buffer_head *ibh = NULL, *nbh = NULL; + struct IndirectEntry *ie; + + ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident); + if (ident == TID_INDIRECT_ENTRY) + { + if (ibh) + { + lb_addr loc; + ie = (struct IndirectEntry *)ibh->b_data; + + loc = lelb_to_cpu(ie->indirectICB.extLocation); + + if (ie->indirectICB.extLength && + (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) + { + if (ident == TID_FILE_ENTRY || + ident == TID_EXTENDED_FILE_ENTRY) + { + memcpy(&UDF_SB_LOCATION(inode->i_sb), &loc, sizeof(lb_addr)); + udf_release_data(bh); + udf_release_data(ibh); + udf_release_data(nbh); + udf_read_inode(inode); + return; + } + else + { + udf_release_data(nbh); + udf_release_data(ibh); + } + } + else + udf_release_data(ibh); + } + } + else + udf_release_data(ibh); + } + else if (le16_to_cpu(fe->icbTag.strategyType) != 4) + { + printk(KERN_ERR "udf: unsupported strategy type: %d\n", + le16_to_cpu(fe->icbTag.strategyType)); + udf_release_data(bh); + make_bad_inode(inode); + return; + } + udf_fill_inode(inode, bh); + udf_release_data(bh); +} + +static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) +{ + struct FileEntry *fe; + struct ExtendedFileEntry *efe; + time_t convtime; + int offset, alen; + + fe = (struct FileEntry *)bh->b_data; + efe = (struct ExtendedFileEntry *)bh->b_data; + + if (fe->descTag.tagIdent == TID_EXTENDED_FILE_ENTRY) + UDF_I_EXTENDED_FE(inode) = 1; + else /* fe->descTag.tagIdent == TID_FILE_ENTRY */ + UDF_I_EXTENDED_FE(inode) = 0; + + if (le16_to_cpu(fe->icbTag.strategyType) == 4) + UDF_I_STRAT4096(inode) = 0; + else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */ + UDF_I_STRAT4096(inode) = 1; + + inode->i_uid = udf_convert_uid(le32_to_cpu(fe->uid)); + if ( !inode->i_uid ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; + + inode->i_gid = udf_convert_gid(le32_to_cpu(fe->gid)); + if ( !inode->i_gid ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; + + inode->i_nlink = le16_to_cpu(fe->fileLinkCount); + if (!inode->i_nlink) + inode->i_nlink = 1; + + inode->i_size = le64_to_cpu(fe->informationLength); +#if BITS_PER_LONG < 64 + if (le64_to_cpu(fe->informationLength) & 0xFFFFFFFF00000000) + inode->i_size = (Uint32)-1; +#endif + + inode->i_mode = udf_convert_permissions(fe); + inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; + +#ifdef UDF_PREALLOCATE +#if 0 + UDF_I_PREALLOC_BLOCK(inode) = 0; + UDF_I_PREALLOC_COUNT(inode) = 0; +#endif +#endif + UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; + UDF_I_NEXT_ALLOC_GOAL(inode) = 0; + + UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICB_FLAG_ALLOC_MASK; + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) << + (inode->i_sb->s_blocksize_bits - 9); + + if ( udf_stamp_to_time(&convtime, lets_to_cpu(fe->modificationTime)) ) + { + inode->i_mtime = convtime; + inode->i_ctime = convtime; + } + else + { + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); + } + + if ( udf_stamp_to_time(&convtime, lets_to_cpu(fe->accessTime)) ) + inode->i_atime = convtime; + else + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); + + UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID); + UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); + UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); + offset = sizeof(struct FileEntry) + UDF_I_LENEATTR(inode); + alen = offset + UDF_I_LENALLOC(inode); + } + else + { + inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << + (inode->i_sb->s_blocksize_bits - 9); + + if ( udf_stamp_to_time(&convtime, lets_to_cpu(efe->modificationTime)) ) + inode->i_mtime = convtime; + else + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); + + if ( udf_stamp_to_time(&convtime, lets_to_cpu(efe->accessTime)) ) + inode->i_atime = convtime; + else + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); + + if ( udf_stamp_to_time(&convtime, lets_to_cpu(efe->createTime)) ) + inode->i_ctime = convtime; + else + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); + + UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID); + UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr); + UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs); + offset = sizeof(struct ExtendedFileEntry) + UDF_I_LENEATTR(inode); + alen = offset + UDF_I_LENALLOC(inode); + } + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + short_ad * sa; + + sa = udf_get_fileshortad(fe, alen, &offset, 1); + if (sa) + { + UDF_I_EXT0LEN(inode) = le32_to_cpu(sa->extLength); + UDF_I_EXT0LOC(inode).logicalBlockNum = le32_to_cpu(sa->extPosition); + UDF_I_EXT0LOC(inode).partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + } + break; + } + case ICB_FLAG_AD_LONG: + { + long_ad * la; + + la = udf_get_filelongad(fe, alen, &offset, 1); + if (la) + { + UDF_I_EXT0LEN(inode) = le32_to_cpu(la->extLength); + UDF_I_EXT0LOC(inode).logicalBlockNum = le32_to_cpu(la->extLocation.logicalBlockNum); + UDF_I_EXT0LOC(inode).partitionReferenceNum = le16_to_cpu(la->extLocation.partitionReferenceNum); + } + break; + } + case ICB_FLAG_AD_EXTENDED: + { + extent_ad * ext; + + ext = udf_get_fileextent(fe, alen, &offset); + if ( (ext) && (ext->extLength) ) + { + UDF_I_EXT0LEN(inode) = le32_to_cpu(ext->extLength); +#if 0 + UDF_I_EXT0LOC(inode) = ext->extLocation; +#endif + } + break; + } + case ICB_FLAG_AD_IN_ICB: /* short directories */ + { + UDF_I_EXT0LEN(inode) = le32_to_cpu(fe->lengthAllocDescs); + UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); + UDF_I_EXT0OFFS(inode) = sizeof(struct FileEntry) + + le32_to_cpu(fe->lengthExtendedAttr); + break; + } + } /* end switch ad_type */ + + switch (fe->icbTag.fileType) + { + case FILE_TYPE_DIRECTORY: + { + inode->i_op = &udf_dir_inode_operations; + inode->i_mode |= S_IFDIR; + inode->i_nlink ++; + break; + } + case FILE_TYPE_REGULAR: + case FILE_TYPE_NONE: + { + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + inode->i_op = &udf_file_inode_operations_adinicb; + else + inode->i_op = &udf_file_inode_operations; + inode->i_mode |= S_IFREG; + break; + } + case FILE_TYPE_BLOCK: + { + inode->i_op = &blkdev_inode_operations; + inode->i_mode |= S_IFBLK; + break; + } + case FILE_TYPE_CHAR: + { + inode->i_op = &chrdev_inode_operations; + inode->i_mode |= S_IFCHR; + break; + } + case FILE_TYPE_FIFO: + { + init_fifo(inode); + } + case FILE_TYPE_SYMLINK: + { + /* untested! */ + inode->i_op = &udf_symlink_inode_operations; + inode->i_mode = S_IFLNK|S_IRWXUGO; + break; + } + default: + { + printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", + inode->i_ino, fe->icbTag.fileType); + make_bad_inode(inode); + return; + } + } + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + { + struct buffer_head *tbh = NULL; + struct DeviceSpecificationExtendedAttr *dsea = + (struct DeviceSpecificationExtendedAttr *) + udf_get_extendedattr(inode, 12, 1, &tbh); + + if (dsea) + { + inode->i_rdev = to_kdev_t( + (le32_to_cpu(dsea->majorDeviceIdent)) << 8) | + (le32_to_cpu(dsea->minorDeviceIdent) & 0xFF); + /* Developer ID ??? */ + udf_release_data(tbh); + } + else + { + make_bad_inode(inode); + } + } +} + +static mode_t +udf_convert_permissions(struct FileEntry *fe) +{ + mode_t mode; + Uint32 permissions; + Uint32 flags; + + permissions = le32_to_cpu(fe->permissions); + flags = le16_to_cpu(fe->icbTag.flags); + + mode = (( permissions ) & S_IRWXO) | + (( permissions >> 2 ) & S_IRWXG) | + (( permissions >> 4 ) & S_IRWXU) | + (( flags & ICB_FLAG_SETUID) ? S_ISUID : 0) | + (( flags & ICB_FLAG_SETGID) ? S_ISGID : 0) | + (( flags & ICB_FLAG_STICKY) ? S_ISVTX : 0); + + return mode; +} + +/* + * udf_write_inode + * + * PURPOSE + * Write out the specified inode. + * + * DESCRIPTION + * This routine is called whenever an inode is synced. + * Currently this routine is just a placeholder. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +void udf_write_inode(struct inode * inode) +{ + udf_update_inode(inode, 0); +} + +int udf_sync_inode(struct inode * inode) +{ + return udf_update_inode(inode, 1); +} + +static int +udf_update_inode(struct inode *inode, int do_sync) +{ + struct buffer_head *bh = NULL; + struct FileEntry *fe; + struct ExtendedFileEntry *efe; + Uint32 udfperms; + Uint16 icbflags; + Uint16 crclen; + int i; + timestamp cpu_time; + int err = 0; + + bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0), + inode->i_sb->s_blocksize); + if (!bh) + { + udf_debug("bread failure\n"); + return -EIO; + } + fe = (struct FileEntry *)bh->b_data; + efe = (struct ExtendedFileEntry *)bh->b_data; + + if (inode->i_uid != UDF_SB(inode->i_sb)->s_uid) + fe->uid = cpu_to_le32(inode->i_uid); + + if (inode->i_gid != UDF_SB(inode->i_sb)->s_gid) + fe->gid = cpu_to_le32(inode->i_gid); + + udfperms = ((inode->i_mode & S_IRWXO) ) | + ((inode->i_mode & S_IRWXG) << 2) | + ((inode->i_mode & S_IRWXU) << 4); + + udfperms |= (le32_to_cpu(fe->permissions) & + (PERM_O_DELETE | PERM_O_CHATTR | + PERM_G_DELETE | PERM_G_CHATTR | + PERM_U_DELETE | PERM_U_CHATTR)); + fe->permissions = cpu_to_le32(udfperms); + + if (S_ISDIR(inode->i_mode)) + fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1); + else + fe->fileLinkCount = cpu_to_le16(inode->i_nlink); + + + fe->informationLength = cpu_to_le64(inode->i_size); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + { + EntityID *eid; + struct buffer_head *tbh = NULL; + struct DeviceSpecificationExtendedAttr *dsea = + (struct DeviceSpecificationExtendedAttr *) + udf_get_extendedattr(inode, 12, 1, &tbh); + + if (!dsea) + { + dsea = (struct DeviceSpecificationExtendedAttr *) + udf_add_extendedattr(inode, + sizeof(struct DeviceSpecificationExtendedAttr) + + sizeof(EntityID), 12, 0x3, &tbh); + dsea->attrType = 12; + dsea->attrSubtype = 1; + dsea->attrLength = sizeof(struct DeviceSpecificationExtendedAttr) + + sizeof(EntityID); + dsea->impUseLength = sizeof(EntityID); + } + eid = (EntityID *)dsea->impUse; + memset(eid, 0, sizeof(EntityID)); + strcpy(eid->ident, UDF_ID_DEVELOPER); + eid->identSuffix[0] = UDF_OS_CLASS_UNIX; + eid->identSuffix[1] = UDF_OS_ID_LINUX; + dsea->majorDeviceIdent = kdev_t_to_nr(inode->i_rdev) >> 8; + dsea->minorDeviceIdent = kdev_t_to_nr(inode->i_rdev) & 0xFF; + mark_buffer_dirty(tbh, 1); + udf_release_data(tbh); + } + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + fe->logicalBlocksRecorded = cpu_to_le64( + (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> + (inode->i_sb->s_blocksize_bits - 9)); + + if (udf_time_to_stamp(&cpu_time, inode->i_atime, UDF_I_UATIME(inode))) + fe->accessTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode))) + fe->modificationTime = cpu_to_lets(cpu_time); + memset(&(fe->impIdent), 0, sizeof(EntityID)); + strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER); + fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); + fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); + fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); + fe->descTag.tagIdent = le16_to_cpu(TID_FILE_ENTRY); + crclen = sizeof(struct FileEntry); + } + else + { + efe->logicalBlocksRecorded = cpu_to_le64( + (inode->i_blocks + (2 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> + (inode->i_sb->s_blocksize_bits - 9)); + + if (udf_time_to_stamp(&cpu_time, inode->i_atime, UDF_I_UATIME(inode))) + efe->accessTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode))) + efe->modificationTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_ctime, UDF_I_UCTIME(inode))) + efe->createTime = cpu_to_lets(cpu_time); + memset(&(efe->impIdent), 0, sizeof(EntityID)); + strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER); + efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); + efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); + efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); + efe->descTag.tagIdent = le16_to_cpu(TID_EXTENDED_FILE_ENTRY); + crclen = sizeof(struct ExtendedFileEntry); + } + fe->icbTag.strategyType = UDF_I_STRAT4096(inode) ? cpu_to_le16(4096) : + cpu_to_le16(4); + + if (S_ISDIR(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_DIRECTORY; + else if (S_ISREG(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_REGULAR; + else if (S_ISLNK(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_SYMLINK; + else if (S_ISBLK(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_BLOCK; + else if (S_ISCHR(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_CHAR; + else if (S_ISFIFO(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_FIFO; + + icbflags = UDF_I_ALLOCTYPE(inode) | + ((inode->i_mode & S_ISUID) ? ICB_FLAG_SETUID : 0) | + ((inode->i_mode & S_ISGID) ? ICB_FLAG_SETGID : 0) | + ((inode->i_mode & S_ISVTX) ? ICB_FLAG_STICKY : 0) | + (le16_to_cpu(fe->icbTag.flags) & + ~(ICB_FLAG_ALLOC_MASK | ICB_FLAG_SETUID | + ICB_FLAG_SETGID | ICB_FLAG_STICKY)); + + fe->icbTag.flags = cpu_to_le16(icbflags); + fe->descTag.descVersion = cpu_to_le16(2); + fe->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); + fe->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); + crclen += UDF_I_LENEATTR(inode) + UDF_I_LENALLOC(inode) - sizeof(tag); + fe->descTag.descCRCLength = cpu_to_le16(crclen); + fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0)); + + fe->descTag.tagChecksum = 0; + for (i=0; i<16; i++) + if (i != 4) + fe->descTag.tagChecksum += ((Uint8 *)&(fe->descTag))[i]; + + /* write the data blocks */ + mark_buffer_dirty(bh, 1); + if (do_sync) + { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + { + printk("IO error syncing udf inode [%s:%08lx]\n", + bdevname(inode->i_dev), inode->i_ino); + err = -EIO; + } + } + udf_release_data(bh); + return err; +} + +/* + * udf_iget + * + * PURPOSE + * Get an inode. + * + * DESCRIPTION + * This routine replaces iget() and read_inode(). + * + * HISTORY + * October 3, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * 12/19/98 dgb Added semaphore and changed to be a wrapper of iget + */ +struct inode * +udf_iget(struct super_block *sb, lb_addr ino) +{ + struct inode *inode; + unsigned long block; + + block = udf_get_lb_pblock(sb, ino, 0); + + down(&read_semaphore); /* serialize access to UDF_SB_LOCATION() */ + /* This is really icky.. should fix -- blf */ + + /* put the location where udf_read_inode can find it */ + memcpy(&UDF_SB_LOCATION(sb), &ino, sizeof(lb_addr)); + + /* Get the inode */ + + inode = iget(sb, block); + /* calls udf_read_inode() ! */ + + up(&read_semaphore); + + if (!inode) + { + printk(KERN_ERR "udf: iget() failed\n"); + return NULL; + } + else if (is_bad_inode(inode)) + { + iput(inode); + return NULL; + } + + if ( ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum) ) + { + udf_debug("block=%d, partition=%d out of range\n", + ino.logicalBlockNum, ino.partitionReferenceNum); + return NULL; + } + + return inode; +} + +int udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr eloc, Uint32 elen, struct buffer_head **bh, int inc) +{ + int adsize; + short_ad *sad = NULL; + long_ad *lad = NULL; + struct AllocExtDesc *aed; + int ret; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + } + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + return -1; + + if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize) + { + char *sptr, *dptr; + struct buffer_head *nbh; + int err, loffset; + Uint32 lblock = bloc->logicalBlockNum; + Uint16 lpart = bloc->partitionReferenceNum; + + if (!(bloc->logicalBlockNum = udf_new_block(inode, + lpart, lblock, &err))) + { + return -1; + } + if (!(nbh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, + *bloc, 0), inode->i_sb->s_blocksize))) + { + return -1; + } + aed = (struct AllocExtDesc *)(nbh->b_data); + aed->previousAllocExtLocation = cpu_to_le32(lblock); + if (*extoffset + adsize > inode->i_sb->s_blocksize) + { + loffset = *extoffset; + aed->lengthAllocDescs = cpu_to_le32(adsize); + sptr = (*bh)->b_data + *extoffset - adsize; + dptr = nbh->b_data + sizeof(struct AllocExtDesc); + memcpy(dptr, sptr, adsize); + *extoffset = sizeof(struct AllocExtDesc) + adsize; + } + else + { + loffset = *extoffset + adsize; + aed->lengthAllocDescs = cpu_to_le32(0); + sptr = (*bh)->b_data + *extoffset; + *extoffset = sizeof(struct AllocExtDesc); + + if (UDF_I_LOCATION(inode).logicalBlockNum == lblock) + UDF_I_LENALLOC(inode) += adsize; + else + { + aed = (struct AllocExtDesc *)(*bh)->b_data; + aed->lengthAllocDescs = + cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); + } + } + udf_new_tag(nbh->b_data, TID_ALLOC_EXTENT_DESC, 2, 1, + bloc->logicalBlockNum, sizeof(tag)); + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + sad = (short_ad *)sptr; + sad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize; + sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); + break; + } + case ICB_FLAG_AD_LONG: + { + lad = (long_ad *)sptr; + lad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize; + lad->extLocation = cpu_to_lelb(*bloc); + break; + } + } + udf_update_tag((*bh)->b_data, loffset); + mark_buffer_dirty(*bh, 1); + udf_release_data(*bh); + *bh = nbh; + } + + ret = udf_write_aext(inode, *bloc, extoffset, eloc, elen, bh, inc); + + if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr))) + { + UDF_I_LENALLOC(inode) += adsize; + mark_inode_dirty(inode); + } + else + { + aed = (struct AllocExtDesc *)(*bh)->b_data; + aed->lengthAllocDescs = + cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); + udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); + mark_buffer_dirty(*bh, 1); + } + + return ret; +} + +int udf_write_aext(struct inode *inode, lb_addr bloc, int *extoffset, + lb_addr eloc, Uint32 elen, struct buffer_head **bh, int inc) +{ + int adsize; + short_ad *sad = NULL; + long_ad *lad = NULL; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, bloc, 0)); + return -1; + } + } + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + return -1; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + sad = (short_ad *)((*bh)->b_data + *extoffset); + sad->extLength = cpu_to_le32(elen); + sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); + break; + } + case ICB_FLAG_AD_LONG: + { + lad = (long_ad *)((*bh)->b_data + *extoffset); + lad->extLength = cpu_to_le32(elen); + lad->extLocation = cpu_to_lelb(eloc); + break; + } + } + + if (memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(*bh)->b_data; + udf_update_tag((*bh)->b_data, + le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct AllocExtDesc)); + } + + mark_buffer_dirty(*bh, 1); + + if (inc) + *extoffset += adsize; + return (elen >> 30); +} + +int udf_next_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr *eloc, Uint32 *elen, struct buffer_head **bh, int inc) +{ + int pos, alen; + Uint8 etype; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + } + + if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr))) + { + pos = udf_file_entry_alloc_offset(inode); + alen = UDF_I_LENALLOC(inode) + pos; + } + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(*bh)->b_data; + + pos = sizeof(struct AllocExtDesc); + alen = le32_to_cpu(aed->lengthAllocDescs) + pos; + } + + if (!(*extoffset)) + *extoffset = pos; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + short_ad *sad; + + if (!(sad = udf_get_fileshortad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + if ((etype = le32_to_cpu(sad->extLength) >> 30) == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + bloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + *extoffset = 0; + udf_release_data(*bh); + *bh = NULL; + return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc); + } + else + { + eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; + } + break; + } + case ICB_FLAG_AD_LONG: + { + long_ad *lad; + + if (!(lad = udf_get_filelongad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + if ((etype = le32_to_cpu(lad->extLength) >> 30) == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + *bloc = lelb_to_cpu(lad->extLocation); + *extoffset = 0; + udf_release_data(*bh); + *bh = NULL; + return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc); + } + else + { + *eloc = lelb_to_cpu(lad->extLocation); + *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; + } + break; + } + default: + { + udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + return -1; + } + } + if (*elen) + return etype; + + udf_debug("Empty Extent!\n"); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + *extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + *extoffset -= sizeof(long_ad); + return -1; +} + +int udf_current_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr *eloc, Uint32 *elen, struct buffer_head **bh, int inc) +{ + int pos, alen; + Uint8 etype; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + } + + if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr))) + { + if (!(UDF_I_EXTENDED_FE(inode))) + pos = sizeof(struct FileEntry) + UDF_I_LENEATTR(inode); + else + pos = sizeof(struct ExtendedFileEntry) + UDF_I_LENEATTR(inode); + alen = UDF_I_LENALLOC(inode) + pos; + } + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(*bh)->b_data; + + pos = sizeof(struct AllocExtDesc); + alen = le32_to_cpu(aed->lengthAllocDescs) + pos; + } + + if (!(*extoffset)) + *extoffset = pos; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + short_ad *sad; + + if (!(sad = udf_get_fileshortad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + etype = le32_to_cpu(sad->extLength) >> 30; + eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + } + case ICB_FLAG_AD_LONG: + { + long_ad *lad; + + if (!(lad = udf_get_filelongad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + etype = le32_to_cpu(lad->extLength) >> 30; + *eloc = lelb_to_cpu(lad->extLocation); + *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + } + default: + { + udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + return -1; + } + } + if (*elen) + return etype; + + udf_debug("Empty Extent!\n"); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + *extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + *extoffset -= sizeof(long_ad); + return -1; +} + +int udf_insert_aext(struct inode *inode, lb_addr bloc, int extoffset, + lb_addr neloc, Uint32 nelen, struct buffer_head *bh) +{ + lb_addr oeloc; + Uint32 oelen; + int type; + + if (!bh) + { + if (!(bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, bloc, 0)); + return -1; + } + } + + while ((type = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1) + { + udf_write_aext(inode, bloc, &extoffset, neloc, nelen, &bh, 1); + + neloc = oeloc; + nelen = (type << 30) | oelen; + } + udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1); + return (nelen >> 30); +} + +int udf_delete_aext(struct inode *inode, lb_addr nbloc, int nextoffset, + lb_addr eloc, Uint32 elen, struct buffer_head *nbh) +{ + struct buffer_head *obh; + lb_addr obloc; + int oextoffset, adsize; + char type; + struct AllocExtDesc *aed; + + if (!(nbh)) + { + if (!(nbh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, nbloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, nbloc, 0)); + return -1; + } + } + else + atomic_inc(&nbh->b_count); + atomic_inc(&nbh->b_count); + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + adsize = 0; + + obh = nbh; + obloc = nbloc; + oextoffset = nextoffset; + + if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1) + return -1; + + while ((type = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) + { + udf_write_aext(inode, obloc, &oextoffset, eloc, (type << 30) | elen, &obh, 1); + if (memcmp(&nbloc, &obloc, sizeof(lb_addr))) + { + obloc = nbloc; + udf_release_data(obh); + atomic_inc(&nbh->b_count); + obh = nbh; + oextoffset = nextoffset - adsize; + } + } + memset(&eloc, 0x00, sizeof(lb_addr)); + elen = 0; + + if (memcmp(&nbloc, &obloc, sizeof(lb_addr))) + { + udf_free_blocks(inode, nbloc, 0, 1); + udf_write_aext(inode, obloc, &oextoffset, eloc, elen, &obh, 1); + udf_write_aext(inode, obloc, &oextoffset, eloc, elen, &obh, 1); + if (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr))) + { + UDF_I_LENALLOC(inode) -= (adsize * 2); + mark_inode_dirty(inode); + } + else + { + aed = (struct AllocExtDesc *)(obh)->b_data; + aed->lengthAllocDescs = + cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); + udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); + mark_buffer_dirty(obh, 1); + } + } + else + { + udf_write_aext(inode, obloc, &oextoffset, eloc, elen, &obh, 1); + if (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr))) + { + UDF_I_LENALLOC(inode) -= adsize; + mark_inode_dirty(inode); + } + else + { + aed = (struct AllocExtDesc *)(obh)->b_data; + aed->lengthAllocDescs = + cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); + udf_update_tag((obh)->b_data, oextoffset - adsize); + mark_buffer_dirty(obh, 1); + } + } + + udf_release_data(nbh); + udf_release_data(obh); + return (elen >> 30); +} + +int inode_bmap(struct inode *inode, int block, lb_addr *bloc, Uint32 *extoffset, + lb_addr *eloc, Uint32 *elen, Uint32 *offset, struct buffer_head **bh) +{ + int etype, lbcount = 0, b_off; + + if (block < 0) + { + printk(KERN_ERR "udf: inode_bmap: block < 0\n"); + return 0; + } + if (!inode) + { + printk(KERN_ERR "udf: inode_bmap: NULL inode\n"); + return 0; + } + + b_off = block << inode->i_sb->s_blocksize_bits; + *bloc = UDF_I_LOCATION(inode); + *eloc = UDF_I_EXT0LOC(inode); + *elen = UDF_I_EXT0LEN(inode) & UDF_EXTENT_LENGTH_MASK; + *extoffset = udf_file_entry_alloc_offset(inode); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + *extoffset += sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + *extoffset += sizeof(long_ad); + etype = UDF_I_EXT0LEN(inode) >> 30; + + while (lbcount + *elen <= b_off) + { + lbcount += *elen; + if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1) + { + *offset = (b_off - lbcount) >> inode->i_sb->s_blocksize_bits; + return -1; + } + } + *offset = (b_off - lbcount) >> inode->i_sb->s_blocksize_bits; + + return etype; +} + +long udf_locked_block_map(struct inode *inode, long block) +{ + lb_addr eloc, bloc; + Uint32 offset, extoffset, elen; + struct buffer_head *bh = NULL; + int ret; + + if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + ret = udf_get_lb_pblock(inode->i_sb, eloc, offset); + else + ret = 0; + + if (bh) + udf_release_data(bh); + + if (UDF_SB(inode->i_sb)->s_flags & UDF_FLAG_VARCONV) + return udf_fixed_to_variable(ret); + else + return ret; +} + +long udf_block_map(struct inode *inode, long block) +{ + int ret; + + lock_kernel(); + ret = udf_locked_block_map(inode, block); + unlock_kernel(); + return ret; +} diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c new file mode 100644 index 000000000..91ead3d39 --- /dev/null +++ b/fs/udf/lowlevel.c @@ -0,0 +1,149 @@ +/* + * lowlevel.c + * + * PURPOSE + * Low Level Device Routines for the UDF filesystem + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1999 Ben Fennema + * + * HISTORY + * + * 03/26/99 blf Created. + */ + +#include "udfdecl.h" + +#include <linux/blkdev.h> +#include <linux/cdrom.h> +#include <asm/uaccess.h> +#include <scsi/scsi.h> + +typedef struct scsi_device Scsi_Device; +typedef struct scsi_cmnd Scsi_Cmnd; + +#include <scsi/scsi_ioctl.h> + +#include <linux/udf_fs.h> +#include "udf_sb.h" + +unsigned int +udf_get_last_session(kdev_t 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; + + vol_desc_start=0; + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + /* Whoops. We must save the old FS, since otherwise + * we would destroy the kernels idea about FS on root + * mount in read_super... [chexum] + */ + mm_segment_t old_fs=get_fs(); + 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(old_fs); + +#define WE_OBEY_THE_WRITTEN_STANDARDS 1 + + if (i == 0) + { + udf_debug("XA disk: %s, vol_desc_start=%d\n", + (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); +#if WE_OBEY_THE_WRITTEN_STANDARDS + if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ +#endif + vol_desc_start = ms_info.addr.lba; + } + else + { + udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); + } + } + else + { + udf_debug("Device doesn't know how to ioctl?\n"); + } + return vol_desc_start; +} + +unsigned int +udf_get_last_block(kdev_t dev, int *flags) +{ + extern int *blksize_size[]; + struct inode inode_fake; + extern struct file_operations * get_blkfops(unsigned int); + int ret; + unsigned long lblock; + unsigned int hbsize = get_hardblocksize(dev); + unsigned int mult = 0; + unsigned int div = 0; + + if (!hbsize) + hbsize = 512; + + if (hbsize > blksize_size[MAJOR(dev)][MINOR(dev)]) + mult = hbsize / blksize_size[MAJOR(dev)][MINOR(dev)]; + else if (blksize_size[MAJOR(dev)][MINOR(dev)] > hbsize) + div = blksize_size[MAJOR(dev)][MINOR(dev)] / hbsize; + + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + /* Whoops. We must save the old FS, since otherwise + * we would destroy the kernels idea about FS on root + * mount in read_super... [chexum] + */ + mm_segment_t old_fs=get_fs(); + inode_fake.i_rdev=dev; + set_fs(KERNEL_DS); + + lblock = 0; + ret = get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + BLKGETSIZE, + (unsigned long) &lblock); + + if (!ret) /* Hard Disk */ + { + if (mult) + lblock *= mult; + else if (div) + lblock /= div; + } + else /* CDROM */ + { + ret = get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROM_LAST_WRITTEN, + (unsigned long) &lblock); + } + + set_fs(old_fs); + if (!ret) + return lblock - 1; + } + else + { + udf_debug("Device doesn't know how to ioctl?\n"); + } + return 0; +} diff --git a/fs/udf/misc.c b/fs/udf/misc.c new file mode 100644 index 000000000..2d7eb08f4 --- /dev/null +++ b/fs/udf/misc.c @@ -0,0 +1,540 @@ +/* + * misc.c + * + * PURPOSE + * Miscellaneous routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998 Dave Boynton + * (C) 1998-1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 04/19/99 blf partial support for reading/writing specific EA's + */ + + +#if defined(__linux__) && defined(__KERNEL__) + +#include "udfdecl.h" + +#include "udf_sb.h" +#include "udf_i.h" + +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/udf_fs.h> + +#else + +#include "udfdecl.h" +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +int udf_blocksize=0; +int udf_errno=0; + +void +udf_setblocksize(int size) +{ + udf_blocksize=size; +} +#endif + +Uint32 +udf64_low32(Uint64 indat) +{ + return indat & 0x00000000FFFFFFFFULL; +} + +Uint32 +udf64_high32(Uint64 indat) +{ + return indat >> 32; +} + +/* + * udf_stamp_to_time + */ +time_t * +udf_stamp_to_time(time_t *dest, timestamp src) +{ + struct ktm tm; + + if ((!dest)) + return NULL; + + /* this is very rough. need to find source to mktime() */ + tm.tm_year=(src.year) - 1900; + tm.tm_mon=(src.month); + tm.tm_mday=(src.day); + tm.tm_hour=src.hour; + tm.tm_min=src.minute; + tm.tm_sec=src.second; + *dest = udf_converttime(&tm); + return dest; +} + +uid_t udf_convert_uid(int uidin) +{ + if ( uidin == -1 ) + return 0; + if ( uidin > (64*1024U - 1) ) /* 16 bit UID */ + return 0; + return uidin; +} + +gid_t udf_convert_gid(int gidin) +{ + if ( gidin == -1 ) + return 0; + if ( gidin > (64*1024U - 1) ) /* 16 bit GID */ + return 0; + return gidin; +} + +#if defined(__linux__) && defined(__KERNEL__) + +extern struct buffer_head * +udf_tread(struct super_block *sb, int block, int size) +{ + if (UDF_SB(sb)->s_flags & UDF_FLAG_VARCONV) + return bread(sb->s_dev, udf_fixed_to_variable(block), size); + else + return bread(sb->s_dev, block, size); +} + +extern struct GenericAttrFormat * +udf_add_extendedattr(struct inode * inode, Uint32 size, Uint32 type, + Uint8 loc, struct buffer_head **bh) +{ + Uint8 *ea = NULL, *ad = NULL; + long_ad eaicb; + int offset; + + *bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + struct FileEntry *fe; + + fe = (struct FileEntry *)(*bh)->b_data; + eaicb = fe->extendedAttrICB; + offset = sizeof(struct FileEntry); + } + else + { + struct ExtendedFileEntry *efe; + + efe = (struct ExtendedFileEntry *)(*bh)->b_data; + eaicb = efe->extendedAttrICB; + offset = sizeof(struct ExtendedFileEntry); + } + + ea = &(*bh)->b_data[offset]; + if (UDF_I_LENEATTR(inode)) + offset += UDF_I_LENEATTR(inode); + else + size += sizeof(struct ExtendedAttrHeaderDesc); + + ad = &(*bh)->b_data[offset]; + if (UDF_I_LENALLOC(inode)) + offset += UDF_I_LENALLOC(inode); + + offset = inode->i_sb->s_blocksize - offset; + + /* TODO - Check for FreeEASpace */ + + if (loc & 0x01 && offset >= size) + { + struct ExtendedAttrHeaderDesc *eahd; + eahd = (struct ExtendedAttrHeaderDesc *)ea; + + if (UDF_I_LENALLOC(inode)) + { + memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); + UDF_I_EXT0OFFS(inode) += size; + } + + if (UDF_I_LENEATTR(inode)) + { + /* check checksum/crc */ + if (le16_to_cpu(eahd->descTag.tagIdent) != TID_EXTENDED_ATTRE_HEADER_DESC || + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) + { + udf_release_data(*bh); + return NULL; + } + } + else + { + size -= sizeof(struct ExtendedAttrHeaderDesc); + UDF_I_LENEATTR(inode) += sizeof(struct ExtendedAttrHeaderDesc); + eahd->descTag.tagIdent = cpu_to_le16(TID_EXTENDED_ATTRE_HEADER_DESC); + eahd->descTag.descVersion = cpu_to_le16(2); + eahd->descTag.tagSerialNum = cpu_to_le16(1); + eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); + eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); + eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); + } + + offset = UDF_I_LENEATTR(inode); + if (type < 2048) + { + if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) + { + Uint32 aal = le32_to_cpu(eahd->appAttrLocation); + memmove(&ea[offset - aal + size], + &ea[aal], offset - aal); + offset -= aal; + eahd->appAttrLocation = cpu_to_le32(aal + size); + } + if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) + { + Uint32 ial = le32_to_cpu(eahd->impAttrLocation); + memmove(&ea[offset - ial + size], + &ea[ial], offset - ial); + offset -= ial; + eahd->impAttrLocation = cpu_to_le32(ial + size); + } + } + else if (type < 65536) + { + if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) + { + Uint32 aal = le32_to_cpu(eahd->appAttrLocation); + memmove(&ea[offset - aal + size], + &ea[aal], offset - aal); + offset -= aal; + eahd->appAttrLocation = cpu_to_le32(aal + size); + } + } + /* rewrite CRC + checksum of eahd */ + UDF_I_LENEATTR(inode) += size; + return (struct GenericAttrFormat *)&ea[offset]; + } + if (loc & 0x02) + { + } + udf_release_data(*bh); + return NULL; +} + +extern struct GenericAttrFormat * +udf_get_extendedattr(struct inode * inode, Uint32 type, Uint8 subtype, + struct buffer_head **bh) +{ + struct GenericAttrFormat *gaf; + Uint8 *ea = NULL; + long_ad eaicb; + Uint32 offset; + + *bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + struct FileEntry *fe; + + fe = (struct FileEntry *)(*bh)->b_data; + eaicb = fe->extendedAttrICB; + if (UDF_I_LENEATTR(inode)) + ea = fe->extendedAttr; + } + else + { + struct ExtendedFileEntry *efe; + + efe = (struct ExtendedFileEntry *)(*bh)->b_data; + eaicb = efe->extendedAttrICB; + if (UDF_I_LENEATTR(inode)) + ea = efe->extendedAttr; + } + + if (UDF_I_LENEATTR(inode)) + { + struct ExtendedAttrHeaderDesc *eahd; + eahd = (struct ExtendedAttrHeaderDesc *)ea; + + /* check checksum/crc */ + if (le16_to_cpu(eahd->descTag.tagIdent) != TID_EXTENDED_ATTRE_HEADER_DESC || + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) + { + udf_release_data(*bh); + return NULL; + } + + if (type < 2048) + offset = sizeof(struct ExtendedAttrHeaderDesc); + else if (type < 65536) + offset = le32_to_cpu(eahd->impAttrLocation); + else + offset = le32_to_cpu(eahd->appAttrLocation); + + while (offset < UDF_I_LENEATTR(inode)) + { + gaf = (struct GenericAttrFormat *)&ea[offset]; + if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) + return gaf; + else + offset += le32_to_cpu(gaf->attrLength); + } + } + + udf_release_data(*bh); + if (eaicb.extLength) + { + /* TODO */ + } + return NULL; +} + +extern struct buffer_head * +udf_read_untagged(struct super_block *sb, Uint32 block, Uint32 offset) +{ + struct buffer_head *bh = NULL; + + /* Read the block */ + bh = udf_tread(sb, block+offset, sb->s_blocksize); + if (!bh) + { + printk(KERN_ERR "udf: udf_read_untagged(%p,%d,%d) failed\n", + sb, block, offset); + return NULL; + } + return bh; +} + +/* + * udf_read_tagged + * + * PURPOSE + * Read the first block of a tagged descriptor. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +extern struct buffer_head * +udf_read_tagged(struct super_block *sb, Uint32 block, Uint32 location, Uint16 *ident) +{ + tag *tag_p; + struct buffer_head *bh = NULL; + register Uint8 checksum; + register int i; + + /* Read the block */ + if (block == 0xFFFFFFFF) + return NULL; + + bh = udf_tread(sb, block, sb->s_blocksize); + if (!bh) + { + udf_debug("block=%d, location=%d: read failed\n", block, location); + return NULL; + } + + tag_p = (tag *)(bh->b_data); + + *ident = le16_to_cpu(tag_p->tagIdent); + + if ( location != le32_to_cpu(tag_p->tagLocation) ) + { + udf_debug("location mismatch block %d, tag %d != %d\n", + block, le32_to_cpu(tag_p->tagLocation), location); + goto error_out; + } + + /* Verify the tag checksum */ + checksum = 0U; + for (i = 0; i < 4; i++) + checksum += (Uint8)(bh->b_data[i]); + for (i = 5; i < 16; i++) + checksum += (Uint8)(bh->b_data[i]); + if (checksum != tag_p->tagChecksum) { + printk(KERN_ERR "udf: tag checksum failed block %d\n", block); + goto error_out; + } + + /* Verify the tag version */ + if (le16_to_cpu(tag_p->descVersion) != 0x0002U && + le16_to_cpu(tag_p->descVersion) != 0x0003U) + { + udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n", + le16_to_cpu(tag_p->descVersion), block); + goto error_out; + } + + /* Verify the descriptor CRC */ + if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || + le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), + le16_to_cpu(tag_p->descCRCLength), 0)) + { + return bh; + } + udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", + block, le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); + +error_out: + brelse(bh); + return NULL; +} + +extern struct buffer_head * +udf_read_ptagged(struct super_block *sb, lb_addr loc, Uint32 offset, Uint16 *ident) +{ + return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), + loc.logicalBlockNum + offset, ident); +} + +void udf_release_data(struct buffer_head *bh) +{ + if (bh) + brelse(bh); +} + +#endif + +void udf_update_tag(char *data, int length) +{ + tag *tptr = (tag *)data; + int i; + + length -= sizeof(tag); + + tptr->tagChecksum = 0; + tptr->descCRCLength = le16_to_cpu(length); + tptr->descCRC = le16_to_cpu(udf_crc(data + sizeof(tag), length, 0)); + + for (i=0; i<16; i++) + if (i != 4) + tptr->tagChecksum += (Uint8)(data[i]); +} + +void udf_new_tag(char *data, Uint16 ident, Uint16 version, Uint16 snum, + Uint32 loc, int length) +{ + tag *tptr = (tag *)data; + tptr->tagIdent = le16_to_cpu(ident); + tptr->descVersion = le16_to_cpu(version); + tptr->tagSerialNum = le16_to_cpu(snum); + tptr->tagLocation = le32_to_cpu(loc); + udf_update_tag(data, length); +} + +#ifndef __KERNEL__ +/* + * udf_read_tagged_data + * + * PURPOSE + * Read the first block of a tagged descriptor. + * Usable from user-land. + * + * HISTORY + * 10/4/98 dgb: written + */ +int +udf_read_tagged_data(char *buffer, int size, int fd, int block, int offset) +{ + tag *tag_p; + register Uint8 checksum; + register int i; + unsigned long offs; + + if (!buffer) + { + udf_errno = 1; + return -1; + } + + if ( !udf_blocksize ) + { + udf_errno = 2; + return -1; + } + + if ( size < udf_blocksize ) + { + udf_errno=3; + return -1; + } + udf_errno=0; + + offs=(long)block * udf_blocksize; + if ( lseek(fd, offs, SEEK_SET) != offs ) { + udf_errno=4; + return -1; + } + + i=read(fd, buffer, udf_blocksize); + if ( i < udf_blocksize ) { + udf_errno=5; + return -1; + } + + tag_p = (tag *)(buffer); + + /* Verify the tag location */ + if ((block-offset) != tag_p->tagLocation) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: location mismatch block %d, tag %d\n", + block, tag_p->tagLocation); +#else + udf_errno=6; +#endif + goto error_out; + } + + /* Verify the tag checksum */ + checksum = 0U; + for (i = 0; i < 4; i++) + checksum += (Uint8)(buffer[i]); + for (i = 5; i < 16; i++) + checksum += (Uint8)(buffer[i]); + if (checksum != tag_p->tagChecksum) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: tag checksum failed\n"); +#else + udf_errno=7; +#endif + goto error_out; + } + + /* Verify the tag version */ + if (tag_p->descVersion != 0x0002U) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: tag version 0x%04x != 0x0002U\n", + tag_p->descVersion); +#else + udf_errno=8; +#endif + goto error_out; + } + + /* Verify the descriptor CRC */ + if (tag_p->descCRC == udf_crc(buffer + 16, tag_p->descCRCLength, 0)) { + udf_errno=0; + return 0; + } +#ifdef __KERNEL__ + printk(KERN_ERR "udf: crc failure in udf_read_tagged\n"); +#else + udf_errno=9; +#endif + +error_out: + return -1; +} +#endif diff --git a/fs/udf/namei.c b/fs/udf/namei.c new file mode 100644 index 000000000..01b3979c9 --- /dev/null +++ b/fs/udf/namei.c @@ -0,0 +1,1302 @@ +/* + * namei.c + * + * PURPOSE + * Inode name handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 12/12/98 blf Created. Split out the lookup code from dir.c + * 04/19/99 blf link, mknod, symlink support + * + */ + +#if defined(__linux__) && defined(__KERNEL__) +#include <linux/version.h> +#include "udf_i.h" +#include "udf_sb.h" +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/udf_fs.h> +#endif + +#include "udfdecl.h" + +static inline int udf_match(int len, const char * const name, struct qstr *qs) +{ + if (len != qs->len) + return 0; + return !memcmp(name, qs->name, len); +} + +int udf_write_fi(struct FileIdentDesc *cfi, struct FileIdentDesc *sfi, + struct udf_fileident_bh *fibh, + Uint8 *impuse, Uint8 *fileident) +{ + struct FileIdentDesc *efi; + Uint16 crclen = fibh->eoffset - fibh->soffset - sizeof(tag); + Uint16 crc; + Uint8 checksum = 0; + int i; + int offset, len; + int padlen = fibh->eoffset - fibh->soffset - cfi->lengthOfImpUse - cfi->lengthFileIdent - + sizeof(struct FileIdentDesc); + + crc = udf_crc((Uint8 *)cfi + sizeof(tag), sizeof(struct FileIdentDesc) - + sizeof(tag), 0); + efi = (struct FileIdentDesc *)(fibh->ebh->b_data + fibh->soffset); + if (fibh->sbh == fibh->ebh || + (!fileident && + (sizeof(struct FileIdentDesc) + (impuse ? cfi->lengthOfImpUse : 0)) + <= -fibh->soffset)) + { + memcpy((Uint8 *)sfi, (Uint8 *)cfi, sizeof(struct FileIdentDesc)); + + if (impuse) + memcpy(sfi->impUse, impuse, cfi->lengthOfImpUse); + + if (fileident) + memcpy(sfi->fileIdent + cfi->lengthOfImpUse, fileident, + cfi->lengthFileIdent); + + /* Zero padding */ + memset(sfi->fileIdent + cfi->lengthOfImpUse + cfi->lengthFileIdent, 0, + padlen); + + if (fibh->sbh == fibh->ebh) + crc = udf_crc((Uint8 *)sfi + sizeof(tag), crclen, 0); + else + { + crc = udf_crc((Uint8 *)sfi + sizeof(tag), crclen - fibh->eoffset, 0); + crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); + } + + sfi->descTag.descCRC = cpu_to_le32(crc); + sfi->descTag.descCRCLength = cpu_to_le16(crclen); + + for (i=0; i<16; i++) + if (i != 4) + checksum += ((Uint8 *)&sfi->descTag)[i]; + + sfi->descTag.tagChecksum = checksum; + + mark_buffer_dirty(fibh->sbh, 1); + } + else + { + offset = -fibh->soffset; + len = sizeof(struct FileIdentDesc); + + if (len <= offset) + memcpy((Uint8 *)sfi, (Uint8 *)cfi, len); + else + { + memcpy((Uint8 *)sfi, (Uint8 *)cfi, offset); + memcpy(fibh->ebh->b_data, (Uint8 *)cfi + offset, len - offset); + } + + offset -= len; + len = cfi->lengthOfImpUse; + + if (impuse) + { + if (offset <= 0) + memcpy(efi->impUse, impuse, len); + else if (sizeof(struct FileIdentDesc) + len <= -fibh->soffset) + memcpy(sfi->impUse, impuse, len); + else + { + memcpy(sfi->impUse, impuse, offset); + memcpy(efi->impUse + offset, impuse + offset, len - offset); + } + } + + offset -= len; + len = cfi->lengthFileIdent; + + if (fileident) + { + if (offset <= 0) + memcpy(efi->fileIdent + cfi->lengthOfImpUse, fileident, len); + else + { + memcpy(sfi->fileIdent + cfi->lengthOfImpUse, fileident, offset); + memcpy(efi->fileIdent + cfi->lengthOfImpUse + offset, + fileident + offset, len - offset); + } + } + + /* Zero padding */ + memset(efi->fileIdent + cfi->lengthOfImpUse + cfi->lengthFileIdent, 0x00, + padlen); + + if (sizeof(tag) < -fibh->soffset) + { + crc = udf_crc((Uint8 *)sfi + sizeof(tag), crclen - fibh->eoffset, 0); + crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); + } + else + crc = udf_crc((Uint8 *)fibh->ebh->b_data + fibh->eoffset - crclen, crclen, 0); + + if (&(efi->descTag.descCRC) < (Uint16 *)fibh->ebh->b_data) + { + sfi->descTag.descCRC = cpu_to_le16(crc); + sfi->descTag.descCRCLength = cpu_to_le16(crclen); + } + else + { + efi->descTag.descCRC = cpu_to_le16(crc); + efi->descTag.descCRCLength = cpu_to_le16(crclen); + } + + for (i=0; i<16; i++) + { + if (i != 4) + { + if (&(((Uint8 *)&efi->descTag)[i]) < (Uint8 *)fibh->ebh->b_data) + checksum += ((Uint8 *)&sfi->descTag)[i]; + else + checksum += ((Uint8 *)&efi->descTag)[i]; + } + } + + if (&(cfi->descTag.tagChecksum) < (Uint8 *)fibh->ebh->b_data) + sfi->descTag.tagChecksum = checksum; + else + efi->descTag.tagChecksum = checksum; + + mark_buffer_dirty(fibh->sbh, 1); + mark_buffer_dirty(fibh->ebh, 1); + } + return 0; +} + +static struct FileIdentDesc * +udf_find_entry(struct inode *dir, struct dentry *dentry, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi) +{ + struct FileIdentDesc *fi=NULL; + int f_pos, block; + int flen; + char fname[255]; + char *nameptr; + Uint8 lfi; + Uint16 liu; + int size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + struct buffer_head *bh = NULL; + + if (!dir) + return NULL; + + f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + + fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; + if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if (++offset < (elen >> dir->i_sb->s_blocksize_bits)) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return NULL; + } + + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + { + udf_debug("udf_tread failed: block=%d\n", block); + udf_release_data(bh); + return NULL; + } + + while ( (f_pos < size) ) + { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &offset, &bh); + liu = le16_to_cpu(cfi->lengthOfImpUse); + lfi = cfi->lengthFileIdent; + + if (!fi) + { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + return NULL; + } + + if (fibh->sbh == fibh->ebh) + { + nameptr = fi->fileIdent + liu; + } + else + { + int poffset; /* Unpaded ending offset */ + + poffset = fibh->soffset + sizeof(struct FileIdentDesc) + liu + lfi; + + if (poffset >= lfi) + nameptr = (Uint8 *)(fibh->ebh->b_data + poffset - lfi); + else + { + nameptr = fname; + memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); + memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); + } + } + + if ( (cfi->fileCharacteristics & FILE_DELETED) != 0 ) + { + if ( !IS_UNDELETE(dir->i_sb) ) + continue; + } + + if ( (cfi->fileCharacteristics & FILE_HIDDEN) != 0 ) + { + if ( !IS_UNHIDE(dir->i_sb) ) + continue; + } + + if (!lfi) + continue; + + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (udf_match(flen, fname, &(dentry->d_name))) + { + udf_release_data(bh); + return fi; + } + } + } + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + return NULL; +} + +/* + * udf_lookup + * + * PURPOSE + * Look-up the inode for a given name. + * + * DESCRIPTION + * Required - lookup_dentry() will return -ENOTDIR if this routine is not + * available for a directory. The filesystem is useless if this routine is + * not available for at least the filesystem's root directory. + * + * This routine is passed an incomplete dentry - it must be completed by + * calling d_add(dentry, inode). If the name does not exist, then the + * specified inode must be set to null. An error should only be returned + * when the lookup fails for a reason other than the name not existing. + * Note that the directory inode semaphore is held during the call. + * + * Refer to lookup_dentry() in fs/namei.c + * lookup_dentry() -> lookup() -> real_lookup() -> . + * + * PRE-CONDITIONS + * dir Pointer to inode of parent directory. + * dentry Pointer to dentry to complete. + * + * POST-CONDITIONS + * <return> Zero on success. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +struct dentry * +udf_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = NULL; + struct FileIdentDesc cfi, *fi; + struct udf_fileident_bh fibh; + +#ifdef UDF_RECOVERY + /* temporary shorthand for specifying files by inode number */ + if (!strncmp(dentry->d_name.name, ".B=", 3) ) + { + lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; + inode = udf_iget(dir->i_sb, lb); + if (!inode) + return ERR_PTR(-EACCES); + } + else +#endif /* UDF_RECOVERY */ + + if ((fi = udf_find_entry(dir, dentry, &fibh, &cfi))) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + + inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); + if ( !inode ) + return ERR_PTR(-EACCES); + } + d_add(dentry, inode); + return NULL; +} + +static struct FileIdentDesc * +udf_add_entry(struct inode *dir, struct dentry *dentry, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi, int *err) +{ + struct super_block *sb; + struct FileIdentDesc *fi=NULL; + struct ustr unifilename; + char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; + int namelen; + int f_pos; + int flen; + char *nameptr; + int size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + int nfidlen; + Uint8 lfi; + Uint16 liu; + int block; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + struct buffer_head *bh = NULL; + + *err = -EINVAL; + if (!dir || !dir->i_nlink) + return NULL; + sb = dir->i_sb; + + if (!dentry->d_name.len) + return NULL; + + if (dir->i_size == 0) + { + *err = -ENOENT; + return NULL; + } + + if ( !(udf_char_to_ustr(&unifilename, dentry->d_name.name, dentry->d_name.len)) ) + { + *err = -ENAMETOOLONG; + return NULL; + } + + if ( !(namelen = udf_UTF8toCS0(name, &unifilename, UDF_NAME_LEN)) ) + return 0; + + nfidlen = (sizeof(struct FileIdentDesc) + 0 + namelen + 3) & ~3; + + f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + + fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; + if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if (++offset < (elen >> dir->i_sb->s_blocksize_bits)) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return NULL; + } + + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return NULL; + + block = UDF_I_LOCATION(dir).logicalBlockNum; + + while ( (f_pos < size) ) + { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &offset, &bh); + liu = le16_to_cpu(cfi->lengthOfImpUse); + lfi = cfi->lengthFileIdent; + + if (!fi) + { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + return NULL; + } + + if (fibh->sbh == fibh->ebh) + nameptr = fi->fileIdent + liu; + else + { + int poffset; /* Unpaded ending offset */ + + poffset = fibh->soffset + sizeof(struct FileIdentDesc) + liu + lfi; + + if (poffset >= lfi) + nameptr = (char *)(fibh->ebh->b_data + poffset - lfi); + else + { + nameptr = fname; + memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); + memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); + } + } + + if ( (cfi->fileCharacteristics & FILE_DELETED) != 0 ) + { + if (((sizeof(struct FileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) + { + udf_release_data(bh); + cfi->descTag.tagSerialNum = cpu_to_le16(1); + cfi->fileVersionNum = cpu_to_le16(1); + cfi->fileCharacteristics = 0; + cfi->lengthFileIdent = namelen; + cfi->lengthOfImpUse = cpu_to_le16(0); + if (!udf_write_fi(cfi, fi, fibh, NULL, name)) + return fi; + else + return NULL; + } + } + + if (!lfi) + continue; + + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (udf_match(flen, fname, &(dentry->d_name))) + { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + *err = -EEXIST; + return NULL; + } + } + } + + f_pos += nfidlen; + + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB && + sb->s_blocksize - fibh->eoffset < nfidlen) + { + udf_release_data(bh); + bh = NULL; + fibh->soffset -= UDF_I_EXT0OFFS(dir); + fibh->eoffset -= UDF_I_EXT0OFFS(dir); + f_pos -= (UDF_I_EXT0OFFS(dir) >> 2); + udf_release_data(fibh->sbh); + if (!(fibh->sbh = fibh->ebh = udf_expand_adinicb(dir, &block, 1, err))) + return NULL; + bloc = UDF_I_LOCATION(dir); + extoffset = udf_file_entry_alloc_offset(dir); + } + else + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + + dir->i_size += nfidlen; + if (sb->s_blocksize - fibh->eoffset >= nfidlen) + { + fibh->soffset = fibh->eoffset; + fibh->eoffset += nfidlen; + if (fibh->sbh != fibh->ebh) + { + udf_release_data(fibh->sbh); + fibh->sbh = fibh->ebh; + } + + if (UDF_I_ALLOCTYPE(dir) != ICB_FLAG_AD_IN_ICB) + { + Uint32 lextoffset = extoffset; + if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) != + EXTENT_RECORDED_ALLOCATED) + { + udf_release_data(bh); + udf_release_data(fibh->sbh); + return NULL; + } + else + { + elen += nfidlen; + elen = (EXTENT_RECORDED_ALLOCATED << 30) | elen; + udf_write_aext(dir, bloc, &lextoffset, eloc, elen, &bh, 1); + block = eloc.logicalBlockNum + (elen >> dir->i_sb->s_blocksize_bits); + } + } + else + block = UDF_I_LOCATION(dir).logicalBlockNum; + + fi = (struct FileIdentDesc *)(fibh->sbh->b_data + fibh->soffset); + } + else + { + Uint32 lextoffset = extoffset; + + fibh->soffset = fibh->eoffset - sb->s_blocksize; + fibh->eoffset += nfidlen - sb->s_blocksize; + if (fibh->sbh != fibh->ebh) + { + udf_release_data(fibh->sbh); + fibh->sbh = fibh->ebh; + } + + if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) != + EXTENT_RECORDED_ALLOCATED) + { + udf_release_data(bh); + udf_release_data(fibh->sbh); + return NULL; + } + else + block = eloc.logicalBlockNum + (elen >> dir->i_sb->s_blocksize_bits); + + *err = -ENOSPC; + if (!(fibh->ebh = udf_getblk(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err))) + { + udf_release_data(bh); + udf_release_data(fibh->sbh); + return NULL; + } + if (!(fibh->soffset)) + { + if (udf_next_aext(dir, &bloc, &lextoffset, &eloc, &elen, &bh, 1) == + EXTENT_RECORDED_ALLOCATED) + { + block = eloc.logicalBlockNum + (elen >> dir->i_sb->s_blocksize_bits); + } + else + block ++; + } + + fi = (struct FileIdentDesc *)(fibh->sbh->b_data + sb->s_blocksize + fibh->soffset); + } + + memset(cfi, 0, sizeof(struct FileIdentDesc)); + udf_new_tag((char *)cfi, TID_FILE_IDENT_DESC, 2, 1, block, sizeof(tag)); + cfi->fileVersionNum = cpu_to_le16(1); + cfi->lengthFileIdent = namelen; + cfi->lengthOfImpUse = cpu_to_le16(0); + if (!udf_write_fi(cfi, fi, fibh, NULL, name)) + { + udf_release_data(bh); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + UDF_I_LENALLOC(dir) += nfidlen; + dir->i_version = ++event; + mark_inode_dirty(dir); + return fi; + } + else + { + udf_release_data(bh); + dir->i_size -= nfidlen; + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + return NULL; + } +} + +static int udf_delete_entry(struct FileIdentDesc *fi, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi) +{ + cfi->fileCharacteristics |= FILE_DELETED; + return udf_write_fi(cfi, fi, fibh, NULL, NULL); +} + +int udf_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct udf_fileident_bh fibh; + struct inode *inode; + struct FileIdentDesc cfi, *fi; + int err; + + inode = udf_new_inode(dir, mode, &err); + if (!inode) + return err; + + inode->i_op = &udf_file_inode_operations; + inode->i_mode = mode; + mark_inode_dirty(inode); + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + { + udf_debug("udf_add_entry failure!\n"); + inode->i_nlink --; + mark_inode_dirty(inode); + iput(inode); + return err; + } + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); + dir->i_version = ++event; + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + d_instantiate(dentry, inode); + return 0; +} + +int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) +{ + struct inode * inode; + struct udf_fileident_bh fibh; + int err; + struct FileIdentDesc cfi, *fi; + + err = -EIO; + inode = udf_new_inode(dir, mode, &err); + if (!inode) + goto out; + + inode->i_uid = current->fsuid; + inode->i_mode = mode; + inode->i_op = NULL; + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + { + udf_debug("udf_add_entry failure!\n"); + inode->i_nlink --; + mark_inode_dirty(inode); + iput(inode); + return err; + } + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); + dir->i_version = ++event; + } + if (S_ISREG(inode->i_mode)) + { + inode->i_op = &udf_file_inode_operations; + } + else if (S_ISCHR(inode->i_mode)) + { + inode->i_op = &chrdev_inode_operations; + } + else if (S_ISBLK(inode->i_mode)) + { + inode->i_op = &blkdev_inode_operations; + } + else if (S_ISFIFO(inode->i_mode)) + { + init_fifo(inode); + } + if (S_ISBLK(mode) || S_ISCHR(mode)) + inode->i_rdev = to_kdev_t(rdev); + mark_inode_dirty(inode); + + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + d_instantiate(dentry, inode); + err = 0; +out: + return err; +} + +int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + struct inode * inode; + struct udf_fileident_bh fibh; + int err; + struct FileEntry *fe; + struct FileIdentDesc cfi, *fi; + Uint32 loc; + + err = -EMLINK; + if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1) + goto out; + + err = -EIO; + inode = udf_new_inode(dir, S_IFDIR, &err); + if (!inode) + goto out; + + inode->i_op = &udf_dir_inode_operations; + inode->i_size = (sizeof(struct FileIdentDesc) + 3) & ~3; + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + UDF_I_EXT0LEN(inode) = inode->i_size; + UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); + UDF_I_LENALLOC(inode) = inode->i_size; + loc = UDF_I_LOCATION(inode).logicalBlockNum; + fibh.sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + } + else + { + fibh.sbh = udf_bread (inode, 0, 1, &err); + loc = UDF_I_EXT0LOC(inode).logicalBlockNum; + } + + if (!fibh.sbh) + { + inode->i_nlink--; + mark_inode_dirty(inode); + iput(inode); + goto out; + } + inode->i_nlink = 2; + fe = (struct FileEntry *)fibh.sbh->b_data; + fi = (struct FileIdentDesc *)&(fe->extendedAttr[UDF_I_LENEATTR(inode)]); + udf_new_tag((char *)&cfi, TID_FILE_IDENT_DESC, 2, 1, loc, + sizeof(struct FileIdentDesc)); + cfi.fileVersionNum = cpu_to_le16(1); + cfi.fileCharacteristics = FILE_DIRECTORY | FILE_PARENT; + cfi.lengthFileIdent = 0; + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL); + cfi.lengthOfImpUse = cpu_to_le16(0); + fibh.ebh = fibh.sbh; + fibh.soffset = sizeof(struct FileEntry); + fibh.eoffset = sizeof(struct FileEntry) + inode->i_size; + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + udf_release_data(fibh.sbh); + inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + if (dir->i_mode & S_ISGID) + inode->i_mode |= S_ISGID; + mark_inode_dirty(inode); + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + { + udf_debug("udf_add_entry failure!\n"); + inode->i_nlink = 0; + mark_inode_dirty(inode); + iput(inode); + goto out; + } + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + cfi.fileCharacteristics |= FILE_DIRECTORY; + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + dir->i_version = ++event; + dir->i_nlink++; + mark_inode_dirty(dir); + d_instantiate(dentry, inode); + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + err = 0; +out: + return err; +} + +static int empty_dir(struct inode *dir) +{ + struct FileIdentDesc *fi, cfi; + struct udf_fileident_bh fibh; + int f_pos; + int size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + int block; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + struct buffer_head *bh = NULL; + + f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + + fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; + if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if (++offset < (elen >> dir->i_sb->s_blocksize_bits)) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return 0; + } + + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + return 0; + + while ( (f_pos < size) ) + { + fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &offset, &bh); + + if (!fi) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 0; + } + + if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FILE_DELETED) == 0) + { + udf_release_data(bh); + return 0; + } + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; +} + +int udf_rmdir(struct inode * dir, struct dentry * dentry) +{ + int retval; + struct inode * inode; + struct udf_fileident_bh fibh; + struct FileIdentDesc *fi, cfi; + + retval = -ENOENT; + fi = udf_find_entry(dir, dentry, &fibh, &cfi); + if (!fi) + goto out; + + inode = dentry->d_inode; + + retval = -EIO; + if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) != inode->i_ino) + goto end_rmdir; + retval = -ENOTEMPTY; + if (!empty_dir(inode)) + goto end_rmdir; + retval = udf_delete_entry(fi, &fibh, &cfi); + dir->i_version = ++event; + if (retval) + goto end_rmdir; + if (inode->i_nlink != 2) + udf_warning(inode->i_sb, "udf_rmdir", + "empty directory has nlink != 2 (%d)", + inode->i_nlink); + inode->i_version = ++event; + inode->i_nlink = 0; + inode->i_size = 0; + mark_inode_dirty(inode); + dir->i_nlink --; + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; + mark_inode_dirty(dir); + d_delete(dentry); + +end_rmdir: + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); +out: + return retval; +} + +int udf_unlink(struct inode * dir, struct dentry * dentry) +{ + int retval; + struct inode * inode; + struct udf_fileident_bh fibh; + struct FileIdentDesc *fi; + struct FileIdentDesc cfi; + + retval = -ENOENT; + fi = udf_find_entry(dir, dentry, &fibh, &cfi); + if (!fi) + goto out; + + inode = dentry->d_inode; + + retval = -EIO; + + if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) != + inode->i_ino) + { + goto end_unlink; + } + + if (!inode->i_nlink) + { + udf_debug("Deleting nonexistent file (%lu), %d\n", + inode->i_ino, inode->i_nlink); + inode->i_nlink = 1; + } + retval = udf_delete_entry(fi, &fibh, &cfi); + if (retval) + goto end_unlink; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; + mark_inode_dirty(dir); + inode->i_nlink--; + mark_inode_dirty(inode); + inode->i_ctime = dir->i_ctime; + retval = 0; + d_delete(dentry); /* This also frees the inode */ + +end_unlink: + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); +out: + return retval; +} + +int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname) +{ + struct inode * inode; + struct PathComponent *pc; + struct udf_fileident_bh fibh; + struct buffer_head *bh = NULL; + int eoffset, elen = 0; + struct FileIdentDesc *fi; + struct FileIdentDesc cfi; + char *ea; + int err; + + if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) + goto out; + + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &udf_symlink_inode_operations; + + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + ea = bh->b_data + udf_file_entry_alloc_offset(inode); + + eoffset = inode->i_sb->s_blocksize - (ea - bh->b_data); + pc = (struct PathComponent *)ea; + + if (*symname == '/') + { + do + { + symname++; + } while (*symname == '/'); + + pc->componentType = 1; + pc->lengthComponentIdent = 0; + pc->componentFileVersionNum = 0; + pc += sizeof(struct PathComponent); + elen += sizeof(struct PathComponent); + } + + while (*symname && eoffset > elen + sizeof(struct PathComponent)) + { + char *compstart; + pc = (struct PathComponent *)(ea + elen); + + compstart = (char *)symname; + + do + { + symname++; + } while (*symname && *symname != '/'); + + pc->componentType = 5; + pc->lengthComponentIdent = 0; + pc->componentFileVersionNum = 0; + if (pc->componentIdent[0] == '.') + { + if (pc->lengthComponentIdent == 1) + pc->componentType = 4; + else if (pc->lengthComponentIdent == 2 && pc->componentIdent[1] == '.') + pc->componentType = 3; + } + + if (pc->componentType == 5) + { + if (elen + sizeof(struct PathComponent) + symname - compstart > eoffset) + pc->lengthComponentIdent = eoffset - elen - sizeof(struct PathComponent); + else + pc->lengthComponentIdent = symname - compstart; + + memcpy(pc->componentIdent, compstart, pc->lengthComponentIdent); + } + + elen += sizeof(struct PathComponent) + pc->lengthComponentIdent; + + if (*symname) + { + do + { + symname++; + } while (*symname == '/'); + } + } + + udf_release_data(bh); + UDF_I_LENALLOC(inode) = inode->i_size = elen; + mark_inode_dirty(inode); + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + goto out; + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + if (UDF_SB_LVIDBH(inode->i_sb)) + { + struct LogicalVolHeaderDesc *lvhd; + Uint64 uniqueID; + lvhd = (struct LogicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); + uniqueID = le64_to_cpu(lvhd->uniqueID); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + le32_to_cpu(uniqueID & 0x00000000FFFFFFFFUL); + if (!(++uniqueID & 0x00000000FFFFFFFFUL)) + uniqueID += 16; + lvhd->uniqueID = cpu_to_le64(uniqueID); + mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb), 1); + } + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); + dir->i_version = ++event; + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + d_instantiate(dentry, inode); + err = 0; + +out: + return err; +} + +int udf_link(struct dentry * old_dentry, struct inode * dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct udf_fileident_bh fibh; + int err; + struct FileIdentDesc cfi, *fi; + + if (S_ISDIR(inode->i_mode)) + return -EPERM; + + if (inode->i_nlink >= (256<<sizeof(inode->i_nlink))-1) + return -EMLINK; + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + return err; + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + if (UDF_SB_LVIDBH(inode->i_sb)) + { + struct LogicalVolHeaderDesc *lvhd; + Uint64 uniqueID; + lvhd = (struct LogicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); + uniqueID = le64_to_cpu(lvhd->uniqueID); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); + if (!(++uniqueID & 0x00000000FFFFFFFFUL)) + uniqueID += 16; + lvhd->uniqueID = cpu_to_le64(uniqueID); + mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb), 1); + } + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); + dir->i_version = ++event; + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + inode->i_nlink ++; + inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(inode) = CURRENT_UTIME; + mark_inode_dirty(inode); + inode->i_count ++; + d_instantiate(dentry, inode); + return 0; +} + +/* Anybody can rename anything with this: the permission checks are left to the + * higher-level routines. + */ +int udf_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) +{ + struct inode * old_inode, * new_inode; + struct udf_fileident_bh ofibh, nfibh; + struct FileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi; + struct buffer_head *dir_bh = NULL; + int retval = -ENOENT; + + old_inode = old_dentry->d_inode; + ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + if (!ofi || udf_get_lb_pblock(old_dir->i_sb, lelb_to_cpu(ocfi.icb.extLocation), 0) != + old_inode->i_ino) + { + goto end_rename; + } + + new_inode = new_dentry->d_inode; + nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); + if (nfi) + { + if (!new_inode) + { + if (nfibh.sbh != nfibh.ebh) + udf_release_data(nfibh.ebh); + udf_release_data(nfibh.sbh); + nfi = NULL; + } + else + { +/* + DQUOT_INIT(new_inode); +*/ + } + } + if (S_ISDIR(old_inode->i_mode)) + { + Uint32 offset = UDF_I_EXT0OFFS(old_inode); + + if (new_inode) + { + retval = -ENOTEMPTY; + if (!empty_dir(new_inode)) + goto end_rename; + } + retval = -EIO; + dir_bh = udf_bread(old_inode, 0, 0, &retval); + if (!dir_bh) + goto end_rename; + dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset); + if (!dir_fi) + goto end_rename; + if (udf_get_lb_pblock(old_inode->i_sb, cpu_to_lelb(dir_fi->icb.extLocation), 0) != + old_dir->i_ino) + { + goto end_rename; + } + retval = -EMLINK; + if (!new_inode && new_dir->i_nlink >= (256<<sizeof(new_dir->i_nlink))-1) + goto end_rename; + } + if (!nfi) + { + nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); + if (!nfi) + goto end_rename; + } + new_dir->i_version = ++event; + + /* + * ok, that's it + */ + ncfi.fileVersionNum = ocfi.fileVersionNum; + ncfi.fileCharacteristics = ocfi.fileCharacteristics; + memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad)); + udf_write_fi(&ncfi, nfi, &nfibh, NULL, NULL); + + udf_delete_entry(ofi, &ofibh, &ocfi); + + old_dir->i_version = ++event; + if (new_inode) + { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(new_inode) = CURRENT_UTIME; + mark_inode_dirty(new_inode); + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(old_dir) = UDF_I_UMTIME(old_dir) = CURRENT_UTIME; + mark_inode_dirty(old_dir); + + if (dir_bh) + { + dir_fi->icb.extLocation = lelb_to_cpu(UDF_I_LOCATION(new_dir)); + udf_update_tag((char *)dir_fi, sizeof(struct FileIdentDesc) + + cpu_to_le16(dir_fi->lengthOfImpUse)); + if (UDF_I_ALLOCTYPE(old_inode) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(old_inode); + old_inode->i_version = ++event; + } + else + mark_buffer_dirty(dir_bh, 1); + old_dir->i_nlink --; + mark_inode_dirty(old_dir); + if (new_inode) + { + new_inode->i_nlink --; + mark_inode_dirty(new_inode); + } + else + { + new_dir->i_nlink ++; + mark_inode_dirty(new_dir); + } + } + + retval = 0; + +end_rename: + udf_release_data(dir_bh); + if (ofi) + { + if (ofibh.sbh != ofibh.ebh) + udf_release_data(ofibh.ebh); + udf_release_data(ofibh.sbh); + } + if (nfi) + { + if (nfibh.sbh != nfibh.ebh) + udf_release_data(nfibh.ebh); + udf_release_data(nfibh.sbh); + } + return retval; +} diff --git a/fs/udf/partition.c b/fs/udf/partition.c new file mode 100644 index 000000000..ecfabb849 --- /dev/null +++ b/fs/udf/partition.c @@ -0,0 +1,190 @@ +/* + * partition.c + * + * PURPOSE + * Partition handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Ben Fennema + * + * HISTORY + * + * 12/06/98 blf Created file. + * + */ + +#include "udfdecl.h" +#include "udf_sb.h" +#include "udf_i.h" + +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/udf_fs.h> + +extern Uint32 udf_get_pblock(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + Uint16 ident; + + if (partition >= UDF_SB_NUMPARTS(sb)) + { + udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", + block, partition, offset); + return 0xFFFFFFFF; + } + switch (UDF_SB_PARTTYPE(sb, partition)) + { + case UDF_TYPE1_MAP15: + { + return UDF_SB_PARTROOT(sb, partition) + block + offset; + } + case UDF_VIRTUAL_MAP15: + case UDF_VIRTUAL_MAP20: + { + struct buffer_head *bh = NULL; + Uint32 newblock; + Uint32 index; + Uint32 loc; + + index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(Uint32); + + + if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) + { + udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", + block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); + return 0xFFFFFFFF; + } + + if (block >= index) + { + block -= index; + newblock = 1 + (block / (sb->s_blocksize / sizeof(Uint32))); + index = block % (sb->s_blocksize / sizeof(Uint32)); + } + else + { + newblock = 0; + index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(Uint32) + block; + } + + loc = udf_locked_block_map(UDF_SB_VAT(sb), newblock); + + if (!(bh = bread(sb->s_dev, loc, sb->s_blocksize))) + { + udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", + sb, block, partition, loc, index); + return 0xFFFFFFFF; + } + + loc = le32_to_cpu(((Uint32 *)bh->b_data)[index]); + + udf_release_data(bh); + + if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) + { + udf_debug("recursive call to udf_get_pblock!\n"); + return 0xFFFFFFFF; + } + + return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); + } + case UDF_SPARABLE_MAP15: + { + Uint32 newblock = UDF_SB_PARTROOT(sb, partition) + block + offset; + Uint32 spartable = UDF_SB_TYPESPAR(sb, partition).s_spar_loc; + Uint32 plength = UDF_SB_TYPESPAR(sb,partition).s_spar_plen; + Uint32 packet = (block + offset) & (~(plength-1)); + struct buffer_head *bh = NULL; + struct SparingTable *st; + SparingEntry *se; + + bh = udf_read_tagged(sb, spartable, spartable, &ident); + + if (!bh) + { + printk(KERN_ERR "udf: udf_read_tagged(%p,%d,%d)\n", + sb, spartable, spartable); + return 0xFFFFFFFF; + } + + st = (struct SparingTable *)bh->b_data; + if (ident == 0) + { + if (!strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) + { + Uint16 rtl = le16_to_cpu(st->reallocationTableLen); + Uint16 index; + + /* If the sparing table span multiple blocks, find out which block we are on */ + + se = &(st->mapEntry[0]); + + if (rtl * sizeof(SparingEntry) + sizeof(struct SparingTable) > sb->s_blocksize) + { + index = (sb->s_blocksize - sizeof(struct SparingTable)) / sizeof(SparingEntry); + if (le32_to_cpu(se[index-1].origLocation) == packet) + { + udf_release_data(bh); + return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); + } + else if (le32_to_cpu(se[index-1].origLocation) < packet) + { + do + { + udf_release_data(bh); + bh = udf_tread(sb, spartable, sb->s_blocksize); + if (!bh) + return 0xFFFFFFFF; + se = (SparingEntry *)bh->b_data; + spartable ++; + rtl -= index; + index = sb->s_blocksize / sizeof(SparingEntry); + + if (le32_to_cpu(se[index].origLocation) == packet) + { + udf_release_data(bh); + return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); + } + } while (rtl * sizeof(SparingEntry) > sb->s_blocksize && + le32_to_cpu(se[index-1].origLocation) < packet); + } + } + + for (index=0; index<rtl; index++) + { + if (le32_to_cpu(se[index].origLocation) == packet) + { + udf_release_data(bh); + return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); + } + else if (le32_to_cpu(se[index].origLocation) > packet) + { + udf_release_data(bh); + return newblock; + } + } + + udf_release_data(bh); + return newblock; + } + } + udf_release_data(bh); + } + } + return 0xFFFFFFFF; +} + +extern Uint32 udf_get_lb_pblock(struct super_block *sb, lb_addr loc, Uint32 offset) +{ + return udf_get_pblock(sb, loc.logicalBlockNum, loc.partitionReferenceNum, offset); +} diff --git a/fs/udf/super.c b/fs/udf/super.c new file mode 100644 index 000000000..c6ff2c6e2 --- /dev/null +++ b/fs/udf/super.c @@ -0,0 +1,1623 @@ +/* + * super.c + * + * PURPOSE + * Super block routines for the OSTA-UDF(tm) filesystem. + * + * DESCRIPTION + * OSTA-UDF(tm) = Optical Storage Technology Association + * Universal Disk Format. + * + * This code is based on version 2.00 of the UDF specification, + * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346]. + * http://www.osta.org/ + * http://www.ecma.ch/ + * http://www.iso.org/ + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998 Dave Boynton + * (C) 1998-1999 Ben Fennema + * + * HISTORY + * + * 09/24/98 dgb changed to allow compiling outside of kernel, and + * added some debugging. + * 10/01/98 dgb updated to allow (some) possibility of compiling w/2.0.34 + * 10/16/98 attempting some multi-session support + * 10/17/98 added freespace count for "df" + * 11/11/98 gr added novrs option + * 11/26/98 dgb added fileset,anchor mount options + * 12/06/98 blf really hosed things royally. vat/sparing support. sequenced vol descs + * rewrote option handling based on isofs + * 12/20/98 find the free space bitmap (if it exists) + */ + +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +#include "udfdecl.h" + +#include <linux/blkdev.h> +#include <linux/malloc.h> +#include <linux/kernel.h> +#include <linux/locks.h> +#include <linux/module.h> +#include <linux/stat.h> +#include <linux/cdrom.h> +#include <linux/nls.h> +#include <asm/byteorder.h> + +#include <linux/udf_fs.h> +#include "udf_sb.h" +#include "udf_i.h" + +#include <linux/init.h> +#include <asm/uaccess.h> + +static char error_buf[1024]; + +/* These are the "meat" - everything else is stuffing */ +static struct super_block *udf_read_super(struct super_block *, void *, int); +static void udf_put_super(struct super_block *); +static int udf_remount_fs(struct super_block *, int *, char *); +static int udf_check_valid(struct super_block *, int, int); +static int udf_vrs(struct super_block *sb, int silent); +static int udf_load_partition(struct super_block *, lb_addr *); +static int udf_load_logicalvol(struct super_block *, struct buffer_head *, lb_addr *); +static void udf_load_logicalvolint(struct super_block *, extent_ad); +static int udf_find_anchor(struct super_block *, int, int); +static int udf_find_fileset(struct super_block *, lb_addr *, lb_addr *); +static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); +static void udf_load_fileset(struct super_block *, struct buffer_head *, lb_addr *); +static void udf_load_partdesc(struct super_block *, struct buffer_head *); +static void udf_open_lvid(struct super_block *); +static void udf_close_lvid(struct super_block *); +static unsigned int udf_count_free(struct super_block *); + +/* version specific functions */ +static int udf_statfs(struct super_block *, struct statfs *, int); + +/* UDF filesystem type */ +static struct file_system_type udf_fstype = { + "udf", /* name */ + FS_REQUIRES_DEV, /* fs_flags */ + udf_read_super, /* read_super */ + NULL /* next */ +}; + +/* Superblock operations */ +static struct super_operations udf_sb_ops = +{ + udf_read_inode, /* read_inode */ +#ifdef CONFIG_UDF_RW + udf_write_inode, /* write_inode */ +#else + NULL, /* write_inode */ +#endif + udf_put_inode, /* put_inode */ +#ifdef CONFIG_UDF_RW + udf_delete_inode, /* delete_inode */ +#else + NULL, /* delete_inode */ +#endif + NULL, /* notify_change */ + udf_put_super, /* put_super */ + NULL, /* write_super */ + udf_statfs, /* statfs */ + udf_remount_fs, /* remount_fs */ + NULL, /* clear_inode */ + NULL, /* umount_begin */ +}; + +struct udf_options +{ + unsigned char novrs; + unsigned char utf8; + unsigned int blocksize; + unsigned int session; + unsigned int lastblock; + unsigned int anchor; + unsigned int volume; + unsigned short partition; + unsigned int fileset; + unsigned int rootdir; + unsigned int flags; + mode_t umask; + gid_t gid; + uid_t uid; + char *iocharset; +}; + +#if defined(MODULE) + +/* + * cleanup_module + * + * PURPOSE + * Unregister the UDF filesystem type. + * + * DESCRIPTION + * Clean-up before the module is unloaded. + * This routine only applies when compiled as a module. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int +cleanup_module(void) +{ + printk(KERN_NOTICE "udf: unregistering filesystem\n"); + return unregister_filesystem(&udf_fstype); +} + +/* + * init_module / init_udf_fs + * + * PURPOSE + * Register the UDF filesystem type. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int init_module(void) +#else /* if !defined(MODULE) */ +int __init init_udf_fs(void) +#endif +{ + printk(KERN_NOTICE "udf: registering filesystem\n"); + { + struct super_block sb; + int size; + + size = sizeof(struct super_block) + + (long)&sb.u - (long)&sb; + if ( size < sizeof(struct udf_sb_info) ) + { + printk(KERN_ERR "udf: Danger! Kernel was compiled without enough room for udf_sb_info\n"); + printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %u\n", + size, sizeof(struct udf_sb_info)); + return 0; + } + } + return register_filesystem(&udf_fstype); +} + +/* + * udf_parse_options + * + * PURPOSE + * Parse mount options. + * + * DESCRIPTION + * The following mount options are supported: + * + * gid= Set the default group. + * umask= Set the default umask. + * uid= Set the default user. + * unhide Show otherwise hidden files. + * undelete Show deleted files in lists. + * strict Set strict conformance (unused) + * utf8 (unused) + * iocharset (unused) + * + * The remaining are for debugging and disaster recovery: + * + * bs= Set the block size. (may not work unless 2048) + * novrs Skip volume sequence recognition + * + * The following expect a offset from 0. + * + * session= Set the CDROM session (default= last session) + * anchor= Override standard anchor location. (default= 256) + * volume= Override the VolumeDesc location. (unused) + * partition= Override the PartitionDesc location. (unused) + * lastblock= Set the last block of the filesystem/ + * + * The following expect a offset from the partition root. + * + * fileset= Override the fileset block location. (unused) + * rootdir= Override the root directory location. (unused) + * WARNING: overriding the rootdir to a non-directory may + * yield highly unpredictable results. + * + * PRE-CONDITIONS + * options Pointer to mount options string. + * uopts Pointer to mount options variable. + * + * POST-CONDITIONS + * <return> 0 Mount options parsed okay. + * <return> -1 Error parsing mount options. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +static int +udf_parse_options(char *options, struct udf_options *uopt) +{ + char *opt, *val; + + uopt->novrs = 0; + uopt->blocksize = 2048; + uopt->partition = 0xFFFF; + uopt->session = 0xFFFFFFFF; + uopt->lastblock = 0xFFFFFFFF; + uopt->anchor = 0xFFFFFFFF; + uopt->volume = 0xFFFFFFFF; + uopt->rootdir = 0xFFFFFFFF; + uopt->fileset = 0xFFFFFFFF; + uopt->iocharset = NULL; + + if (!options) + return 1; + + for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) + { + /* Make "opt=val" into two strings */ + val = strchr(opt, '='); + if (val) + *(val++) = 0; + if (!strcmp(opt, "novrs") && !val) + uopt->novrs = 1; + else if (!strcmp(opt, "utf8") && !val) + uopt->utf8 = 1; + else if (!strcmp(opt, "bs") && val) + uopt->blocksize = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "unhide") && !val) + uopt->flags |= UDF_FLAG_UNHIDE; + else if (!strcmp(opt, "undelete") && !val) + uopt->flags |= UDF_FLAG_UNDELETE; + else if (!strcmp(opt, "gid") && val) + uopt->gid = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "umask") && val) + uopt->umask = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "strict") && !val) + uopt->flags |= UDF_FLAG_STRICT; + else if (!strcmp(opt, "uid") && val) + uopt->uid = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "session") && val) + uopt->session = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "lastblock") && val) + uopt->lastblock = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "anchor") && val) + uopt->anchor = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "volume") && val) + uopt->volume = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "partition") && val) + uopt->partition = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "fileset") && val) + uopt->fileset = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "rootdir") && val) + uopt->rootdir = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "iocharset") && val) + { + uopt->iocharset = val; + while (*val && *val != ',') + val ++; + if (val == uopt->iocharset) + return 0; + *val = 0; + } + else if (val) + { + printk(KERN_ERR "udf: bad mount option \"%s=%s\"\n", + opt, val); + return 0; + } + else + { + printk(KERN_ERR "udf: bad mount option \"%s\"\n", + opt); + return 0; + } + } + return 1; +} + +static int +udf_remount_fs(struct super_block *sb, int *flags, char *options) +{ + struct udf_options uopt; + + uopt.flags = UDF_SB(sb)->s_flags ; + uopt.uid = UDF_SB(sb)->s_uid ; + uopt.gid = UDF_SB(sb)->s_gid ; + uopt.umask = UDF_SB(sb)->s_umask ; + uopt.utf8 = UDF_SB(sb)->s_utf8 ; + + if ( !udf_parse_options(options, &uopt) ) + return -EINVAL; + + UDF_SB(sb)->s_flags = uopt.flags; + UDF_SB(sb)->s_uid = uopt.uid; + UDF_SB(sb)->s_gid = uopt.gid; + UDF_SB(sb)->s_umask = uopt.umask; + UDF_SB(sb)->s_utf8 = uopt.utf8; + + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (*flags & MS_RDONLY) + udf_close_lvid(sb); + else + udf_open_lvid(sb); + + return 0; +} + +/* + * udf_set_blocksize + * + * PURPOSE + * Set the block size to be used in all transfers. + * + * DESCRIPTION + * To allow room for a DMA transfer, it is best to guess big when unsure. + * This routine picks 2048 bytes as the blocksize when guessing. This + * should be adequate until devices with larger block sizes become common. + * + * Note that the Linux kernel can currently only deal with blocksizes of + * 512, 1024, 2048, 4096, and 8192 bytes. + * + * PRE-CONDITIONS + * sb Pointer to _locked_ superblock. + * + * POST-CONDITIONS + * sb->s_blocksize Blocksize. + * sb->s_blocksize_bits log2 of blocksize. + * <return> 0 Blocksize is valid. + * <return> 1 Blocksize is invalid. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static int +udf_set_blocksize(struct super_block *sb, int bsize) +{ + /* Use specified block size if specified */ + sb->s_blocksize = get_hardblocksize(sb->s_dev); + sb->s_blocksize = sb->s_blocksize ? sb->s_blocksize : 2048; + if (bsize > sb->s_blocksize) + sb->s_blocksize = bsize; + + /* Block size must be an even multiple of 512 */ + switch (sb->s_blocksize) { + case 512: sb->s_blocksize_bits = 9; break; + case 1024: sb->s_blocksize_bits = 10; break; + case 2048: sb->s_blocksize_bits = 11; break; + case 4096: sb->s_blocksize_bits = 12; break; + case 8192: sb->s_blocksize_bits = 13; break; + default: + { + udf_debug("Bad block size (%ld)\n", sb->s_blocksize); + printk(KERN_ERR "udf: bad block size (%ld)\n", sb->s_blocksize); + return 0; + } + } + + /* Set the block size */ + set_blocksize(sb->s_dev, sb->s_blocksize); + return sb->s_blocksize; +} + +static int +udf_vrs(struct super_block *sb, int silent) +{ + struct VolStructDesc *vsd = NULL; + int sector = 32768; + struct buffer_head *bh = NULL; + int iso9660=0; + int nsr02=0; + int nsr03=0; + + /* Block size must be a multiple of 512 */ + if (sb->s_blocksize & 511) + return sector; + + sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); + + udf_debug("Starting at sector %u (%ld byte sectors)\n", + (sector >> sb->s_blocksize_bits), sb->s_blocksize); + /* Process the sequence (if applicable) */ + for (;!nsr02 && !nsr03; sector += 2048) + { + /* Read a block */ + bh = udf_tread(sb, sector >> sb->s_blocksize_bits, 2048); + if (!bh) + break; + + /* Look for ISO descriptors */ + vsd = (struct VolStructDesc *)(bh->b_data + + (sector & (sb->s_blocksize - 1))); + + if (vsd->stdIdent[0] == 0) + { + udf_release_data(bh); + break; + } + else if (!strncmp(vsd->stdIdent, STD_ID_CD001, STD_ID_LEN)) + { + iso9660 = sector; + switch (vsd->structType) + { + case 0: + udf_debug("ISO9660 Boot Record found\n"); + break; + case 1: + udf_debug("ISO9660 Primary Volume Descriptor found\n"); + break; + case 2: + udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); + break; + case 3: + udf_debug("ISO9660 Volume Partition Descriptor found\n"); + break; + case 255: + udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); + break; + default: + udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); + break; + } + } + else if (!strncmp(vsd->stdIdent, STD_ID_BEA01, STD_ID_LEN)) + { + } + else if (!strncmp(vsd->stdIdent, STD_ID_TEA01, STD_ID_LEN)) + { + udf_release_data(bh); + break; + } + else if (!strncmp(vsd->stdIdent, STD_ID_NSR02, STD_ID_LEN)) + { + nsr02 = sector; + } + else if (!strncmp(vsd->stdIdent, STD_ID_NSR03, STD_ID_LEN)) + { + nsr03 = sector; + } + udf_release_data(bh); + } + + if (nsr03) + return nsr03; + else if (nsr02) + return nsr02; + else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768) + return -1; + else + return 0; +} + +/* + * udf_find_anchor + * + * PURPOSE + * Find an anchor volume descriptor. + * + * PRE-CONDITIONS + * sb Pointer to _locked_ superblock. + * lastblock Last block on media. + * + * POST-CONDITIONS + * <return> 1 if not found, 0 if ok + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static int +udf_find_anchor(struct super_block *sb, int useranchor, int lastblock) +{ + int varlastblock = udf_variable_to_fixed(lastblock); + int last[] = { lastblock, lastblock - 2, + lastblock - 150, lastblock - 152, + varlastblock, varlastblock - 2, + varlastblock - 150, varlastblock - 152 }; + struct buffer_head *bh = NULL; + Uint16 ident; + Uint32 location; + int i; + + UDF_SB_ANCHOR(sb)[0] = 0; + UDF_SB_ANCHOR(sb)[1] = 0; + UDF_SB_ANCHOR(sb)[2] = 0; + UDF_SB_ANCHOR(sb)[3] = 256 + UDF_SB_SESSION(sb); + + lastblock = 0; + + /* Search for an anchor volume descriptor pointer */ + + /* according to spec, anchor is in either: + * block 256 + * lastblock-256 + * lastblock + * however, if the disc isn't closed, it could be 512 */ + + for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++) + { + if (!(bh = bread(sb->s_dev, last[i], sb->s_blocksize))) + { + ident = location = 0; + } + else + { + ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); + location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); + udf_release_data(bh); + } + + if (ident == TID_ANCHOR_VOL_DESC_PTR) + { + if (location == last[i] - UDF_SB_SESSION(sb)) + { + lastblock = UDF_SB_ANCHOR(sb)[0] = last[i]; + UDF_SB_ANCHOR(sb)[1] = last[i] - 256; + } + else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) + { + UDF_SB(sb)->s_flags |= UDF_FLAG_VARCONV; + lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]); + UDF_SB_ANCHOR(sb)[1] = lastblock - 256; + } + else + udf_debug("Anchor found at block %d, location mismatch %d.\n", + last[i], location); + } + else if (ident == TID_FILE_ENTRY || ident == TID_EXTENDED_FILE_ENTRY) + { + lastblock = last[i]; + UDF_SB_ANCHOR(sb)[2] = 512 + UDF_SB_SESSION(sb); + } + else + { + if (!(bh = bread(sb->s_dev, last[i] - 256, sb->s_blocksize))) + { + ident = location = 0; + } + else + { + ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); + location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); + udf_release_data(bh); + } + + if (ident == TID_ANCHOR_VOL_DESC_PTR && + location == last[i] - 256 - UDF_SB_SESSION(sb)) + { + lastblock = last[i]; + UDF_SB_ANCHOR(sb)[1] = last[i] - 256; + } + else + { + if (!(bh = bread(sb->s_dev, last[i] - 312 - UDF_SB_SESSION(sb), + sb->s_blocksize))) + { + ident = location = 0; + } + else + { + ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); + location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); + udf_release_data(bh); + } + + if (ident == TID_ANCHOR_VOL_DESC_PTR && + location == udf_variable_to_fixed(last[i]) - 256) + { + UDF_SB(sb)->s_flags |= UDF_FLAG_VARCONV; + lastblock = udf_variable_to_fixed(last[i]); + UDF_SB_ANCHOR(sb)[1] = lastblock - 256; + } + } + } + } + + if (!lastblock) + { + /* We havn't found the lastblock. check 312 */ + if ((bh = bread(sb->s_dev, 312 + UDF_SB_SESSION(sb), sb->s_blocksize))) + { + ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); + location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); + udf_release_data(bh); + + if (ident == TID_ANCHOR_VOL_DESC_PTR && location == 256) + UDF_SB(sb)->s_flags |= UDF_FLAG_VARCONV; + } + } + + for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++) + { + if (UDF_SB_ANCHOR(sb)[i]) + { + if (!(bh = udf_read_tagged(sb, + UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) + { + UDF_SB_ANCHOR(sb)[i] = 0; + } + else + { + udf_release_data(bh); + if ((ident != TID_ANCHOR_VOL_DESC_PTR) && (i || + (ident != TID_FILE_ENTRY && ident != TID_EXTENDED_FILE_ENTRY))) + { + UDF_SB_ANCHOR(sb)[i] = 0; + } + } + } + else if (useranchor != 0xFFFFFFFF) + { + UDF_SB_ANCHOR(sb)[i] = useranchor; + useranchor = 0xFFFFFFFF; + i --; + } + } + + return lastblock; +} + +static int +udf_find_fileset(struct super_block *sb, lb_addr *fileset, lb_addr *root) +{ + struct buffer_head *bh = NULL; + long lastblock; + Uint16 ident; + + if (fileset->logicalBlockNum != 0xFFFFFFFF || + fileset->partitionReferenceNum != 0xFFFF) + { + bh = udf_read_ptagged(sb, *fileset, 0, &ident); + + if (!bh) + return 1; + else if (ident != TID_FILE_SET_DESC) + { + udf_release_data(bh); + return 1; + } + + } + + if (!bh) /* Search backwards through the partitions */ + { + lb_addr newfileset; + + return 1; + + for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1; + (newfileset.partitionReferenceNum != 0xFFFF && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); + newfileset.partitionReferenceNum--) + { + lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum); + newfileset.logicalBlockNum = 0; + + do + { + bh = udf_read_ptagged(sb, newfileset, 0, &ident); + if (!bh) + { + newfileset.logicalBlockNum ++; + continue; + } + + switch (ident) + { + case TID_SPACE_BITMAP_DESC: + { + struct SpaceBitmapDesc *sp; + sp = (struct SpaceBitmapDesc *)bh->b_data; + newfileset.logicalBlockNum += 1 + + ((le32_to_cpu(sp->numOfBytes) + sizeof(struct SpaceBitmapDesc) - 1) + >> sb->s_blocksize_bits); + udf_release_data(bh); + break; + } + case TID_FILE_SET_DESC: + { + *fileset = newfileset; + break; + } + default: + { + newfileset.logicalBlockNum ++; + udf_release_data(bh); + bh = NULL; + break; + } + } + } + while (newfileset.logicalBlockNum < lastblock && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); + } + } + + if ((fileset->logicalBlockNum != 0xFFFFFFFF || + fileset->partitionReferenceNum != 0xFFFF) && bh) + { + udf_debug("Fileset at block=%d, partition=%d\n", + fileset->logicalBlockNum, fileset->partitionReferenceNum); + + UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; + udf_load_fileset(sb, bh, root); + udf_release_data(bh); + return 0; + } + return 1; +} + +static void +udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) +{ + struct PrimaryVolDesc *pvoldesc; + time_t recording; + struct ustr instr; + struct ustr outstr; + + pvoldesc = (struct PrimaryVolDesc *)bh->b_data; + + if ( udf_stamp_to_time(&recording, lets_to_cpu(pvoldesc->recordingDateAndTime)) ) + { + timestamp ts; + ts = lets_to_cpu(pvoldesc->recordingDateAndTime); + udf_debug("recording time %ld, %u/%u/%u %u:%u (%x)\n", + recording, ts.year, ts.month, ts.day, ts.hour, ts.minute, + ts.typeAndTimezone); + UDF_SB_RECORDTIME(sb) = recording; + } + + if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) + { + if (!udf_CS0toUTF8(&outstr, &instr)) + { + udf_debug("volIdent[] = '%s'\n", outstr.u_name); + strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len); + } + } + + if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) ) + { + if (!udf_CS0toUTF8(&outstr, &instr)) + udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); + } +} + +static void +udf_load_fileset(struct super_block *sb, struct buffer_head *bh, lb_addr *root) +{ + struct FileSetDesc *fset; + + fset = (struct FileSetDesc *)bh->b_data; + + *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); + + UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum); + + udf_debug("Rootdir at block=%d, partition=%d\n", + root->logicalBlockNum, root->partitionReferenceNum); +} + +static void +udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) +{ + struct PartitionDesc *p; + int i; + + p=(struct PartitionDesc *)bh->b_data; + + for (i=0; i<UDF_SB_NUMPARTS(sb); i++) + { + udf_debug("Searching map: (%d == %d)\n", + UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber)); + if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) + { + UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ + UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation) + UDF_SB_SESSION(sb); + UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF; + + if (!strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR02) || + !strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR03)) + { + struct PartitionHeaderDesc *phd; + + phd = (struct PartitionHeaderDesc *)(p->partitionContentsUse); + if (phd->unallocatedSpaceTable.extLength) + udf_debug("unallocatedSpaceTable (part %d)\n", i); + if (phd->unallocatedSpaceBitmap.extLength) + { + UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = + le32_to_cpu(phd->unallocatedSpaceBitmap.extPosition); + udf_debug("unallocatedSpaceBitmap (part %d) @ %d\n", + i, UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap); + } + if (phd->partitionIntegrityTable.extLength) + udf_debug("partitionIntegrityTable (part %d)\n", i); + if (phd->freedSpaceTable.extLength) + udf_debug("freedSpaceTable (part %d)\n", i); + if (phd->freedSpaceBitmap.extLength) + udf_debug("freedSpaceBitmap (part %d\n", i); + } + break; + } + } + if (i == UDF_SB_NUMPARTS(sb)) + { + udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber)); + } + else + { + udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n", + le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i), + UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i)); + } +} + +static int +udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, lb_addr *fileset) +{ + struct LogicalVolDesc *lvd; + int i, offset; + Uint8 type; + + lvd = (struct LogicalVolDesc *)bh->b_data; + + UDF_SB_NUMPARTS(sb) = le32_to_cpu(lvd->numPartitionMaps); + UDF_SB_ALLOC_PARTMAPS(sb, UDF_SB_NUMPARTS(sb)); + + for (i=0,offset=0; + i<UDF_SB_NUMPARTS(sb) && offset<le32_to_cpu(lvd->mapTableLength); + i++,offset+=((struct GenericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) + { + type = ((struct GenericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType; + udf_debug("Partition (%d) type %d\n", i, type); + if (type == 1) + { + struct GenericPartitionMap1 *gpm1 = (struct GenericPartitionMap1 *)&(lvd->partitionMaps[offset]); + UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; + UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); + UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); + } + else if (type == 2) + { + struct UdfPartitionMap2 *upm2 = (struct UdfPartitionMap2 *)&(lvd->partitionMaps[offset]); + if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) + { + if (le16_to_cpu(((Uint16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) + UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; + else if (le16_to_cpu(((Uint16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) + UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; + } + else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) + { + struct SparablePartitionMap *spm = (struct SparablePartitionMap *)&(lvd->partitionMaps[offset]); + UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; + UDF_SB_TYPESPAR(sb,i).s_spar_plen = le16_to_cpu(spm->packetLength); + UDF_SB_TYPESPAR(sb,i).s_spar_loc = le32_to_cpu(spm->locSparingTable[0]); + } + else + { + udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); + continue; + } + UDF_SB_PARTVSN(sb,i) = le16_to_cpu(upm2->volSeqNum); + UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum); + } + } + + if (fileset) + { + long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]); + + *fileset = lelb_to_cpu(la->extLocation); + udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n", + fileset->logicalBlockNum, + fileset->partitionReferenceNum); + } + if (lvd->integritySeqExt.extLength) + udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); + return 0; +} + +/* + * udf_load_logicalvolint + * + */ +static void +udf_load_logicalvolint(struct super_block *sb, extent_ad loc) +{ + struct buffer_head *bh = NULL; + Uint16 ident; + + while ((bh = udf_read_tagged(sb, loc.extLocation, loc.extLocation, &ident)) && + ident == TID_LOGICAL_VOL_INTEGRITY_DESC && loc.extLength > 0) + { + UDF_SB_LVIDBH(sb) = bh; + + if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) + udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); + + if (UDF_SB_LVIDBH(sb) != bh) + udf_release_data(bh); + loc.extLength -= sb->s_blocksize; + loc.extLocation ++; + } + if (UDF_SB_LVIDBH(sb) != bh) + udf_release_data(bh); +} + +/* + * udf_process_sequence + * + * PURPOSE + * Process a main/reserve volume descriptor sequence. + * + * PRE-CONDITIONS + * sb Pointer to _locked_ superblock. + * block First block of first extent of the sequence. + * lastblock Lastblock of first extent of the sequence. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static int +udf_process_sequence(struct super_block *sb, long block, long lastblock, lb_addr *fileset) +{ + struct buffer_head *bh = NULL; + struct udf_vds_record vds[VDS_POS_LENGTH]; + struct GenericDesc *gd; + int done=0; + int i,j; + Uint32 vdsn; + Uint16 ident; + + memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); + + /* Read the main descriptor sequence */ + for (;(!done && block <= lastblock); block++) + { + + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + break; + + /* Process each descriptor (ISO 13346 3/8.3-8.4) */ + gd = (struct GenericDesc *)bh->b_data; + vdsn = le32_to_cpu(gd->volDescSeqNum); + switch (ident) + { + case TID_PRIMARY_VOL_DESC: /* ISO 13346 3/10.1 */ + if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) + { + vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_PRIMARY_VOL_DESC].block = block; + } + break; + case TID_VOL_DESC_PTR: /* ISO 13346 3/10.3 */ + if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) + { + vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; + vds[VDS_POS_VOL_DESC_PTR].block = block; + } + break; + case TID_IMP_USE_VOL_DESC: /* ISO 13346 3/10.4 */ + if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) + { + vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_IMP_USE_VOL_DESC].block = block; + } + break; + case TID_PARTITION_DESC: /* ISO 13346 3/10.5 */ + if (!vds[VDS_POS_PARTITION_DESC].block) + vds[VDS_POS_PARTITION_DESC].block = block; + break; + case TID_LOGICAL_VOL_DESC: /* ISO 13346 3/10.6 */ + if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) + { + vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_LOGICAL_VOL_DESC].block = block; + } + break; + case TID_UNALLOC_SPACE_DESC: /* ISO 13346 3/10.8 */ + if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) + { + vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; + } + break; + case TID_TERMINATING_DESC: /* ISO 13346 3/10.9 */ + vds[VDS_POS_TERMINATING_DESC].block = block; + done = 1; + break; + } + udf_release_data(bh); + } + for (i=0; i<VDS_POS_LENGTH; i++) + { + if (vds[i].block) + { + bh = udf_read_tagged(sb, vds[i].block, vds[i].block, &ident); + + if (i == VDS_POS_PRIMARY_VOL_DESC) + udf_load_pvoldesc(sb, bh); + else if (i == VDS_POS_LOGICAL_VOL_DESC) + udf_load_logicalvol(sb, bh, fileset); + else if (i == VDS_POS_PARTITION_DESC) + { + struct buffer_head *bh2 = NULL; + udf_load_partdesc(sb, bh); + for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++) + { + bh2 = udf_read_tagged(sb, j, j, &ident); + gd = (struct GenericDesc *)bh2->b_data; + if (ident == TID_PARTITION_DESC) + udf_load_partdesc(sb, bh2); + udf_release_data(bh2); + } + } + udf_release_data(bh); + } + } + + return 0; +} + +/* + * udf_check_valid() + */ +static int +udf_check_valid(struct super_block *sb, int novrs, int silent) +{ + long block; + + if (novrs) + { + udf_debug("Validity check skipped because of novrs option\n"); + return 0; + } + /* Check that it is NSR02 compliant */ + /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ + else if ((block = udf_vrs(sb, silent)) == -1) + { + udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); + return 0; + } + else + return !block; +} + +static int +udf_load_partition(struct super_block *sb, lb_addr *fileset) +{ + struct AnchorVolDescPtr *anchor; + Uint16 ident; + struct buffer_head *bh; + long main_s, main_e, reserve_s, reserve_e; + int i; + + if (!sb) + return 1; + + for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++) + { + if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb, + UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i] - UDF_SB_SESSION(sb), &ident))) + { + anchor = (struct AnchorVolDescPtr *)bh->b_data; + + /* Locate the main sequence */ + main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation ); + main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength ); + main_e = main_e >> sb->s_blocksize_bits; + main_e += main_s; + + /* Locate the reserve sequence */ + reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation); + reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength); + reserve_e = reserve_e >> sb->s_blocksize_bits; + reserve_e += reserve_s; + + udf_release_data(bh); + + /* Process the main & reserve sequences */ + /* responsible for finding the PartitionDesc(s) */ + if (!(udf_process_sequence(sb, main_s, main_e, fileset) && + udf_process_sequence(sb, reserve_s, reserve_e, fileset))) + { + break; + } + } + } + + if (i == sizeof(UDF_SB_ANCHOR(sb))/sizeof(int)) + { + udf_debug("No Anchor block found\n"); + return 1; + } + else + udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); + + for (i=0; i<UDF_SB_NUMPARTS(sb); i++) + { + switch UDF_SB_PARTTYPE(sb, i) + { + case UDF_VIRTUAL_MAP15: + case UDF_VIRTUAL_MAP20: + { + lb_addr ino; + + if (!UDF_SB_LASTBLOCK(sb)) + { + udf_debug("Unable to determine Lastblock (For Virtual Partition)\n"); + return 1; + } + + if (i == 0) + ino.partitionReferenceNum = i+1; + else + ino.partitionReferenceNum = i-1; + + ino.logicalBlockNum = UDF_SB_LASTBLOCK(sb) - UDF_SB_PARTROOT(sb,ino.partitionReferenceNum); + + if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino))) + return 1; + + if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15) + { + UDF_SB_TYPEVIRT(sb,i).s_start_offset = UDF_I_EXT0OFFS(UDF_SB_VAT(sb)); + UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) / sizeof(Uint32); + } + else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) + { + struct buffer_head *bh = NULL; + Uint32 pos; + + pos = udf_block_map(UDF_SB_VAT(sb), 0); + bh = bread(sb->s_dev, pos, sb->s_blocksize); + UDF_SB_TYPEVIRT(sb,i).s_start_offset = + le16_to_cpu(((struct VirtualAllocationTable20 *)bh->b_data + UDF_I_EXT0OFFS(UDF_SB_VAT(sb)))->lengthHeader) + + UDF_I_EXT0OFFS(UDF_SB_VAT(sb)); + UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - + UDF_SB_TYPEVIRT(sb,i).s_start_offset) / sizeof(Uint32); + udf_release_data(bh); + } + UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); + UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); + UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF; + } + } + } + return 0; +} + +static void udf_open_lvid(struct super_block *sb) +{ +#ifdef CONFIG_UDF_RW + if (UDF_SB_LVIDBH(sb)) + { + int i; + timestamp cpu_time; + + UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + if (udf_time_to_stamp(&cpu_time, CURRENT_TIME, CURRENT_UTIME)) + UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); + UDF_SB_LVID(sb)->integrityType = INTEGRITY_TYPE_OPEN; + + UDF_SB_LVID(sb)->descTag.descCRC = + cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), + le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); + + UDF_SB_LVID(sb)->descTag.tagChecksum = 0; + for (i=0; i<16; i++) + if (i != 4) + UDF_SB_LVID(sb)->descTag.tagChecksum += + ((Uint8 *)&(UDF_SB_LVID(sb)->descTag))[i]; + + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } +#endif +} + +static void udf_close_lvid(struct super_block *sb) +{ +#ifdef CONFIG_UDF_RW + if (UDF_SB_LVIDBH(sb) && + UDF_SB_LVID(sb)->integrityType == INTEGRITY_TYPE_OPEN) + { + int i; + timestamp cpu_time; + + UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + if (udf_time_to_stamp(&cpu_time, CURRENT_TIME, CURRENT_UTIME)) + UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); + + UDF_SB_LVID(sb)->integrityType = INTEGRITY_TYPE_CLOSE; + + UDF_SB_LVID(sb)->descTag.descCRC = + cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), + le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); + + UDF_SB_LVID(sb)->descTag.tagChecksum = 0; + for (i=0; i<16; i++) + if (i != 4) + UDF_SB_LVID(sb)->descTag.tagChecksum += + ((Uint8 *)&(UDF_SB_LVID(sb)->descTag))[i]; + + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } +#endif +} + +/* + * udf_read_super + * + * PURPOSE + * Complete the specified super block. + * + * PRE-CONDITIONS + * sb Pointer to superblock to complete - never NULL. + * sb->s_dev Device to read suberblock from. + * options Pointer to mount options. + * silent Silent flag. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static struct super_block * +udf_read_super(struct super_block *sb, void *options, int silent) +{ + struct inode *inode=NULL; + struct udf_options uopt; + lb_addr rootdir, fileset; + int i; + + uopt.flags = 0; + uopt.uid = 0; + uopt.gid = 0; + uopt.umask = 0; + uopt.utf8 = 0; + + /* Lock the module in memory (if applicable) */ + MOD_INC_USE_COUNT; + + lock_super(sb); + + UDF_SB_PARTMAPS(sb) = NULL; + UDF_SB_LVIDBH(sb) = NULL; + UDF_SB_VAT(sb) = NULL; + + if (!udf_parse_options((char *)options, &uopt)) + goto error_out; + + memset(UDF_SB_ANCHOR(sb), 0x00, sizeof(UDF_SB_ANCHOR(sb))); + fileset.logicalBlockNum = 0xFFFFFFFF; + fileset.partitionReferenceNum = 0xFFFF; + UDF_SB_RECORDTIME(sb)=0; + UDF_SB_VOLIDENT(sb)[0]=0; + + UDF_SB(sb)->s_flags = uopt.flags; + UDF_SB(sb)->s_uid = uopt.uid; + UDF_SB(sb)->s_gid = uopt.gid; + UDF_SB(sb)->s_umask = uopt.umask; + UDF_SB(sb)->s_utf8 = uopt.utf8; + + /* Set the block size for all transfers */ + if (!udf_set_blocksize(sb, uopt.blocksize)) + goto error_out; + + if ( uopt.session == 0xFFFFFFFF ) + UDF_SB_SESSION(sb) = udf_get_last_session(sb->s_dev); + else + UDF_SB_SESSION(sb) = uopt.session; + + udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb)); + + if ( uopt.lastblock == 0xFFFFFFFF ) + UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb->s_dev, &(UDF_SB(sb)->s_flags)); + else + UDF_SB_LASTBLOCK(sb) = uopt.lastblock; + + UDF_SB_LASTBLOCK(sb) = udf_find_anchor(sb, uopt.anchor, UDF_SB_LASTBLOCK(sb)); + + udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); + + if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ + { + udf_debug("No VRS found\n"); + goto error_out; + } + + UDF_SB_CHARSET(sb) = NULL; + +#ifdef CONFIG_NLS + if (uopt.utf8 == 0) + { + char *p = uopt.iocharset ? uopt.iocharset : "iso8859-1"; + UDF_SB_CHARSET(sb) = load_nls(p); + if (!UDF_SB_CHARSET(sb)) + if (uopt.iocharset) + goto error_out; + UDF_SB_CHARSET(sb) = load_nls_default(); + } +#endif + + /* Fill in the rest of the superblock */ + sb->s_op = &udf_sb_ops; + sb->s_time = 0; + sb->dq_op = NULL; + sb->s_dirt = 0; + sb->s_magic = UDF_SUPER_MAGIC; + + for (i=0; i<UDF_MAX_BLOCK_LOADED; i++) + { + UDF_SB_BLOCK_BITMAP_NUMBER(sb,i) = 0; + UDF_SB_BLOCK_BITMAP(sb,i) = NULL; + } + UDF_SB_LOADED_BLOCK_BITMAPS(sb) = 0; + + if (udf_load_partition(sb, &fileset)) + { + udf_debug("No partition found (1)\n"); + goto error_out; + } + + if ( !UDF_SB_NUMPARTS(sb) ) + { + udf_debug("No partition found (2)\n"); + goto error_out; + } + + if ( udf_find_fileset(sb, &fileset, &rootdir) ) + { + udf_debug("No fileset found\n"); + goto error_out; + } + + if (!silent) + { + timestamp ts; + udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb), 0); + udf_info("Mounting volume '%s', timestamp %u/%02u/%u %02u:%02u\n", + UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute); + } + if (!(sb->s_flags & MS_RDONLY)) + udf_open_lvid(sb); + unlock_super(sb); + + /* Assign the root inode */ + /* assign inodes by physical block number */ + /* perhaps it's not extensible enough, but for now ... */ + inode = udf_iget(sb, rootdir); + if (!inode) + { + udf_debug("Error in udf_iget, block=%d, partition=%d\n", + rootdir.logicalBlockNum, rootdir.partitionReferenceNum); + goto error_out; + } + + /* Allocate a dentry for the root inode */ + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) + { + iput(inode); + udf_debug("Couldn't allocate root dentry\n"); + goto error_out; + } + + return sb; + +error_out: + sb->s_dev = NODEV; + if (UDF_SB_VAT(sb)) + iput(UDF_SB_VAT(sb)); + if (!(sb->s_flags & MS_RDONLY)) + udf_close_lvid(sb); + udf_release_data(UDF_SB_LVIDBH(sb)); + UDF_SB_FREE(sb); + unlock_super(sb); + MOD_DEC_USE_COUNT; + return NULL; +} + +void udf_error(struct super_block *sb, const char *function, + const char *fmt, ...) +{ + va_list args; + + if (!(sb->s_flags & MS_RDONLY)) + { + /* mark sb error */ + sb->s_dirt = 1; + } + va_start(args, fmt); + vsprintf(error_buf, fmt, args); + va_end(args); + printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); +} + +void udf_warning(struct super_block *sb, const char *function, + const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf(error_buf, fmt, args); + va_end(args); + printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); +} + +/* + * udf_put_super + * + * PURPOSE + * Prepare for destruction of the superblock. + * + * DESCRIPTION + * Called before the filesystem is unmounted. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static void +udf_put_super(struct super_block *sb) +{ + int i; + + if (UDF_SB_VAT(sb)) + iput(UDF_SB_VAT(sb)); + if (!(sb->s_flags & MS_RDONLY)) + udf_close_lvid(sb); + udf_release_data(UDF_SB_LVIDBH(sb)); + for (i=0; i<UDF_MAX_BLOCK_LOADED; i++) + udf_release_data(UDF_SB_BLOCK_BITMAP(sb, i)); + UDF_SB_FREE(sb); + + MOD_DEC_USE_COUNT; +} + +/* + * udf_stat_fs + * + * PURPOSE + * Return info about the filesystem. + * + * DESCRIPTION + * Called by sys_statfs() + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static int +udf_statfs(struct super_block *sb, struct statfs *buf, int bufsize) +{ + int size; + struct statfs tmp; + int rc; + + size = (bufsize < sizeof(tmp)) ? bufsize: sizeof(tmp); + + memset(&tmp, 0, sizeof(tmp)); + tmp.f_type = UDF_SUPER_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); + tmp.f_bfree = udf_count_free(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = (UDF_SB_LVIDBH(sb) ? + (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + + le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + tmp.f_bfree; + tmp.f_ffree = tmp.f_bfree; + /* __kernel_fsid_t f_fsid */ + tmp.f_namelen = UDF_NAME_LEN; + + rc= copy_to_user(buf, &tmp, size) ? -EFAULT: 0; + return rc; +} + +static unsigned char udf_bitmap_lookup[16] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 +}; + +static unsigned int +udf_count_free(struct super_block *sb) +{ + struct buffer_head *bh = NULL; + unsigned int accum=0; + int index; + int block=0, newblock; + lb_addr loc; + Uint32 bytes; + Uint8 value; + Uint8 * ptr; + Uint16 ident; + + if (UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap == 0xFFFFFFFF) + { + if (UDF_SB_LVIDBH(sb)) + { + if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) + accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); + + if (accum == 0xFFFFFFFF) + accum = 0; + + return accum; + } + else + return 0; + } + else + { + struct SpaceBitmapDesc *bm; + loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap; + loc.partitionReferenceNum = UDF_SB_PARTITION(sb); + bh = udf_read_ptagged(sb, loc, 0, &ident); + + if (!bh) + { + printk(KERN_ERR "udf: udf_count_free failed\n"); + return 0; + } + else if (ident != TID_SPACE_BITMAP_DESC) + { + udf_release_data(bh); + printk(KERN_ERR "udf: udf_count_free failed\n"); + return 0; + } + + bm = (struct SpaceBitmapDesc *)bh->b_data; + bytes = bm->numOfBytes; + index = sizeof(struct SpaceBitmapDesc); /* offset in first block only */ + ptr = (Uint8 *)bh->b_data; + + while ( bytes > 0 ) + { + while ((bytes > 0) && (index < sb->s_blocksize)) + { + value = ptr[index]; + accum += udf_bitmap_lookup[ value & 0x0f ]; + accum += udf_bitmap_lookup[ value >> 4 ]; + index++; + bytes--; + } + if ( bytes ) + { + udf_release_data(bh); + newblock = udf_get_lb_pblock(sb, loc, ++block); + bh = udf_tread(sb, newblock, sb->s_blocksize); + if (!bh) + { + udf_debug("read failed\n"); + return accum; + } + index = 0; + ptr = (Uint8 *)bh->b_data; + } + } + udf_release_data(bh); + return accum; + } +} diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c new file mode 100644 index 000000000..0e52198f5 --- /dev/null +++ b/fs/udf/symlink.c @@ -0,0 +1,189 @@ +/* + * symlink.c + * + * PURPOSE + * Symlink handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1998-1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 04/16/99 blf Created. + * + */ + +#include "udfdecl.h" +#include <asm/uaccess.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/udf_fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/stat.h> +#include <linux/malloc.h> +#include "udf_i.h" + +static int udf_readlink(struct dentry *, char *, int); +static struct dentry * udf_follow_link(struct dentry * dentry, + struct dentry * base, unsigned int follow); + +/* + * symlinks can't do much... + */ +struct inode_operations udf_symlink_inode_operations = { + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + udf_readlink, /* readlink */ + udf_follow_link,/* follow_link */ + NULL, /* get_block */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + +int udf_pc_to_char(char *from, int fromlen, char **to) +{ + struct PathComponent *pc; + int elen = 0, len = 0; + + *to = (char *)kmalloc(fromlen, GFP_KERNEL); + + if (!(*to)) + return -1; + + while (elen < fromlen) + { + pc = (struct PathComponent *)(from + elen); + if (pc->componentType == 1 && pc->lengthComponentIdent == 0) + { + (*to)[0] = '/'; + len = 1; + } + else if (pc->componentType == 3) + { + memcpy(&(*to)[len], "../", 3); + len += 3; + } + else if (pc->componentType == 4) + { + memcpy(&(*to)[len], "./", 2); + len += 2; + } + else if (pc->componentType == 5) + { + memcpy(&(*to)[len], pc->componentIdent, pc->lengthComponentIdent); + len += pc->lengthComponentIdent + 1; + (*to)[len-1] = '/'; + } + elen += sizeof(struct PathComponent) + pc->lengthComponentIdent; + } + + if (len) + { + len --; + (*to)[len] = '\0'; + } + return len; +} + +static struct dentry * udf_follow_link(struct dentry * dentry, + struct dentry * base, unsigned int follow) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh = NULL; + char *symlink, *tmpbuf; + int len; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data + udf_file_entry_alloc_offset(inode); + } + else + { + bh = bread(inode->i_dev, udf_block_map(inode, 0), inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data; + } + + if ((len = udf_pc_to_char(symlink, inode->i_size, &tmpbuf)) >= 0) + { + base = lookup_dentry(tmpbuf, base, follow); + kfree(tmpbuf); + return base; + } + else + return ERR_PTR(-ENOMEM); +} + +static int udf_readlink(struct dentry * dentry, char * buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh = NULL; + char *symlink, *tmpbuf; + int len; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data + udf_file_entry_alloc_offset(inode); + } + else + { + bh = bread(inode->i_dev, udf_block_map(inode, 0), inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data; + } + + if ((len = udf_pc_to_char(symlink, inode->i_size, &tmpbuf)) >= 0) + { + if (copy_to_user(buffer, tmpbuf, len > buflen ? buflen : len)) + len = -EFAULT; + kfree(tmpbuf); + } + else + len = -ENOMEM; + + UPDATE_ATIME(inode); + if (bh) + udf_release_data(bh); + return len; +} diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c new file mode 100644 index 000000000..f11ccf7af --- /dev/null +++ b/fs/udf/truncate.c @@ -0,0 +1,229 @@ +/* + * truncate.c + * + * PURPOSE + * Truncate handling routines for the OSTA-UDF(tm) filesystem. + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + * + * (C) 1999 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/udf_fs.h> + +#include "udf_i.h" +#include "udf_sb.h" + +static void extent_trunc(struct inode * inode, lb_addr bloc, int *extoffset, + lb_addr eloc, Uint32 elen, struct buffer_head **bh, Uint32 offset) +{ + lb_addr neloc = { 0, 0 }; + int nelen = 0; + int blocks = inode->i_sb->s_blocksize / 512; + int last_block = (elen + inode->i_sb->s_blocksize - 1) / inode->i_sb->s_blocksize; + + + if (offset) + { + nelen = ((offset - 1) << inode->i_sb->s_blocksize_bits) + + (inode->i_size & (inode->i_sb->s_blocksize - 1)); + neloc = eloc; + } + + inode->i_blocks -= (blocks * (last_block - offset)); + udf_write_aext(inode, bloc, extoffset, neloc, nelen, bh, 1); + if (!memcmp(&UDF_I_EXT0LOC(inode), &eloc, sizeof(lb_addr))) + { + UDF_I_EXT0LOC(inode) = neloc; + UDF_I_EXT0LEN(inode) = nelen; + } + mark_inode_dirty(inode); + udf_free_blocks(inode, eloc, offset, last_block - offset); +} + +static void trunc(struct inode * inode) +{ + lb_addr bloc, eloc, neloc = { 0, 0 }; + Uint32 extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; + int etype; + int first_block = (inode->i_size + inode->i_sb->s_blocksize - 1) / inode->i_sb->s_blocksize; + struct buffer_head *bh = NULL; + int adsize; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + adsize = 0; + + if ((etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh)) != -1) + { + extoffset -= adsize; + extent_trunc(inode, bloc, &extoffset, eloc, elen, &bh, offset); + + if (offset) + lenalloc = extoffset; + else + lenalloc = extoffset - adsize; + + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + lenalloc -= udf_file_entry_alloc_offset(inode); + else + lenalloc -= sizeof(struct AllocExtDesc); + + while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1) + { + if (etype == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + udf_write_aext(inode, bloc, &extoffset, neloc, nelen, &bh, 0); + extoffset = 0; + if (lelen) + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode)); + else + memset(bh->b_data, 0x00, sizeof(struct AllocExtDesc)); + udf_free_blocks(inode, bloc, 0, lelen); + } + else + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + UDF_I_LENALLOC(inode) = lenalloc; + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(bh->b_data); + aed->lengthAllocDescs = cpu_to_le32(lenalloc); + } + } + + udf_release_data(bh); + bh = NULL; + + bloc = eloc; + if (elen) + lelen = (elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; + else + lelen = 1; + } + else if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) + extent_trunc(inode, bloc, &extoffset, eloc, elen, &bh, 0); + else + udf_write_aext(inode, bloc, &extoffset, neloc, nelen, &bh, 1); + } + + if (lelen) + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode)); + else + memset(bh->b_data, 0x00, sizeof(struct AllocExtDesc)); + udf_free_blocks(inode, bloc, 0, lelen); + } + else + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + UDF_I_LENALLOC(inode) = lenalloc; + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(bh->b_data); + aed->lengthAllocDescs = cpu_to_le32(lenalloc); + } + } + } + else if (inode->i_size) + { + lb_addr e0loc = UDF_I_LOCATION(inode); + Uint32 ext0offset = udf_file_entry_alloc_offset(inode); + char tetype; + + if (offset) + { + extoffset -= adsize; + tetype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1); + if (tetype == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + { + extoffset -= adsize; + elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | + (elen + (offset << inode->i_sb->s_blocksize_bits)); + if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) + UDF_I_EXT0LEN(inode) = elen; + udf_write_aext(inode, bloc, &extoffset, eloc, elen, &bh, 0); + } + else + { + if (elen & (inode->i_sb->s_blocksize - 1)) + { + extoffset -= adsize; + elen = (EXTENT_RECORDED_ALLOCATED << 30) | + ((elen + inode->i_sb->s_blocksize - 1) & + ~(inode->i_sb->s_blocksize - 1)); + if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) + UDF_I_EXT0LEN(inode) = elen; + udf_write_aext(inode, bloc, &extoffset, eloc, elen, &bh, 1); + } + memset(&eloc, 0x00, sizeof(lb_addr)); + elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | + (offset << inode->i_sb->s_blocksize_bits); + if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) + { + UDF_I_EXT0LOC(inode) = eloc; + UDF_I_EXT0LEN(inode) = elen; + } + udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); + } + } + } + + udf_release_data(bh); +} + +void udf_truncate(struct inode * inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + + if (!UDF_I_EXT0OFFS(inode)) + { + udf_discard_prealloc(inode); + + trunc(inode); + } + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} + +void udf_truncate_adinicb(struct inode * inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h new file mode 100644 index 000000000..8b9038296 --- /dev/null +++ b/fs/udf/udf_i.h @@ -0,0 +1,24 @@ +#ifndef __LINUX_UDF_I_H +#define __LINUX_UDF_I_H + +#define UDF_I(X) (&((X)->u.udf_i)) + +#define UDF_I_EXT0LOC(X) ( UDF_I(X)->i_ext0Location ) +#define UDF_I_EXT0LEN(X) ( UDF_I(X)->i_ext0Length ) +#define UDF_I_EXT0OFFS(X) ( UDF_I(X)->i_ext0Offset ) +#define UDF_I_LOCATION(X) ( UDF_I(X)->i_location ) +#define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr ) +#define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc ) +#define UDF_I_UNIQUE(X) ( UDF_I(X)->i_unique ) +#define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) +#define UDF_I_EXTENDED_FE(X)( UDF_I(X)->i_extended_fe ) +#define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat_4096 ) +#define UDF_I_PREALLOC_COUNT(X) ( UDF_I(X)->i_prealloc_count ) +#define UDF_I_PREALLOC_BLOCK(X) ( UDF_I(X)->i_prealloc_block ) +#define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) +#define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) +#define UDF_I_UATIME(X) ( UDF_I(X)->i_uatime ) +#define UDF_I_UMTIME(X) ( UDF_I(X)->i_umtime ) +#define UDF_I_UCTIME(X) ( UDF_I(X)->i_uctime ) + +#endif /* !defined(_LINUX_UDF_I_H) */ diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h new file mode 100644 index 000000000..407d6006d --- /dev/null +++ b/fs/udf/udf_sb.h @@ -0,0 +1,66 @@ +#ifndef __LINUX_UDF_SB_H +#define __LINUX_UDF_SB_H + +#include <linux/udf_167.h> +#include <linux/udf_udf.h> + +/* Since UDF 1.50 is ISO 13346 based... */ +#define UDF_SUPER_MAGIC 0x15013346 + +#define UDF_FLAG_STRICT 0x00000001U +#define UDF_FLAG_UNDELETE 0x00000002U +#define UDF_FLAG_UNHIDE 0x00000004U +#define UDF_FLAG_VARCONV 0x00000008U + +#define UDF_SB_FREE(X)\ +{\ + if (UDF_SB(X))\ + {\ + if (UDF_SB_PARTMAPS(X))\ + kfree(UDF_SB_PARTMAPS(X));\ + UDF_SB_PARTMAPS(X) = NULL;\ + }\ +} +#define UDF_SB(X) (&((X)->u.udf_sb)) + +#define UDF_SB_ALLOC_PARTMAPS(X,Y)\ +{\ + UDF_SB_NUMPARTS(X) = Y;\ + UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ +} + +#define IS_STRICT(X) ( UDF_SB(X)->s_flags & UDF_FLAG_STRICT ) +#define IS_UNDELETE(X) ( UDF_SB(X)->s_flags & UDF_FLAG_UNDELETE ) +#define IS_UNHIDE(X) ( UDF_SB(X)->s_flags & UDF_FLAG_UNHIDE ) + +#define UDF_SB_SESSION(X) ( UDF_SB(X)->s_session ) +#define UDF_SB_ANCHOR(X) ( UDF_SB(X)->s_anchor ) +#define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) +#define UDF_SB_VOLUME(X) ( UDF_SB(X)->s_thisvolume ) +#define UDF_SB_LASTBLOCK(X) ( UDF_SB(X)->s_lastblock ) +#define UDF_SB_VOLDESC(X) ( UDF_SB(X)->s_voldesc ) +#define UDF_SB_LVIDBH(X) ( UDF_SB(X)->s_lvidbh ) +#define UDF_SB_LVID(X) ( (struct LogicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data ) +#define UDF_SB_LVIDIU(X) ( (struct LogicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(sb)->impUse[UDF_SB_LVID(sb)->numOfPartitions * 2 * sizeof(Uint32)/sizeof(Uint8)]) ) +#define UDF_SB_PARTITION(X) ( UDF_SB(X)->s_partition ) +#define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime ) +#define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) +#define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps ) +#define UDF_SB_LOCATION(X) ( UDF_SB(X)->s_location ) +#define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum ) +#define UDF_SB_CHARSET(X) ( UDF_SB(X)->s_nls_iocharset ) +#define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat ) + +#define UDF_SB_BLOCK_BITMAP_NUMBER(X,Y) ( UDF_SB(X)->s_block_bitmap_number[Y] ) +#define UDF_SB_BLOCK_BITMAP(X,Y) ( UDF_SB(X)->s_block_bitmap[Y] ) +#define UDF_SB_LOADED_BLOCK_BITMAPS(X) ( UDF_SB(X)->s_loaded_block_bitmaps ) + +#define UDF_SB_PARTTYPE(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_type ) +#define UDF_SB_PARTROOT(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_root ) +#define UDF_SB_PARTLEN(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_len ) +#define UDF_SB_PARTVSN(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_volumeseqnum ) +#define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_num ) +#define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_type_specific.s_sparing ) +#define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_type_specific.s_virtual ) + +#endif /* __LINUX_UDF_SB_H */ diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h new file mode 100644 index 000000000..9ac5999b9 --- /dev/null +++ b/fs/udf/udfdecl.h @@ -0,0 +1,278 @@ +#ifndef __UDF_DECL_H +#define __UDF_DECL_H + +#define UDF_VERSION_NOTICE "v0.8.9" + +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <linux/udf_udf.h> +#include <linux/udf_fs.h> +#include <linux/config.h> + +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,7) +#error "The UDF Module Current Requires Kernel Version 2.3.7 or greater" +#endif + +/* if we're not defined, we must be compiling outside of the kernel tree */ +#if !defined(CONFIG_UDF_FS) && !defined(CONFIG_UDF_FS_MODULE) +/* ... so override config */ +#define CONFIG_UDF_FS_MODULE +#include <linux/fs.h> +/* explicitly include udf_fs_sb.h and udf_fs_i.h */ +#include <linux/udf_fs_sb.h> +#include <linux/udf_fs_i.h> +#else +#include <linux/fs.h> /* also gets udf_fs_i.h and udf_fs_sb.h */ +#endif + +struct dentry; +struct inode; +struct task_struct; +struct buffer_head; +struct super_block; + +extern struct inode_operations udf_dir_inode_operations; +extern struct inode_operations udf_file_inode_operations; +extern struct inode_operations udf_file_inode_operations_adinicb; +extern struct inode_operations udf_symlink_inode_operations; + +struct udf_fileident_bh +{ + struct buffer_head *sbh; + struct buffer_head *ebh; + int soffset; + int eoffset; +}; + +extern void udf_error(struct super_block *, const char *, const char *, ...); +extern void udf_warning(struct super_block *, const char *, const char *, ...); +extern int udf_write_fi(struct FileIdentDesc *, struct FileIdentDesc *, struct udf_fileident_bh *, Uint8 *, Uint8 *); +extern struct dentry * udf_lookup(struct inode *, struct dentry *); +extern int udf_create(struct inode *, struct dentry *, int); +extern int udf_mknod(struct inode *, struct dentry *, int, int); +extern int udf_mkdir(struct inode *, struct dentry *, int); +extern int udf_rmdir(struct inode *, struct dentry *); +extern int udf_unlink(struct inode *, struct dentry *); +extern int udf_symlink(struct inode *, struct dentry *, const char *); +extern int udf_link(struct dentry *, struct inode *, struct dentry *); +extern int udf_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +extern struct inode *udf_iget(struct super_block *, lb_addr); +extern int udf_sync_inode(struct inode *); +extern struct buffer_head * udf_expand_adinicb(struct inode *, int *, int, int *); +extern struct buffer_head * udf_getblk(struct inode *, long, int, int *); +extern int udf_get_block(struct inode *, long, struct buffer_head *, int); +extern struct buffer_head * udf_bread(struct inode *, int, int, int *); +extern void udf_read_inode(struct inode *); +extern void udf_put_inode(struct inode *); +extern void udf_delete_inode(struct inode *); +extern void udf_write_inode(struct inode *); +extern long udf_locked_block_map(struct inode *, long); +extern long udf_block_map(struct inode *, long); +extern int inode_bmap(struct inode *, int, lb_addr *, Uint32 *, lb_addr *, Uint32 *, Uint32 *, struct buffer_head **); +extern int udf_add_aext(struct inode *, lb_addr *, int *, lb_addr, Uint32, struct buffer_head **, int); +extern int udf_write_aext(struct inode *, lb_addr, int *, lb_addr, Uint32, struct buffer_head **, int); +extern int udf_insert_aext(struct inode *, lb_addr, int, lb_addr, Uint32, struct buffer_head *); +extern int udf_delete_aext(struct inode *, lb_addr, int, lb_addr, Uint32, struct buffer_head *); +extern int udf_next_aext(struct inode *, lb_addr *, int *, lb_addr *, Uint32 *, struct buffer_head **, int); +extern int udf_current_aext(struct inode *, lb_addr *, int *, lb_addr *, Uint32 *, struct buffer_head **, int); + +extern int udf_read_tagged_data(char *, int size, int fd, int block, int partref); + +extern struct buffer_head *udf_tread(struct super_block *, int, int); +extern struct GenericAttrFormat *udf_add_extendedattr(struct inode *, Uint32, Uint32, Uint8, struct buffer_head **); +extern struct GenericAttrFormat *udf_get_extendedattr(struct inode *, Uint32, Uint8, struct buffer_head **); +extern struct buffer_head *udf_read_tagged(struct super_block *, Uint32, Uint32, Uint16 *); +extern struct buffer_head *udf_read_ptagged(struct super_block *, lb_addr, Uint32, Uint16 *); +extern struct buffer_head *udf_read_untagged(struct super_block *, Uint32, Uint32); +extern void udf_release_data(struct buffer_head *); + +extern unsigned int udf_get_last_session(kdev_t); +extern unsigned int udf_get_last_block(kdev_t, int *); + +extern Uint32 udf_get_pblock(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_lb_pblock(struct super_block *, lb_addr, Uint32); + +extern int udf_get_filename(Uint8 *, Uint8 *, int); + +extern void udf_free_inode(struct inode *); +extern struct inode * udf_new_inode (const struct inode *, int, int *); +extern void udf_discard_prealloc(struct inode *); +extern void udf_truncate(struct inode *); +extern void udf_truncate_adinicb(struct inode *); +extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32); +extern int udf_alloc_blocks(const struct inode *, Uint16, Uint32, Uint32); +extern int udf_new_block(const struct inode *, Uint16, Uint32, int *); +extern int udf_sync_file(struct file *, struct dentry *); + +#else + +#include <sys/types.h> +#include <linux/udf_udf.h> + +#endif /* __KERNEL__ */ + +#include "udfend.h" + +/* structures */ +struct udf_directory_record +{ + Uint32 d_parent; + Uint32 d_inode; + Uint32 d_name[255]; +}; + +#define VDS_POS_PRIMARY_VOL_DESC 0 +#define VDS_POS_UNALLOC_SPACE_DESC 1 +#define VDS_POS_LOGICAL_VOL_DESC 2 +#define VDS_POS_PARTITION_DESC 3 +#define VDS_POS_IMP_USE_VOL_DESC 4 +#define VDS_POS_VOL_DESC_PTR 5 +#define VDS_POS_TERMINATING_DESC 6 +#define VDS_POS_LENGTH 7 + +struct udf_vds_record +{ + Uint32 block; + Uint32 volDescSeqNum; +}; + +struct ktm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_isdst; +}; + +struct ustr +{ + Uint8 u_cmpID; + Uint8 u_name[UDF_NAME_LEN-1]; + Uint8 u_len; + Uint8 padding; + unsigned long u_hash; +}; + + +#define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) ) +#define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) ) + +#ifdef __KERNEL__ + +#define CURRENT_UTIME (xtime.tv_usec) + +#define udf_file_entry_alloc_offset(inode)\ + ((UDF_I_EXTENDED_FE(inode) ?\ + sizeof(struct ExtendedFileEntry) :\ + sizeof(struct FileEntry)) + UDF_I_LENEATTR(inode)) + +#define udf_clear_bit(nr,addr) ext2_clear_bit(nr,addr) +#define udf_set_bit(nr,addr) ext2_set_bit(nr,addr) +#define udf_test_bit(nr, addr) ext2_test_bit(nr, addr) +#define udf_find_first_one_bit(addr, size) find_first_one_bit(addr, size) +#define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset) + +#define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x) +#define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y) +#define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y)) + +extern inline int find_next_one_bit (void * addr, int size, int offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset / BITS_PER_LONG); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= (BITS_PER_LONG-1); + if (offset) + { + tmp = leBPL_to_cpup(p++); + tmp &= ~0UL << offset; + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) + { + if ((tmp = leBPL_to_cpup(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = leBPL_to_cpup(p); +found_first: + tmp &= ~0UL >> (BITS_PER_LONG-size); +found_middle: + return result + ffz(~tmp); +} + +#define find_first_one_bit(addr, size)\ + find_next_one_bit((addr), (size), 0) + +#endif + +/* Miscellaneous UDF Prototypes */ + +extern int udf_ustr_to_dchars(Uint8 *, const struct ustr *, int); +extern int udf_ustr_to_char(Uint8 *, const struct ustr *, int); +extern int udf_ustr_to_dstring(dstring *, const struct ustr *, int); +extern int udf_dchars_to_ustr(struct ustr *, const Uint8 *, int); +extern int udf_char_to_ustr(struct ustr *, const Uint8 *, int); +extern int udf_dstring_to_ustr(struct ustr *, const dstring *, int); + +extern Uint16 udf_crc(Uint8 *, Uint32, Uint16); +extern int udf_translate_to_linux(Uint8 *, Uint8 *, int, Uint8 *, int); +extern int udf_build_ustr(struct ustr *, dstring *, int); +extern int udf_build_ustr_exact(struct ustr *, dstring *, int); +extern int udf_CS0toUTF8(struct ustr *, struct ustr *); +extern int udf_UTF8toCS0(dstring *, struct ustr *, int); + +extern uid_t udf_convert_uid(int); +extern gid_t udf_convert_gid(int); +extern Uint32 udf64_low32(Uint64); +extern Uint32 udf64_high32(Uint64); + + +extern time_t *udf_stamp_to_time(time_t *, timestamp); +extern timestamp *udf_time_to_stamp(timestamp *, time_t, long); +extern time_t udf_converttime (struct ktm *); + +#ifdef __KERNEL__ +extern Uint8 * +udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, + struct buffer_head **, int *); + +extern struct FileIdentDesc * +udf_fileident_read(struct inode *, int *, + struct udf_fileident_bh *, + struct FileIdentDesc *, + lb_addr *, Uint32 *, + Uint32 *, struct buffer_head **); +#endif +extern struct FileIdentDesc * +udf_get_fileident(void * buffer, int bufsize, int * offset); +extern extent_ad * udf_get_fileextent(void * buffer, int bufsize, int * offset); +extern long_ad * udf_get_filelongad(void * buffer, int bufsize, int * offset, int); +extern short_ad * udf_get_fileshortad(void * buffer, int bufsize, int * offset, int); +extern Uint8 * udf_get_filead(struct FileEntry *, Uint8 *, int, int, int, int *); + +extern void udf_update_tag(char *, int); +extern void udf_new_tag(char *, Uint16, Uint16, Uint16, Uint32, int); + +#endif diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h new file mode 100644 index 000000000..906a619bc --- /dev/null +++ b/fs/udf/udfend.h @@ -0,0 +1,111 @@ +#ifndef __UDF_ENDIAN_H +#define __UDF_ENDIAN_H + +#ifndef __KERNEL__ + +#if __BYTE_ORDER == 0 + +#error "__BYTE_ORDER must be defined" + +#elif __BYTE_ORDER == __BIG_ENDIAN + +#define le16_to_cpu(x) \ + ((Uint16)((((Uint16)(x) & 0x00FFU) << 8) | \ + (((Uint16)(x) & 0xFF00U) >> 8))) + +#define le32_to_cpu(x) \ + ((Uint32)((((Uint32)(x) & 0x000000FFU) << 24) | \ + (((Uint32)(x) & 0x0000FF00U) << 8) | \ + (((Uint32)(x) & 0x00FF0000U) >> 8) | \ + (((Uint32)(x) & 0xFF000000U) >> 24))) + +#define le64_to_cpu(x) \ + ((Uint64)((((Uint64)(x) & 0x00000000000000FFULL) << 56) | \ + (((Uint64)(x) & 0x000000000000FF00ULL) << 40) | \ + (((Uint64)(x) & 0x0000000000FF0000ULL) << 24) | \ + (((Uint64)(x) & 0x00000000FF000000ULL) << 8) | \ + (((Uint64)(x) & 0x000000FF00000000ULL) >> 8) | \ + (((Uint64)(x) & 0x0000FF0000000000ULL) >> 24) | \ + (((Uint64)(x) & 0x00FF000000000000ULL) >> 40) | \ + (((Uint64)(x) & 0xFF00000000000000ULL) >> 56))) + +#define cpu_to_le16(x) (le16_to_cpu(x)) +#define cpu_to_le32(x) (le32_to_cpu(x)) +#define cpu_to_le64(x) (le64_to_cpu(x)) + +#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ + +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) + +#endif + +#endif + +#ifdef __KERNEL__ +#include <linux/string.h> +#endif + +static inline lb_addr lelb_to_cpu(lb_addr in) +{ + lb_addr out; + out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum); + out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum); + return out; +} + +static inline lb_addr cpu_to_lelb(lb_addr in) +{ + lb_addr out; + out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); + out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); + return out; +} + +static inline timestamp lets_to_cpu(timestamp in) +{ + timestamp out; + memcpy(&out, &in, sizeof(timestamp)); + out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone); + out.year = le16_to_cpu(in.year); + return out; +} + +static inline long_ad lela_to_cpu(long_ad in) +{ + long_ad out; + out.extLength = le32_to_cpu(in.extLength); + out.extLocation = lelb_to_cpu(in.extLocation); + return out; +} + +static inline long_ad cpu_to_lela(long_ad in) +{ + long_ad out; + out.extLength = cpu_to_le32(in.extLength); + out.extLocation = cpu_to_lelb(in.extLocation); + return out; +} + +static inline extent_ad leea_to_cpu(extent_ad in) +{ + extent_ad out; + out.extLength = le32_to_cpu(in.extLength); + out.extLocation = le32_to_cpu(in.extLocation); + return out; +} + +static inline timestamp cpu_to_lets(timestamp in) +{ + timestamp out; + memcpy(&out, &in, sizeof(timestamp)); + out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone); + out.year = cpu_to_le16(in.year); + return out; +} + +#endif /* __UDF_ENDIAN_H */ diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c new file mode 100644 index 000000000..715fdb4bc --- /dev/null +++ b/fs/udf/udftime.c @@ -0,0 +1,373 @@ +/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Paul Eggert (eggert@twinsun.com). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* + * dgb 10/2/98: ripped this from glibc source to help convert timestamps to unix time + * 10/4/98: added new table-based lookup after seeing how ugly the gnu code is + */ + +/* Assume that leap seconds are possible, unless told otherwise. + If the host has a `zic' command with a `-L leapsecondfilename' option, + then it supports leap seconds; otherwise it probably doesn't. */ +#ifndef LEAP_SECONDS_POSSIBLE +#define LEAP_SECONDS_POSSIBLE 1 +#endif + +#if defined(__linux__) && defined(__KERNEL__) +#include <linux/types.h> +#include <linux/kernel.h> +#else +#include <stdio.h> +#include <sys/types.h> +#endif + +#include "udfdecl.h" + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#ifndef INT_MIN +#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1)) +#endif +#ifndef INT_MAX +#define INT_MAX (~0 - INT_MIN) +#endif + +#ifndef TIME_T_MIN +#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \ + : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)) +#endif +#ifndef TIME_T_MAX +#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN) +#endif + +#define TM_YEAR_BASE 1900 +#define EPOCH_YEAR 1970 + +#ifndef __isleap +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#endif + +/* How many days come before each month (0-12). */ +const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + +time_t udf_converttime (struct ktm *); +#ifndef USE_GNU_MKTIME_METHOD + +#define MAX_YEAR_SECONDS 68 + +time_t year_seconds[MAX_YEAR_SECONDS]= { + 0, + /*1971:*/ 31554000, /*1972:*/ 63090000, /*1973:*/ 94712400, + /*1974:*/ 126248400, /*1975:*/ 157784400, /*1976:*/ 189320400, + /*1977:*/ 220942800, /*1978:*/ 252478800, /*1979:*/ 284014800, + /*1980:*/ 315550800, /*1981:*/ 347173200, /*1982:*/ 378709200, + /*1983:*/ 410245200, /*1984:*/ 441781200, /*1985:*/ 473403600, + /*1986:*/ 504939600, /*1987:*/ 536475600, /*1988:*/ 568011600, + /*1989:*/ 599634000, /*1990:*/ 631170000, /*1991:*/ 662706000, + /*1992:*/ 694242000, /*1993:*/ 725864400, /*1994:*/ 757400400, + /*1995:*/ 788936400, /*1996:*/ 820472400, /*1997:*/ 852094800, + /*1998:*/ 883630800, /*1999:*/ 915166800, /*2000:*/ 946702800, + /*2001:*/ 978325200, /*2002:*/ 1009861200, /*2003:*/ 1041397200, + /*2004:*/ 1072933200, /*2005:*/ 1104555600, /*2006:*/ 1136091600, + /*2007:*/ 1167627600, /*2008:*/ 1199163600, /*2009:*/ 1230786000, + /*2010:*/ 1262322000, /*2011:*/ 1293858000, /*2012:*/ 1325394000, + /*2013:*/ 1357016400, /*2014:*/ 1388552400, /*2015:*/ 1420088400, + /*2016:*/ 1451624400, /*2017:*/ 1483246800, /*2018:*/ 1514782800, + /*2019:*/ 1546318800, /*2020:*/ 1577854800, /*2021:*/ 1609477200, + /*2022:*/ 1641013200, /*2023:*/ 1672549200, /*2024:*/ 1704085200, + /*2025:*/ 1735707600, /*2026:*/ 1767243600, /*2027:*/ 1798779600, + /*2028:*/ 1830315600, /*2029:*/ 1861938000, /*2030:*/ 1893474000, + /*2031:*/ 1925010000, /*2032:*/ 1956546000, /*2033:*/ 1988168400, + /*2034:*/ 2019704400, /*2035:*/ 2051240400, /*2036:*/ 2082776400, + /*2037:*/ 2114398800 +}; + +time_t udf_converttime (struct ktm *tm) +{ + time_t r; + int yday; + + if ( !tm ) + return -1; + if ( (tm->tm_year+TM_YEAR_BASE < EPOCH_YEAR) || + (tm->tm_year+TM_YEAR_BASE > EPOCH_YEAR+MAX_YEAR_SECONDS) ) + return -1; + r = year_seconds[tm->tm_year-70]; + + yday = ((__mon_yday[__isleap (tm->tm_year + TM_YEAR_BASE)] + [tm->tm_mon-1]) + + tm->tm_mday - 1); + r += ( ( (yday* 24) + (tm->tm_hour-1) ) * 60 + tm->tm_min ) * 60 + tm->tm_sec; + return r; +} + +#ifdef __KERNEL__ + +extern struct timezone sys_tz; + +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +timestamp * +udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) +{ + long int days, rem, y; + const unsigned short int *ip; + int offset = (-sys_tz.tz_minuteswest + (sys_tz.tz_dsttime ? 60 : 0)); + + if (!dest) + return NULL; + + dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); + + tv_sec += offset * 60; + days = tv_sec / SECS_PER_DAY; + rem = tv_sec % SECS_PER_DAY; + dest->hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + dest->minute = rem / 60; + dest->second = rem % 60; + y = 1970; + +#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) + { + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + dest->year = y; + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + dest->month = y + 1; + dest->day = days + 1; + + dest->centiseconds = tv_usec / 10000; + dest->hundredsOfMicroseconds = (tv_usec - dest->centiseconds * 10000) / 100; + dest->microseconds = (tv_usec - dest->centiseconds * 10000 - + dest->hundredsOfMicroseconds * 100); + return dest; +} +#endif + +#else + +static time_t ydhms_tm_diff (int, int, int, int, int, const struct ktm *); + + +/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP), + measured in seconds, ignoring leap seconds. + YEAR uses the same numbering as TM->tm_year. + All values are in range, except possibly YEAR. + If overflow occurs, yield the low order bits of the correct answer. */ +static time_t +ydhms_tm_diff (int year, int yday, int hour, int min, int sec, const struct ktm *tp) +{ + time_t result; + + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid int overflow. time_t overflow is OK, since + only the low order bits of the correct time_t answer are needed. + Don't convert to time_t until after all divisions are done, since + time_t might be unsigned. */ + int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3); + int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = a100 >> 2; + int b400 = b100 >> 2; + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + time_t years = year - (time_t) tp->tm_year; + time_t days = (365 * years + intervening_leap_days); + result= (60 * (60 * (24 * days + (hour - tp->tm_hour)) + + (min - tp->tm_min)) + + (sec - tp->tm_sec)); +#ifdef __KERNEL__ + printk(KERN_ERR "udf: ydhms_tm_diff(%d,%d,%d,%d,%d,) returning %ld\n", + year, yday, hour, min, sec, result); +#endif + return result; +} + + +/* Convert *TP to a time_t value, inverting + the monotonic and mostly-unit-linear conversion function CONVERT. + Use *OFFSET to keep track of a guess at the offset of the result, + compared to what the result would be for UTC without leap seconds. + If *OFFSET's guess is correct, only one CONVERT call is needed. */ +time_t +udf_converttime (struct ktm *tp) +{ + time_t t, dt, t0; + struct ktm tm; + + /* The maximum number of probes (calls to CONVERT) should be enough + to handle any combinations of time zone rule changes, solar time, + and leap seconds. Posix.1 prohibits leap seconds, but some hosts + have them anyway. */ + int remaining_probes = 4; + + /* Time requested. Copy it in case CONVERT modifies *TP; this can + occur if TP is localtime's returned value and CONVERT is localtime. */ + int sec = tp->tm_sec; + int min = tp->tm_min; + int hour = tp->tm_hour; + int mday = tp->tm_mday; + int mon = tp->tm_mon; + int year_requested = tp->tm_year; + int isdst = tp->tm_isdst; + + /* Ensure that mon is in range, and set year accordingly. */ + int mon_remainder = mon % 12; + int negative_mon_remainder = mon_remainder < 0; + int mon_years = mon / 12 - negative_mon_remainder; + int year = year_requested + mon_years; + + /* The other values need not be in range: + the remaining code handles minor overflows correctly, + assuming int and time_t arithmetic wraps around. + Major overflows are caught at the end. */ + + /* Calculate day of year from year, month, and day of month. + The result need not be in range. */ + int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)] + [mon_remainder + 12 * negative_mon_remainder]) + + mday - 1); + +#if LEAP_SECONDS_POSSIBLE + /* Handle out-of-range seconds specially, + since ydhms_tm_diff assumes every minute has 60 seconds. */ + int sec_requested = sec; + if (sec < 0) + sec = 0; + if (59 < sec) + sec = 59; +#endif + + /* Invert CONVERT by probing. First assume the same offset as last time. + Then repeatedly use the error to improve the guess. */ + + tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE; + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + /* + t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm); + + for (t = t0; + (dt = ydhms_tm_diff (year, yday, hour, min, sec, &tm)); + t += dt) + if (--remaining_probes == 0) + return -1; + */ + + /* Check whether tm.tm_isdst has the requested value, if any. */ + if (0 <= isdst && 0 <= tm.tm_isdst) + { + int dst_diff = (isdst != 0) - (tm.tm_isdst != 0); + if (dst_diff) + { + /* Move two hours in the direction indicated by the disagreement, + probe some more, and switch to a new time if found. + The largest known fallback due to daylight savings is two hours: + once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */ + time_t ot = t - 2 * 60 * 60 * dst_diff; + while (--remaining_probes != 0) + { + struct ktm otm; + if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec, + &otm))) + { + t = ot; + tm = otm; + break; + } + if ((ot += dt) == t) + break; /* Avoid a redundant probe. */ + } + } + } + + +#if LEAP_SECONDS_POSSIBLE + if (sec_requested != tm.tm_sec) + { + /* Adjust time to reflect the tm_sec requested, not the normalized value. + Also, repair any damage from a false match due to a leap second. */ + t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60); + } +#endif + + if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) + { + /* time_t isn't large enough to rule out overflows in ydhms_tm_diff, + so check for major overflows. A gross check suffices, + since if t has overflowed, it is off by a multiple of + TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of + the difference that is bounded by a small value. */ + + double dyear = (double) year_requested + mon_years - tm.tm_year; + double dday = 366 * dyear + mday; + double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested; + + if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec)) + return -1; + } + + *tp = tm; +#ifdef __KERNEL__ + udf_debug("returning %ld\n", t); +#endif + return t; +} +#endif + +#ifdef INCLUDE_PRINT_KTM +static void +print_ktm (struct ktm *tp) +{ +#ifdef __KERNEL__ + udf_debug( +#else + printf( +#endif + "%04d-%02d-%02d %02d:%02d:%02d isdst %d", + tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec, tp->tm_isdst); +} +#endif + +/* EOF */ diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c new file mode 100644 index 000000000..6cf63164d --- /dev/null +++ b/fs/udf/unicode.c @@ -0,0 +1,447 @@ +/* + * unicode.c + * + * PURPOSE + * Routines for converting between UTF-8 and OSTA Compressed Unicode. + * Also handles filename mangling + * + * DESCRIPTION + * OSTA Compressed Unicode is explained in the OSTA UDF specification. + * http://www.osta.org/ + * UTF-8 is explained in the IETF RFC XXXX. + * ftp://ftp.internic.net/rfc/rfcxxxx.txt + * + * CONTACTS + * E-mail regarding any portion of the Linux UDF file system should be + * directed to the development team's mailing list (run by majordomo): + * linux_udf@hootie.lvld.hp.com + * + * COPYRIGHT + * This file is distributed under the terms of the GNU General Public + * License (GPL). Copies of the GPL can be obtained from: + * ftp://prep.ai.mit.edu/pub/gnu/GPL + * Each contributing author retains all rights to their own work. + */ + + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <linux/string.h> /* for memset */ +#include <linux/udf_fs.h> +#else +#include <string.h> +#endif + +#include "udfdecl.h" + +int udf_ustr_to_dchars(Uint8 *dest, const struct ustr *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (src->u_len > strlen) ) + return 0; + memcpy(dest+1, src->u_name, src->u_len-1); + dest[0] = src->u_cmpID; + return src->u_len; +} + +int udf_ustr_to_char(Uint8 *dest, const struct ustr *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (src->u_len >= strlen) ) + return 0; + memcpy(dest, src->u_name, src->u_len-1); + return src->u_len - 1; +} + +int udf_ustr_to_dstring(dstring *dest, const struct ustr *src, int dlength) +{ + if ( udf_ustr_to_dchars(dest, src, dlength-1) ) + { + dest[dlength-1] = src->u_len; + return dlength; + } + else + return 0; +} + +int udf_dchars_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN) ) + return 0; + memset(dest, 0, sizeof(struct ustr)); + memcpy(dest->u_name, src+1, strlen-1); + dest->u_cmpID = src[0]; + dest->u_len = strlen; + return strlen; +} + +int udf_char_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (strlen >= UDF_NAME_LEN) ) + return 0; + memset(dest, 0, sizeof(struct ustr)); + memcpy(dest->u_name, src, strlen); + dest->u_cmpID = 0x08; + dest->u_len = strlen + 1; + return strlen + 1; +} + + +int udf_dstring_to_ustr(struct ustr *dest, const dstring *src, int dlength) +{ + if ( dlength && udf_dchars_to_ustr(dest, src, src[dlength-1]) ) + return dlength; + else + return 0; +} + +/* + * udf_build_ustr + */ +int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) +{ + int usesize; + + if ( (!dest) || (!ptr) || (!size) ) + return -1; + + memset(dest, 0, sizeof(struct ustr)); + usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size; + dest->u_cmpID=ptr[0]; + dest->u_len=ptr[size-1]; + memcpy(dest->u_name, ptr+1, usesize-1); + return 0; +} + +/* + * udf_build_ustr_exact + */ +int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) +{ + if ( (!dest) || (!ptr) || (!exactsize) ) + return -1; + + memset(dest, 0, sizeof(struct ustr)); + dest->u_cmpID=ptr[0]; + dest->u_len=exactsize-1; + memcpy(dest->u_name, ptr+1, exactsize-1); + return 0; +} + +/* + * udf_ocu_to_udf8 + * + * PURPOSE + * Convert OSTA Compressed Unicode to the UTF-8 equivalent. + * + * DESCRIPTION + * This routine is only called by udf_filldir(). + * + * PRE-CONDITIONS + * utf Pointer to UTF-8 output buffer. + * ocu Pointer to OSTA Compressed Unicode input buffer + * of size UDF_NAME_LEN bytes. + * both of type "struct ustr *" + * + * POST-CONDITIONS + * <return> Zero on success. + * + * HISTORY + * November 12, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) +{ + Uint8 *ocu; + Uint32 c; + Uint8 cmp_id, ocu_len; + int i; + + ocu = ocu_i->u_name; + + ocu_len = ocu_i->u_len; + cmp_id = ocu_i->u_cmpID; + utf_o->u_len = 0; + + if (ocu_len == 0) + { + memset(utf_o, 0, sizeof(struct ustr)); + utf_o->u_cmpID = 0; + utf_o->u_len = 0; + return 0; + } + + if ((cmp_id != 8) && (cmp_id != 16)) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); +#endif + return 0; + } + + for (i = 0; (i < ocu_len) && (utf_o->u_len < UDF_NAME_LEN) ;) { + + /* Expand OSTA compressed Unicode to Unicode */ + c = ocu[i++]; + if (cmp_id == 16) + { + c = (c << 8) | ocu[i++]; +#ifdef __KERNEL__ + if (c & 0xFF00) + udf_debug("cmd_id == 16 (0x%2x%2x)\n", + ((c >> 8) & 0xFF), (c & 0xFF)); +#endif + } + + /* Compress Unicode to UTF-8 */ + if (c < 0x80U) + utf_o->u_name[utf_o->u_len++] = (Uint8)c; + else if (c < 0x800U) { + utf_o->u_name[utf_o->u_len++] = (Uint8)(0xc0 | (c >> 6)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); +#ifdef __KERNEL__ + udf_debug("(0x%2x%2x) -> (%2x) (%2x)\n", + ((c >> 8) & 0xFF), (c & 0xFF), + utf_o->u_name[utf_o->u_len-2], + utf_o->u_name[utf_o->u_len-1]); +#endif + } else { + utf_o->u_name[utf_o->u_len++] = (Uint8)(0xe0 | (c >> 12)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | ((c >> 6) & 0x3f)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); +#ifdef __KERNEL__ + udf_debug("(0x%2x%2x) -> (%2x) (%2x) (%2x)\n", + ((c >> 8) & 0xFF), (c & 0xFF), + utf_o->u_name[utf_o->u_len-3], + utf_o->u_name[utf_o->u_len-2], + utf_o->u_name[utf_o->u_len-1]); +#endif + } + } + utf_o->u_cmpID=8; + utf_o->u_hash=0L; + utf_o->padding=0; + + return utf_o->u_len; +} + +/* + * + * udf_utf8_to_ocu + * + * PURPOSE + * Convert UTF-8 to the OSTA Compressed Unicode equivalent. + * + * DESCRIPTION + * This routine is only called by udf_lookup(). + * + * PRE-CONDITIONS + * ocu Pointer to OSTA Compressed Unicode output + * buffer of size UDF_NAME_LEN bytes. + * utf Pointer to UTF-8 input buffer. + * utf_len Length of UTF-8 input buffer in bytes. + * + * POST-CONDITIONS + * <return> Zero on success. + * + * HISTORY + * November 12, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) +{ + unsigned c, i, max_val, utf_char; + int utf_cnt; + int u_len = 0; + + memset(ocu, 0, sizeof(dstring) * length); + ocu[0] = 8; + max_val = 0xffU; + +try_again: + utf_char = 0U; + utf_cnt = 0U; + for (i = 0U; i < utf->u_len; i++) { + c = (unsigned)utf->u_name[i]; + + /* Complete a multi-byte UTF-8 character */ + if (utf_cnt) { + utf_char = (utf_char << 6) | (c & 0x3fU); + if (--utf_cnt) + continue; + } else { + /* Check for a multi-byte UTF-8 character */ + if (c & 0x80U) { + /* Start a multi-byte UTF-8 character */ + if ((c & 0xe0U) == 0xc0U) { + utf_char = c & 0x1fU; + utf_cnt = 1; + } else if ((c & 0xf0U) == 0xe0U) { + utf_char = c & 0x0fU; + utf_cnt = 2; + } else if ((c & 0xf8U) == 0xf0U) { + utf_char = c & 0x07U; + utf_cnt = 3; + } else if ((c & 0xfcU) == 0xf8U) { + utf_char = c & 0x03U; + utf_cnt = 4; + } else if ((c & 0xfeU) == 0xfcU) { + utf_char = c & 0x01U; + utf_cnt = 5; + } else + goto error_out; + continue; + } else + /* Single byte UTF-8 character (most common) */ + utf_char = c; + } + + /* Choose no compression if necessary */ + if (utf_char > max_val) { + if ( 0xffU == max_val ) { + max_val = 0xffffU; + ocu[0] = (Uint8)0x10U; + goto try_again; + } + goto error_out; + } + + if (max_val == 0xffffU) + ocu[++u_len] = (Uint8)(utf_char >> 8); + ocu[++u_len] = (Uint8)(utf_char & 0xffU); + } + + if (utf_cnt) { +error_out: +#ifdef __KERNEL__ + printk(KERN_ERR "udf: bad UTF-8 character\n"); +#endif + return 0; + } + + ocu[length - 1] = (Uint8)u_len; + return u_len; +} + +#ifdef __KERNEL__ +int udf_get_filename(Uint8 *sname, Uint8 *dname, int flen) +{ + struct ustr filename, unifilename; + int len; + + if (udf_build_ustr_exact(&unifilename, sname, flen)) + { + return 0; + } + + if (!udf_CS0toUTF8(&filename, &unifilename) ) + { + udf_debug("Failed in udf_get_filename: sname = %s\n", sname); + return 0; + } + + if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, + unifilename.u_name, unifilename.u_len))) + { + return len; + } + return 0; +} +#endif + +#define ILLEGAL_CHAR_MARK '_' +#define EXT_MARK '.' +#define CRC_MARK '#' +#define EXT_SIZE 5 + +int udf_translate_to_linux(Uint8 *newName, Uint8 *udfName, int udfLen, Uint8 *fidName, int fidNameLen) +{ + int index, newIndex = 0, needsCRC = 0; + int extIndex = 0, newExtIndex = 0, hasExt = 0; + unsigned short valueCRC; + Uint8 curr; + const Uint8 hexChar[] = "0123456789ABCDEF"; + + if (udfName[0] == '.' && (udfLen == 1 || + (udfLen == 2 && udfName[1] == '.'))) + { + needsCRC = 1; + newIndex = udfLen; + memcpy(newName, udfName, udfLen); + } + else + { + for (index = 0; index < udfLen; index++) + { + curr = udfName[index]; + if (curr == '/' || curr == 0) + { + needsCRC = 1; + curr = ILLEGAL_CHAR_MARK; + while (index+1 < udfLen && (udfName[index+1] == '/' || + udfName[index+1] == 0)) + index++; + } + if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) + { + if (udfLen == index + 1) + hasExt = 0; + else + { + hasExt = 1; + extIndex = index; + newExtIndex = newIndex; + } + } + if (newIndex < 256) + newName[newIndex++] = curr; + else + needsCRC = 1; + } + } + if (needsCRC) + { + Uint8 ext[EXT_SIZE]; + int localExtIndex = 0; + + if (hasExt) + { + int maxFilenameLen; + for(index = 0; index<EXT_SIZE && extIndex + index +1 < udfLen; + index++ ) + { + curr = udfName[extIndex + index + 1]; + + if (curr == '/' || curr == 0) + { + needsCRC = 1; + curr = ILLEGAL_CHAR_MARK; + while(extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE + && (udfName[extIndex + index + 2] == '/' || + udfName[extIndex + index + 2] == 0))) + index++; + } + ext[localExtIndex++] = curr; + } + maxFilenameLen = 250 - localExtIndex; + if (newIndex > maxFilenameLen) + newIndex = maxFilenameLen; + else + newIndex = newExtIndex; + } + else if (newIndex > 250) + newIndex = 250; + newName[newIndex++] = CRC_MARK; + valueCRC = udf_crc(fidName, fidNameLen, 0); + newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; + newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; + newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; + newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; + + if (hasExt) + { + newName[newIndex++] = EXT_MARK; + for (index = 0;index < localExtIndex ;index++ ) + newName[newIndex++] = ext[index]; + } + } + return newIndex; +} diff --git a/fs/ufs/super.c b/fs/ufs/super.c index b6a1f8ef6..27d404ca1 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -935,7 +935,7 @@ static struct file_system_type ufs_fs_type = { NULL }; -__initfunc(int init_ufs_fs(void)) +int __init init_ufs_fs(void) { return register_filesystem(&ufs_fs_type); } diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index b7214fc49..99069517e 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -455,7 +455,8 @@ void ufs_truncate (struct inode * inode) break; if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) ufs_sync_inode (inode); - current->counter = 0; + run_task_queue(&tq_disk); + current->policy |= SCHED_YIELD; schedule (); diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index f4c8dfb53..8856feba7 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -8,7 +8,7 @@ # Note 2: the CFLAGS definitions are now in the main makefile. O_TARGET := umsdos.o -O_OBJS := dir.o file.o inode.o ioctl.o mangle.o namei.o \ +O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o \ rdir.o symlink.o emd.o check.o M_OBJS := $(O_TARGET) diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index e9860caae..1eda3df93 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -92,24 +92,7 @@ void check_inode (struct inode *inode) } printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched); - - if (inode->i_op == NULL) { - printk (" (i_op is NULL)\n"); - } else if (inode->i_op == &umsdos_dir_inode_operations) { - printk (" (i_op is umsdos_dir_inode_operations)\n"); - } else if (inode->i_op == &umsdos_file_inode_operations) { - printk (" (i_op is umsdos_file_inode_operations)\n"); - } else if (inode->i_op == &umsdos_file_inode_operations_no_bmap) { - printk (" (i_op is umsdos_file_inode_operations_no_bmap)\n"); - } else if (inode->i_op == &umsdos_file_inode_operations_readpage) { - printk (" (i_op is umsdos_file_inode_operations_readpage)\n"); - } else if (inode->i_op == &umsdos_rdir_inode_operations) { - printk (" (i_op is umsdos_rdir_inode_operations)\n"); - } else if (inode->i_op == &umsdos_symlink_inode_operations) { - printk (" (i_op is umsdos_symlink_inode_operations)\n"); - } else { - printk (" (i_op is UNKNOWN: %p)\n", inode->i_op); - } + } else { printk (KERN_DEBUG "* inode is NULL\n"); } diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 34fe302d7..c09c293cf 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -393,7 +393,6 @@ void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info) inode->i_uid = entry->uid; inode->i_gid = entry->gid; - MSDOS_I (inode)->i_binary = 1; /* #Specification: umsdos / i_nlink * The nlink field of an inode is maintained by the MSDOS file system * for directory and by UMSDOS for other files. The logic is that @@ -832,8 +831,8 @@ struct inode_operations umsdos_dir_inode_operations = UMSDOS_rename, /* rename */ NULL, /* readlink */ NULL, /* followlink */ - fat_bmap, /* get_block */ - block_read_full_page, /* readpage */ + NULL, /* get_block */ + NULL, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ NULL, /* truncate */ diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index d5a7a9578..d9b00e8c9 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -34,7 +34,6 @@ ssize_t umsdos_file_read_kmem ( struct file *filp, mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); - MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; ret = fat_file_read (filp, buf, count, &filp->f_pos); set_fs (old_fs); return ret; @@ -54,15 +53,6 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp, mm_segment_t old_fs = get_fs (); ssize_t ret; - /* note: i_binary=2 is for CVF-FAT. We put it here, instead of - * umsdos_file_write_kmem, since it is also wise not to compress - * symlinks (in the unlikely event that they are > 512 bytes and - * can be compressed. - * FIXME: should we set it when reading symlinks too? - */ - - MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; - set_fs (KERNEL_DS); ret = fat_file_write (filp, buf, count, &filp->f_pos); set_fs (old_fs); diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c deleted file mode 100644 index b930c46ae..000000000 --- a/fs/umsdos/file.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * linux/fs/umsdos/file.c - * - * Written 1993 by Jacques Gelinas - * inspired from linux/fs/msdos/file.c Werner Almesberger - * - * Extended MS-DOS regular file handling primitives - */ - -#include <linux/sched.h> -#include <linux/fs.h> -#include <linux/msdos_fs.h> -#include <linux/errno.h> -#include <linux/fcntl.h> -#include <linux/stat.h> -#include <linux/umsdos_fs.h> - -#include <asm/uaccess.h> -#include <asm/system.h> - -/* - * Read a file into user space memory - */ -static ssize_t UMSDOS_file_read ( - struct file *filp, - char *buf, - size_t count, - loff_t * ppos -) -{ - struct dentry *dentry = filp->f_dentry; - struct inode *inode = dentry->d_inode; - - int ret = fat_file_read (filp, buf, count, ppos); - - /* We have to set the access time because msdos don't care */ - if (!IS_RDONLY (inode)) { - inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return ret; -} - - -/* - * Write a file from user space memory - */ -static ssize_t UMSDOS_file_write ( - struct file *filp, - const char *buf, - size_t count, - loff_t * ppos) -{ - return fat_file_write (filp, buf, count, ppos); -} - - -/* - * Truncate a file to 0 length. - */ -static void UMSDOS_truncate (struct inode *inode) -{ - Printk (("UMSDOS_truncate\n")); - if (!IS_RDONLY (inode)) { - fat_truncate (inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty(inode); - } -} - -/* Function for normal file system (512 bytes hardware sector size) */ -struct file_operations umsdos_file_operations = -{ - NULL, /* lseek - default */ - UMSDOS_file_read, /* read */ - UMSDOS_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - file_fsync /* fsync */ -}; - -struct inode_operations umsdos_file_inode_operations = -{ - &umsdos_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - fat_bmap, /* get_block */ - block_read_full_page, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - UMSDOS_truncate, /* truncate */ - NULL, /* permission */ - fat_smap, /* smap */ - NULL /* revalidate */ -}; - -/* 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, /* poll - default */ - NULL, /* ioctl - default */ - fat_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - 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, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - UMSDOS_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -/* For other with larger and unaligned file system with readpage */ -struct file_operations umsdos_file_operations_readpage = -{ - NULL, /* lseek - default */ - UMSDOS_file_read, /* read */ - UMSDOS_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - NULL, /* release */ - file_fsync /* fsync */ -}; - -struct inode_operations umsdos_file_inode_operations_readpage = -{ - &umsdos_file_operations_readpage, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow link */ - NULL, /* get_block */ - fat_readpage, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - UMSDOS_truncate, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 81806ca18..171aa15dc 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -42,7 +42,7 @@ void fill_new_filp (struct file *filp, struct dentry *dentry) filp->f_reada = 1; filp->f_flags = O_RDWR; filp->f_dentry = dentry; - filp->f_op = &umsdos_file_operations; + filp->f_op = dentry->d_inode->i_op->default_file_ops; } @@ -146,19 +146,7 @@ dentry, f_pos)); umsdos_set_dirinfo_new(dentry, f_pos); if (S_ISREG (inode->i_mode)) { - if (MSDOS_SB (inode->i_sb)->cvf_format) { - if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) { - inode->i_op = &umsdos_file_inode_operations_readpage; - } else { - inode->i_op = &umsdos_file_inode_operations_no_bmap; - } - } else { - if (inode->i_op->bmap != NULL) { - inode->i_op = &umsdos_file_inode_operations; - } else { - inode->i_op = &umsdos_file_inode_operations_no_bmap; - } - } + /* All set */ } else if (S_ISDIR (inode->i_mode)) { umsdos_setup_dir(dentry); } else if (S_ISLNK (inode->i_mode)) { @@ -446,7 +434,7 @@ static struct file_system_type umsdos_fs_type = NULL }; -__initfunc (int init_umsdos_fs (void)) +int __init init_umsdos_fs (void) { return register_filesystem (&umsdos_fs_type); } diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 2d08a35d8..8f94230e3 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -135,7 +135,7 @@ struct inode_operations umsdos_symlink_inode_operations = NULL, /* rename */ UMSDOS_readlink, /* readlink */ UMSDOS_followlink, /* followlink */ - fat_bmap, /* get_block */ + fat_get_block, /* get_block */ block_read_full_page, /* readpage */ NULL, /* writepage */ NULL, /* flushpage */ diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index ad241c3cd..0f23487ba 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -982,35 +982,6 @@ int vfat_create(struct inode *dir,struct dentry* dentry,int mode) return 0; } -static int vfat_create_dotdirs(struct inode *dir, struct inode *parent) -{ - struct super_block *sb = dir->i_sb; - struct buffer_head *bh; - struct msdos_dir_entry *de; - __u16 date, time; - - if ((bh = fat_add_cluster1(dir)) == NULL) return -ENOSPC; - /* zeroed out, so... */ - fat_date_unix2dos(dir->i_mtime,&time,&date); - de = (struct msdos_dir_entry*)&bh->b_data[0]; - memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME); - memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME); - de[0].attr = de[1].attr = ATTR_DIR; - de[0].ctime = de[0].time = de[1].ctime = de[1].time = CT_LE_W(time); - de[0].adate = de[0].cdate = de[0].date = de[1].adate = - de[1].cdate = de[1].date = CT_LE_W(date); - de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart); - de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16); - de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart); - de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16); - fat_mark_buffer_dirty(sb, bh, 1); - fat_brelse(sb, bh); - dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(dir); - - return 0; -} - static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, struct buffer_head *bh, struct msdos_dir_entry *de) { @@ -1110,8 +1081,7 @@ int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode) dir->i_version = event; dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ - - res = vfat_create_dotdirs(inode, dir); + res = fat_new_dir(inode, dir, 1); if (res < 0) goto mkdir_failed; dentry->d_time = dentry->d_parent->d_inode->i_version; |