diff options
Diffstat (limited to 'fs')
130 files changed, 5965 insertions, 3755 deletions
diff --git a/fs/Config.in b/fs/Config.in index 4e16dd0e7..57d46b32a 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -80,6 +80,10 @@ if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS + tristate 'QNX filesystem support (EXPERIMENTAL)' CONFIG_QNX4FS_FS + if [ "$CONFIG_QNX4FS_FS" != "n" ]; then + bool ' QNXFS read-write support (FOR TESTING ONLY)' CONFIG_QNX4FS_RW + fi fi bool 'Macintosh partition map support' CONFIG_MAC_PARTITION endmenu diff --git a/fs/Makefile b/fs/Makefile index dce6691e3..1bbf1cbab 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -18,7 +18,7 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \ - nls devpts adfs + nls devpts adfs qnx4 ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -207,6 +207,14 @@ else endif endif +ifeq ($(CONFIG_QNX4FS_FS),y) +SUB_DIRS += qnx4 +else + ifeq ($(CONFIG_QNX4FS_FS),m) + MOD_SUB_DIRS += qnx4 + endif +endif + ifeq ($(CONFIG_AUTOFS_FS),y) SUB_DIRS += autofs else diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index bdadee2c5..12f7f6f6d 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -27,6 +27,7 @@ static struct file_operations adfs_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync */ NULL, /* fasync */ diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 5627c7db4..a886c68ce 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -40,6 +40,7 @@ static struct file_operations adfs_file_operations = { NULL, /* ioctl */ generic_file_mmap, /* mmap */ NULL, /* open - not special */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* fsync */ NULL, /* fasync */ diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 4cc742179..51e45b682 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -36,6 +36,7 @@ static struct file_operations affs_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync /* default fsync */ }; diff --git a/fs/affs/file.c b/fs/affs/file.c index 6f0db87fd..1961b4ec3 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -52,6 +52,7 @@ static struct file_operations affs_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* brute force, but works */ NULL, /* fasync */ @@ -92,6 +93,7 @@ static struct file_operations affs_file_operations_ofs = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* brute force, but works */ NULL, /* fasync */ diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 9264806b4..67b2c04bf 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -53,6 +53,7 @@ static struct file_operations autofs_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 0947e40e0..41694e8eb 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -32,6 +32,7 @@ static struct file_operations autofs_root_operations = { autofs_root_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 71106655b..6eee191e8 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -38,6 +38,7 @@ static struct file_operations bad_file_ops = EIO_ERROR, /* ioctl */ EIO_ERROR, /* mmap */ EIO_ERROR, /* open */ + EIO_ERROR, /* flush */ EIO_ERROR, /* release */ EIO_ERROR, /* fsync */ EIO_ERROR, /* fasync */ diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 122491c7a..f4d37d3c5 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -318,7 +318,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs return -ENOEXEC; } - current->personality = PER_LINUX; fd_offset = N_TXTOFF(ex); #ifdef __i386__ @@ -350,6 +349,8 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs return retval; /* OK, This is the point of no return */ + current->personality = PER_LINUX; + #if defined(__sparc__) && !defined(__sparc_v9__) memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); #endif @@ -396,6 +397,8 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs MAP_FIXED|MAP_PRIVATE, 0); read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0); #endif + flush_icache_range((unsigned long) 0, + (unsigned long) ex.a_text+ex.a_data); } else { if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && (N_MAGIC(ex) != NMAGIC)) @@ -413,6 +416,9 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs MAP_FIXED|MAP_PRIVATE, 0); read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); + flush_icache_range((unsigned long) N_TXTADDR(ex), + (unsigned long) N_TXTADDR(ex) + + ex.a_text+ex.a_data); goto beyond_if; } @@ -563,7 +569,7 @@ load_aout_library(int fd) } -__initfunc(int init_aout_binfmt(void)) +int __init init_aout_binfmt(void) { return register_binfmt(&aout_format); } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index de368e588..dd7ee459b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1196,8 +1196,9 @@ static int elf_core_dump(long signr, struct pt_regs * regs) notes[1].type = NT_PRPSINFO; notes[1].datasz = sizeof(psinfo); notes[1].data = &psinfo; - psinfo.pr_state = current->state; - psinfo.pr_sname = (current->state < 0 || current->state > 5) ? '.' : "RSDZTD"[current->state]; + i = current->state ? ffz(~current->state) + 1 : 0; + psinfo.pr_state = i; + psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[i]; psinfo.pr_zomb = psinfo.pr_sname == 'Z'; psinfo.pr_nice = current->priority-15; psinfo.pr_flag = current->flags; @@ -1333,7 +1334,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) } #endif /* USE_ELF_CORE_DUMP */ -__initfunc(int init_elf_binfmt(void)) +int __init init_elf_binfmt(void) { return register_binfmt(&elf_format); } diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index 133586e69..d1992ca06 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -110,7 +110,7 @@ struct linux_binfmt em86_format = { #endif }; -__initfunc(int init_em86_binfmt(void)) +int __init init_em86_binfmt(void) { return register_binfmt(&em86_format); } diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c index 41e66a21a..ca1ad396c 100644 --- a/fs/binfmt_java.c +++ b/fs/binfmt_java.c @@ -174,7 +174,7 @@ static struct linux_binfmt applet_format = { #endif }; -__initfunc(int init_java_binfmt(void)) +int __init init_java_binfmt(void) { register_binfmt(&java_format); return register_binfmt(&applet_format); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index bc1da662e..8acea3b64 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -488,7 +488,7 @@ static void bm_modcount(struct inode *inode, int fill) } #endif -__initfunc(int init_misc_binfmt(void)) +int __init init_misc_binfmt(void) { struct proc_dir_entry *status = NULL, *reg; int error = -ENOMEM; diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 5a38cf545..6aa1508a4 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -107,7 +107,7 @@ struct linux_binfmt script_format = { #endif }; -__initfunc(int init_script_binfmt(void)) +int __init init_script_binfmt(void) { return register_binfmt(&script_format); } diff --git a/fs/buffer.c b/fs/buffer.c index 7bf6e2824..103caefa3 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1736,7 +1736,7 @@ void show_buffers(void) * Use gfp() for the hash table to decrease TLB misses, use * SLAB cache for buffer heads. */ -__initfunc(void buffer_init(void)) +void __init buffer_init(void) { int order = 5; /* Currently maximum order.. */ unsigned int nr_hash; diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 2f0f86878..4da714208 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -14,7 +14,6 @@ extern int coda_debug; extern int coda_print_entry; /* cnode.c */ - static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) { CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino); @@ -117,7 +116,6 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) return 0; } - inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) { int eq; @@ -127,6 +125,27 @@ inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) return eq; } +void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, + struct ViceFid *newfid) +{ + struct coda_inode_info *cnp; + struct coda_sb_info *sbi= coda_sbp(inode->i_sb); + + cnp = ITOC(inode); + + if ( ! coda_fideq(&cnp->c_fid, oldfid) ) + printk("What? oldfid != cnp->c_fid. Call 911.\n"); + + cnp->c_fid = *newfid; + + list_del(&cnp->c_volrootlist); + if ( !coda_fid_is_weird(newfid) ) + list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); + + return; +} + + /* convert a fid to an inode. Mostly we can compute diff --git a/fs/coda/dir.c b/fs/coda/dir.c index fabc4e3c8..e074ca346 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -96,6 +96,7 @@ struct file_operations coda_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ coda_open, /* open */ + NULL, coda_release, /* release */ coda_fsync, /* fsync */ NULL, diff --git a/fs/coda/file.c b/fs/coda/file.c index 949d9ce00..cbc81542f 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -66,6 +66,7 @@ struct file_operations coda_file_operations = { NULL, /* ioctl */ coda_file_mmap, /* mmap */ coda_open, /* open */ + NULL, coda_release, /* release */ coda_fsync, /* fsync */ NULL, /* fasync */ diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index aa143afcf..4a98361aa 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -65,6 +65,7 @@ struct file_operations coda_ioctl_operations = { coda_pioctl, /* ioctl */ NULL, /* mmap */ coda_ioctl_open, /* open */ + NULL, coda_ioctl_release, /* release */ NULL, /* fsync */ }; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 73fa6ddfe..144162f5a 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -47,13 +47,7 @@ #include <linux/coda_cache.h> #include <linux/coda_proc.h> - -/* - * Where is the prototype? - */ - -int proc_register_dynamic(struct proc_dir_entry * dir, - struct proc_dir_entry * dp); +extern struct proc_dir_entry proc_sys_root; /* * Coda stuff @@ -391,6 +385,7 @@ static struct file_operations coda_psdev_fops = { NULL, /* ioctl */ NULL, /* coda_psdev_mmap */ coda_psdev_open, /* open */ + NULL, coda_psdev_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ @@ -402,7 +397,6 @@ static struct file_operations coda_psdev_fops = { #ifdef CONFIG_PROC_FS -extern struct proc_dir_entry proc_sys_root; struct proc_dir_entry proc_sys_coda = { 0, 4, "coda", @@ -489,6 +483,7 @@ struct proc_dir_entry proc_coda_cache_inv_control = { #endif + __initfunc(int init_coda(void)) { int status; @@ -546,14 +541,12 @@ int init_coda_psdev(void) #ifdef MODULE -EXPORT_NO_SYMBOLS; - MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); int init_module(void) { int status; - printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.6.0, braam@cs.cmu.edu\n"); + printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.7.1, braam@cs.cmu.edu.\n"); status = init_coda_psdev(); if ( status ) { diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 041845e33..dadf27c7e 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -39,8 +39,6 @@ #include <linux/coda_cache.h> #include <linux/coda_proc.h> -extern void coda_purge_dentries(struct inode *inode); -extern void coda_purge_children(struct inode *inode); static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, union inputArgs *buffer); @@ -537,16 +535,12 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, inp->cfs_ioctl.data = (char *)(INSIZE(ioctl)); /* get the data out of user space */ -#ifdef L20 - memcpy_fromfs((char*)inp + (int)inp->cfs_ioctl.data, - data->vi.in, data->vi.in_size); -#else if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data, data->vi.in, data->vi.in_size) ) { error = EINVAL; goto exit; } -#endif + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (error) { @@ -565,18 +559,13 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, error = verify_area(VERIFY_WRITE, data->vi.out, data->vi.out_size); if ( error ) goto exit; -#ifdef L20 - memcpy_tofs(data->vi.out, - (char *)outp + (int)outp->cfs_ioctl.data, - data->vi.out_size); -#else + if (copy_to_user(data->vi.out, (char *)outp + (int)outp->cfs_ioctl.data, data->vi.out_size)) { error = EINVAL; goto exit; } -#endif } exit: @@ -891,18 +880,19 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CFS_REPLACE : { struct inode *inode; - ViceFid *fid = &out->cfs_replace.OldFid; + ViceFid *oldfid = &out->cfs_replace.OldFid; + ViceFid *newfid = &out->cfs_replace.NewFid; clstats(CFS_REPLACE); CDEBUG(D_DOWNCALL, "CFS_REPLACE\n"); - inode = coda_fid_to_inode(fid, sb); + inode = coda_fid_to_inode(oldfid, sb); if ( inode ) { CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino); - coda_purge_children(inode); - coda_purge_dentries(inode); + coda_replace_fid(inode, oldfid, newfid); }else CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); + return 0; - } + } } return 0; } diff --git a/fs/dcache.c b/fs/dcache.c index 4c7ea2e3b..c37da320e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -643,9 +643,10 @@ int d_validate(struct dentry *dentry, struct dentry *dparent, * Special case: local mount points don't live in * the hashes, so we search the super blocks. */ - struct super_block *sb = super_blocks + 0; + struct super_block *sb = sb_entry(super_blocks.next); - for (; sb < super_blocks + NR_SUPER; sb++) { + for (; sb != sb_entry(&super_blocks); + sb = sb_entry(sb->s_list.next)) { if (!sb->s_dev) continue; if (sb->s_root == dentry) @@ -915,7 +916,7 @@ out: return ino; } -__initfunc(void dcache_init(void)) +void __init dcache_init(void) { int i; struct list_head *d = dentry_hashtable; diff --git a/fs/devices.c b/fs/devices.c index f0ab040fc..db5cca48b 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -261,6 +261,7 @@ struct file_operations def_blk_fops = { NULL, /* ioctl */ NULL, /* mmap */ blkdev_open, /* open */ + NULL, /* flush */ NULL, /* release */ }; @@ -313,6 +314,7 @@ struct file_operations def_chr_fops = { NULL, /* ioctl */ NULL, /* mmap */ chrdev_open, /* open */ + NULL, /* flush */ NULL, /* release */ }; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 9ba675b16..78d3ae625 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -77,13 +77,15 @@ static int devpts_parse_options(char *options, struct devpts_sb_info *sbi) { int setuid = 0; int setgid = 0; - uid_t uid = 0; /* To shut up gcc */ + uid_t uid = 0; gid_t gid = 0; umode_t mode = 0600; char *this_char, *value; - if ( !options ) return 1; - for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + this_char = NULL; + if ( options ) + this_char = strtok(options,","); + for ( ; this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) *value++ = 0; if (!strcmp(this_char,"uid")) { diff --git a/fs/devpts/root.c b/fs/devpts/root.c index e5930501c..04215ad40 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -29,6 +29,7 @@ static struct file_operations devpts_root_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/dquot.c b/fs/dquot.c index 9ec40618e..3179a5d4d 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1063,7 +1063,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid } -__initfunc(void dquot_init_hash(void)) +void __init dquot_init_hash(void) { printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__); @@ -67,7 +67,7 @@ asmlinkage int sys_brk(unsigned long); static struct linux_binfmt *formats = (struct linux_binfmt *) NULL; -__initfunc(void binfmt_setup(void)) +void __init binfmt_setup(void) { #ifdef CONFIG_BINFMT_MISC init_misc_binfmt(); @@ -571,6 +571,15 @@ flush_failed: return retval; } +/* + * We mustn't allow tracing of suid binaries, unless + * the tracer has the capability to trace anything.. + */ +static inline int must_not_trace_exec(struct task_struct * p) +{ + return (p->flags & PF_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE); +} + /* * Fill the binprm structure from the inode. * Check permissions, then read the first 512 bytes @@ -663,7 +672,7 @@ int prepare_binprm(struct linux_binprm *bprm) /* 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) */ if (IS_NOSUID(inode) - || (current->flags & PF_PTRACED) + || must_not_trace_exec(current) || (atomic_read(¤t->fs->count) > 1) || (atomic_read(¤t->sig->count) > 1) || (atomic_read(¤t->files->count) > 1)) { diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 40845df97..8f69f0baf 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -213,7 +213,7 @@ static inline int load_block_bitmap (struct super_block * sb, if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && sb->u.ext2_sb.s_block_bitmap_number[0] == block_group && sb->u.ext2_sb.s_block_bitmap[block_group]) { - slot = 0; + return 0; } /* * Or can we do a fast lookup based on a loaded group on a filesystem diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index cbf6a9020..c5af9d8e1 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -43,6 +43,7 @@ static struct file_operations ext2_dir_operations = { ext2_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ ext2_sync_file, /* fsync */ NULL, /* fasync */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 3b36f970f..f4fff8b91 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -74,6 +74,7 @@ static struct file_operations ext2_file_operations = { #else ext2_open_file, #endif + NULL, /* flush */ ext2_release_file, /* release */ ext2_sync_file, /* fsync */ NULL, /* fasync */ diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 7d7a04e70..8b310fc20 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -101,7 +101,8 @@ static int load_inode_bitmap (struct super_block * sb, "block_group = %d, groups_count = %lu", block_group, sb->u.ext2_sb.s_groups_count); if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 && - sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group) + sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group && + sb->u.ext2_sb.s_inode_bitmap[0] != NULL) return 0; if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_inode_bitmap[block_group]) { diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 00ad26330..1c44247a0 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -46,6 +46,7 @@ struct file_operations fat_dir_operations = { fat_dir_ioctl, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync /* fsync */ }; diff --git a/fs/fat/file.c b/fs/fat/file.c index 2495400f4..52e36caea 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -39,6 +39,7 @@ static struct file_operations fat_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; @@ -79,6 +80,7 @@ static struct file_operations fat_file_operations_1024 = { NULL, /* ioctl - default */ fat_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; @@ -122,6 +124,7 @@ static struct file_operations fat_file_operations_readpage = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; diff --git a/fs/fcntl.c b/fs/fcntl.c index a35db83a8..919204088 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -84,7 +84,7 @@ asmlinkage int sys_dup(unsigned int fildes) #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC) -static int setfl(struct file * filp, unsigned long arg) +static int setfl(int fd, struct file * filp, unsigned long arg) { struct inode * inode = filp->f_dentry->d_inode; @@ -98,7 +98,7 @@ static int setfl(struct file * filp, unsigned long arg) /* Did FASYNC state change? */ if ((arg ^ filp->f_flags) & FASYNC) { if (filp->f_op->fasync) - filp->f_op->fasync(filp, (arg & FASYNC) != 0); + filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); } /* required for strict SunOS emulation */ @@ -137,7 +137,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_flags; break; case F_SETFL: - err = setfl(filp, arg); + err = setfl(fd, filp, arg); break; case F_GETLK: err = fcntl_getlk(fd, (struct flock *) arg); @@ -166,6 +166,17 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); break; + case F_GETSIG: + err = filp->f_owner.signum; + break; + case F_SETSIG: + if (arg <= 0 || arg > _NSIG) { + err = -EINVAL; + break; + } + err = 0; + filp->f_owner.signum = arg; + break; default: /* sockets need a few special fcntls. */ if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) @@ -180,10 +191,13 @@ out: return err; } -static void send_sigio(int pid, uid_t uid, uid_t euid) +static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa) { struct task_struct * p; - + int pid = fown->pid; + uid_t uid = fown->uid; + uid_t euid = fown->euid; + read_lock(&tasklist_lock); for_each_task(p) { int match = p->pid; @@ -195,9 +209,27 @@ static void send_sigio(int pid, uid_t uid, uid_t euid) (euid ^ p->suid) && (euid ^ p->uid) && (uid ^ p->suid) && (uid ^ p->uid)) continue; - send_sig(SIGIO, p, 1); - if (p->state == TASK_INTERRUPTIBLE && signal_pending(p)) - wake_up_process(p); + switch (fown->signum) { + siginfo_t si; + default: + /* Queue a rt signal with the appropriate fd as its + value. We use SI_SIGIO as the source, not + SI_KERNEL, since kernel signals always get + delivered even if we can't queue. Failure to + queue in this case _should_ be reported; we fall + back to SIGIO in that case. --sct */ + si.si_signo = fown->signum; + si.si_errno = 0; + si.si_code = SI_SIGIO; + si.si_pid = pid; + si.si_uid = uid; + si.si_fd = fa->fa_fd; + if (!send_sig_info(fown->signum, &si, p)) + break; + /* fall-through: fall back on the old plain SIGIO signal */ + case 0: + send_sig(SIGIO, p, 1); + } } read_unlock(&tasklist_lock); } @@ -213,7 +245,7 @@ void kill_fasync(struct fasync_struct *fa, int sig) } fown = &fa->fa_file->f_owner; if (fown->pid) - send_sigio(fown->pid, fown->uid, fown->euid); + send_sigio(fown, fa); fa = fa->fa_next; } } @@ -128,6 +128,8 @@ static struct file_operations def_fifo_fops = { NULL, fifo_open, /* will set read or write pipe_fops */ NULL, + NULL, + NULL, NULL }; diff --git a/fs/file_table.c b/fs/file_table.c index 3b5f05f81..6a4b1df43 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -12,6 +12,7 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/init.h> /* SLAB cache for filp's. */ static kmem_cache_t *filp_cache; @@ -44,8 +45,7 @@ static inline void put_inuse(struct file *file) file->f_pprev = &inuse_filps; } -/* N.B. This should be an __initfunc ... */ -void file_table_init(void) +void __init file_table_init(void) { filp_cache = kmem_cache_create("filp", sizeof(struct file), 0, diff --git a/fs/filesystems.c b/fs/filesystems.c index 2006c063a..683aa3162 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -24,6 +24,7 @@ #include <linux/ufs_fs.h> #include <linux/romfs_fs.h> #include <linux/auto_fs.h> +#include <linux/qnx4_fs.h> #include <linux/ntfs_fs.h> #include <linux/hfs_fs.h> #include <linux/devpts_fs.h> @@ -47,16 +48,8 @@ extern int init_coda(void); extern int init_devpts_fs(void); #endif -extern void device_setup(void); -extern void binfmt_setup(void); -extern void free_initmem(void); - -__initfunc(static void do_sys_setup(void)) +void __init filesystem_setup(void) { - device_setup(); - - binfmt_setup(); - #ifdef CONFIG_EXT2_FS init_ext2_fs(); #endif @@ -153,35 +146,13 @@ __initfunc(static void do_sys_setup(void)) init_devpts_fs(); #endif +#ifdef CONFIG_QNX4FS_FS + init_qnx4_fs(); +#endif + #ifdef CONFIG_NLS init_nls(); #endif - - mount_root(); -} - -int initmem_freed = 0; - -/* This may be used only twice, enforced by 'static int callable' */ -asmlinkage int sys_setup(int magic) -{ - static int callable = 1; - int err = -1; - - lock_kernel(); - if (magic) { - if (!initmem_freed) { - initmem_freed = 1; - free_initmem (); - err = 0; - } - } else if (callable) { - callable = 0; - do_sys_setup(); - err = 0; - } - unlock_kernel(); - return err; } #ifndef CONFIG_NFSD diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index a7bb7f633..a40854e66 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -66,6 +66,7 @@ static struct file_operations hfs_cap_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap - none */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 553fe8ef9..1e14b3f24 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -66,6 +66,7 @@ static struct file_operations hfs_dbl_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap - none */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index b29bfdc17..b69a6bd70 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -69,6 +69,7 @@ static struct file_operations hfs_nat_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap - none */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/file.c b/fs/hfs/file.c index e12792036..6157afb47 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -41,6 +41,7 @@ static struct file_operations hfs_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index 10f39f751..4fbd1f090 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -55,6 +55,7 @@ static struct file_operations hfs_cap_info_operations = { NULL, /* ioctl - default */ NULL, /* mmap - not yet */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 049381dd0..b12a4606b 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -48,6 +48,7 @@ static struct file_operations hfs_hdr_operations = { NULL, /* ioctl - default */ NULL, /* mmap - XXX: not yet */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync - default */ NULL, /* fasync - default */ diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 166d69cd5..381969d34 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -396,9 +396,7 @@ struct super_block *hfs_read_super(struct super_block *s, void *data, struct hfs_mdb *mdb; struct hfs_cat_key key; kdev_t dev = s->s_dev; -#ifndef CONFIG_MAC_PARTITION hfs_s32 part_size, part_start; -#endif struct inode *root_inode; int part; @@ -415,16 +413,25 @@ struct super_block *hfs_read_super(struct super_block *s, void *data, /* set the device driver to 512-byte blocks */ set_blocksize(dev, HFS_SECTOR_SIZE); - /* look for a partition table and find the correct partition */ -#ifndef CONFIG_MAC_PARTITION +#ifdef CONFIG_MAC_PARTITION + /* check to see if we're in a partition */ + mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); + + /* erk. try parsing the partition table ourselves */ + if (!mdb) { + if (hfs_part_find(s, part, silent, &part_size, &part_start)) { + goto bail2; + } + mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); + } +#else if (hfs_part_find(s, part, silent, &part_size, &part_start)) { goto bail2; } mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); -#else - mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); #endif + if (!mdb) { if (!silent) { printk("VFS: Can't find a HFS filesystem on dev %s.\n", diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index 96ccc688d..a0d77893f 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -159,6 +159,7 @@ static const struct file_operations hpfs_file_ops = NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* fsync */ }; @@ -203,6 +204,7 @@ static const struct file_operations hpfs_dir_ops = NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync, /* fsync */ }; diff --git a/fs/inode.c b/fs/inode.c index 2b8f7a198..d1dd70265 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -8,6 +8,7 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/dcache.h> +#include <linux/init.h> #include <linux/quotaops.h> /* @@ -177,14 +178,13 @@ static inline void sync_list(struct list_head *head) */ void sync_inodes(kdev_t dev) { - struct super_block * sb = super_blocks + 0; - int i; + struct super_block * sb = sb_entry(super_blocks.next); /* * Search the super_blocks array for the device(s) to sync. */ spin_lock(&inode_lock); - for (i = NR_SUPER ; i-- ; sb++) { + for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) { if (!sb->s_dev) continue; if (dev && sb->s_dev != dev) @@ -735,11 +735,11 @@ int bmap(struct inode * inode, int block) /* * Initialize the hash tables and default - * value for max inodes.. + * value for max inodes */ #define MAX_INODE (8192) -void inode_init(void) +void __init inode_init(void) { int i, max; struct list_head *head = inode_hashtable; @@ -771,7 +771,13 @@ int fs_may_remount_ro(struct super_block *sb) inode = file->f_dentry->d_inode; if (!inode || inode->i_sb != sb) continue; - if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE) + + /* File with pending delete? */ + if (inode->i_nlink == 0) + return 0; + + /* Writable file? */ + if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE)) return 0; } return 1; /* Tis' cool bro. */ diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 524624572..f73ec5271 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -35,6 +35,7 @@ static struct file_operations isofs_dir_operations = NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; diff --git a/fs/isofs/file.c b/fs/isofs/file.c index 0f230e0d2..0a508c90b 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -31,6 +31,7 @@ static struct file_operations isofs_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ NULL /* fsync */ }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 62557f29c..06de6ac7c 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -25,6 +25,7 @@ #include <linux/cdrom.h> #include <linux/init.h> #include <linux/nls.h> +#include <linux/ctype.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -41,6 +42,18 @@ static int check_malloc = 0; static int check_bread = 0; #endif +static int isofs_hashi(struct dentry *parent, struct qstr *qstr); +static int isofs_hash(struct dentry *parent, struct qstr *qstr); +static int isofs_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b); + +#ifdef CONFIG_JOLIET +static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr); +static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr); +static int isofs_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); +static int isofs_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b); +#endif + void isofs_put_super(struct super_block *sb) { #ifdef CONFIG_JOLIET @@ -71,31 +84,207 @@ static struct super_operations isofs_sops = { NULL }; +static struct dentry_operations isofs_dentry_ops[] = { + { + NULL, /* d_revalidate */ + isofs_hash, + isofs_cmp, + NULL /* d_delete */ + }, + { + NULL, /* d_revalidate */ + isofs_hashi, + isofs_cmpi, + NULL /* d_delete */ + }, +#ifdef CONFIG_JOLIET + { + NULL, /* d_revalidate */ + isofs_hash_ms, + isofs_cmp_ms, + NULL /* d_delete */ + }, + { + NULL, /* d_revalidate */ + isofs_hashi_ms, + isofs_cmpi_ms, + NULL /* d_delete */ + } +#endif +}; + struct iso9660_options{ - char map; - char rock; - char joliet; - char cruft; - char unhide; - unsigned char check; - unsigned int blocksize; - mode_t mode; - gid_t gid; - uid_t uid; - char *iocharset; - unsigned char utf8; + char map; + char rock; + char joliet; + char cruft; + char unhide; + unsigned char check; + unsigned int blocksize; + mode_t mode; + gid_t gid; + uid_t uid; + char *iocharset; + unsigned char utf8; }; +/* + * Compute the hash for the isofs name corresponding to the dentry. + */ +static int +isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms) +{ + const char *name; + int len; + + len = qstr->len; + name = qstr->name; + if (ms) { + while (len && name[len-1] == '.') + len--; + } + + qstr->hash = full_name_hash(name, len); + + return 0; +} + +/* + * Compute the hash for the isofs name corresponding to the dentry. + */ +static int +isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) +{ + const char *name; + int len; + char c; + unsigned long hash; + + len = qstr->len; + name = qstr->name; + if (ms) { + while (len && name[len-1] == '.') + len--; + } + + hash = init_name_hash(); + while (len--) { + c = tolower(*name++); + hash = partial_name_hash(tolower(c), hash); + } + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case insensitive compare of two isofs names. + */ +static int +isofs_cmpi_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = a->len; + blen = b->len; + if (ms) { + while (alen && a->name[alen-1] == '.') + alen--; + while (blen && b->name[blen-1] == '.') + blen--; + } + if (alen == blen) { + if (strnicmp(a->name, b->name, alen) == 0) + return 0; + } + return 1; +} + +/* + * Case sensitive compare of two isofs names. + */ +static int +isofs_cmp_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = a->len; + blen = b->len; + if (ms) { + while (alen && a->name[alen-1] == '.') + alen--; + while (blen && b->name[blen-1] == '.') + blen--; + } + if (alen == blen) { + if (strncmp(a->name, b->name, alen) == 0) + return 0; + } + return 1; +} + +static int +isofs_hash(struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hash_common(dentry, qstr, 0); +} + +static int +isofs_hashi(struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hashi_common(dentry, qstr, 0); +} + +static int +isofs_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b) +{ + return isofs_cmp_common(dentry, a, b, 0); +} + +static int +isofs_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b) +{ + return isofs_cmpi_common(dentry, a, b, 0); +} + +#ifdef CONFIG_JOLIET +static int +isofs_hash_ms(struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hash_common(dentry, qstr, 1); +} + +static int +isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr) +{ + return isofs_hashi_common(dentry, qstr, 1); +} + +static int +isofs_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +{ + return isofs_cmp_common(dentry, a, b, 1); +} + +static int +isofs_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b) +{ + return isofs_cmpi_common(dentry, a, b, 1); +} +#endif + static int parse_options(char *options, struct iso9660_options * popt) { char *this_char,*value; - popt->map = 'a'; + popt->map = 'n'; popt->rock = 'y'; popt->joliet = 'y'; popt->cruft = 'n'; popt->unhide = 'n'; - popt->check = 's'; /* default: strict */ + popt->check = 'u'; /* unset */ popt->blocksize = 1024; popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could be shared with DOS machines so @@ -274,6 +463,7 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, unsigned int vol_desc_start; struct inode * inode; struct iso9660_options opt; + int table; MOD_INC_USE_COUNT; /* lock before any blocking operations */ @@ -374,7 +564,7 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, } else if (sec->escape[2] == 0x45) { joliet_level = 3; } - printk("ISO 9660 Extensions: Microsoft Joliet Level %d\n", + printk(KERN_DEBUG"ISO 9660 Extensions: Microsoft Joliet Level %d\n", joliet_level); } goto root_found; @@ -532,7 +722,6 @@ root_found: s->s_op = &isofs_sops; s->u.isofs_sb.s_mapping = opt.map; s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 2 : 0); - s->u.isofs_sb.s_name_check = opt.check; s->u.isofs_sb.s_cruft = opt.cruft; s->u.isofs_sb.s_unhide = opt.unhide; s->u.isofs_sb.s_uid = opt.uid; @@ -555,7 +744,10 @@ root_found: * CD with Unicode names. Until someone sees such a beast, it * will not be supported. */ - if (joliet_level && opt.rock == 'y' && s->u.isofs_sb.s_rock != 1) { + if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) { + joliet_level = 0; + } + if (joliet_level) { iput(inode); pri = (struct iso_primary_descriptor *) sec; rootp = (struct iso_directory_record *) @@ -566,11 +758,22 @@ root_found: << s -> u.isofs_sb.s_log_zone_size); inode = iget(s, s->u.isofs_sb.s_firstdatazone); s->u.isofs_sb.s_rock = 0; + opt.rock = 'n'; + } + + if (opt.check == 'u') { + /* Only Joliet is case insensitive by default */ + if (joliet_level) opt.check = 'r'; + else opt.check = 's'; } s->s_root = d_alloc_root(inode, NULL); if (!(s->s_root)) goto out_no_root; + table = 0; + if (joliet_level) table += 2; + if (opt.check == 'r') table++; + s->s_root->d_op = &isofs_dentry_ops[table]; if(!check_disk_change(dev)) { brelse(bh); @@ -694,34 +897,36 @@ int isofs_bmap(struct inode * inode,int block) size = inode->u.isofs_i.i_section_size; nextino = inode->u.isofs_i.i_next_section_ino; #ifdef DEBUG - printk("first inode: inode=%lu nextino=%lu firstext=%u size=%lu\n", + printk("first inode: inode=%x nextino=%x firstext=%u size=%lu\n", inode->i_ino, nextino, firstext, size); #endif i = 0; - while(b_off >= offset + size) { - offset += size; - - if(nextino == 0) return 0; - ino = iget(inode->i_sb, nextino); - if(!ino) return 0; - firstext = ino->u.isofs_i.i_first_extent; - size = ino->u.isofs_i.i_section_size; + if (nextino) { + while(b_off >= offset + size) { + offset += size; + + if(nextino == 0) return 0; + ino = iget(inode->i_sb, nextino); + if(!ino) return 0; + firstext = ino->u.isofs_i.i_first_extent; + size = ino->u.isofs_i.i_section_size; #ifdef DEBUG - printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n", - inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size); + printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n", + inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size); #endif - nextino = ino->u.isofs_i.i_next_section_ino; - iput(ino); + nextino = ino->u.isofs_i.i_next_section_ino; + iput(ino); - if(++i > 100) { - printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n"); - printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n", - inode->i_ino, block, firstext, (unsigned)size, nextino); - return 0; + if(++i > 100) { + printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n"); + printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n", + inode->i_ino, block, firstext, (unsigned)size, nextino); + return 0; + } } } #ifdef DEBUG - printk("isofs_bmap: mapped inode:block %lu:%d to block %lu\n", + printk("isofs_bmap: mapped inode:block %x:%d to block %lu\n", inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode)); #endif return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); @@ -905,7 +1110,7 @@ void isofs_read_inode(struct inode * inode) #endif #ifdef DEBUG - printk("Get inode %d: %d %d: %d\n",inode->i_ino, block, + printk("Get inode %x: %d %d: %d\n",inode->i_ino, block, ((int)pnt) & 0x3ff, inode->i_size); #endif diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index b620e4144..3da5bd04c 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -79,8 +79,6 @@ get_joliet_filename(struct iso_directory_record * de, struct inode * inode, unsigned char utf8; struct nls_table *nls; unsigned char len = 0; - int i; - char c; utf8 = inode->i_sb->u.isofs_sb.s_utf8; nls = inode->i_sb->u.isofs_sb.s_nls_iocharset; @@ -96,14 +94,12 @@ get_joliet_filename(struct iso_directory_record * de, struct inode * inode, len -= 2; } - if (inode->i_sb->u.isofs_sb.s_name_check == 'r') { - for (i = 0; i < len; i++) { - c = outname[i]; - /* lower case */ - if (c >= 'A' && c <= 'Z') c |= 0x20; - if (c == ';') c = '.'; - outname[i] = c; - } + /* + * Windows doesn't like periods at the end of a name, + * so neither do we + */ + while (len >= 2 && (outname[len-1] == '.')) { + len--; } return len; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index a59f15ad8..d59340f10 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -22,33 +22,31 @@ * ok, we cannot use strncmp, as the name is not in our data space. * Thus we'll have to use isofs_match. No big problem. Match also makes * some sanity tests. - * - * NOTE! unlike strncmp, isofs_match returns 1 for success, 0 for failure. */ -static int isofs_match(int len,const char * name, const char * compare, int dlen) +static int +isofs_cmp(struct dentry * dentry, const char * compare, int dlen) { + struct qstr qstr; + if (!compare) - return 0; + return 1; /* check special "." and ".." files */ if (dlen == 1) { /* "." */ if (compare[0] == 0) { - if (!len) - return 1; + if (!dentry->d_name.len) + return 0; compare = "."; } else if (compare[0] == 1) { compare = ".."; dlen = 2; } } -#if 0 - if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen); -#endif - - if (dlen != len) - return 0; - return !memcmp(name, compare, len); + + qstr.name = compare; + qstr.len = dlen; + return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr); } /* @@ -59,8 +57,8 @@ static int isofs_match(int len,const char * name, const char * compare, int dlen * itself (as an inode number). It does NOT read the inode of the * entry - you'll have to do that yourself if you want to. */ -static struct buffer_head * isofs_find_entry(struct inode * dir, - const char * name, int namelen, unsigned long * ino) +static struct buffer_head * +isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); @@ -162,7 +160,8 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, dpnt = de->name; if (dir->i_sb->u.isofs_sb.s_rock || - dir->i_sb->u.isofs_sb.s_joliet_level) { + dir->i_sb->u.isofs_sb.s_joliet_level || + dir->i_sb->u.isofs_sb.s_mapping == 'a') { if (! page) { page = (unsigned char *) __get_free_page(GFP_KERNEL); @@ -195,7 +194,7 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, } /* This allows us to match with and without * a trailing period. */ - if(dpnt[dlen-1] == '.' && namelen == dlen-1) + if(dpnt[dlen-1] == '.' && dentry->d_name.len == dlen-1) dlen--; } /* @@ -205,7 +204,7 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, if( !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) || dir->i_sb->u.isofs_sb.s_unhide == 'y' ) { - match = isofs_match(namelen,name,dpnt,dlen); + match = (isofs_cmp(dentry,dpnt,dlen) == 0); } if (match) { if(inode_number == -1) { @@ -231,11 +230,10 @@ int isofs_lookup(struct inode * dir, struct dentry * dentry) { unsigned long ino; struct buffer_head * bh; - char *lcname; struct inode *inode; #ifdef DEBUG - printk("lookup: %x %d\n",dir->i_ino, dentry->d_name.len); + printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name); #endif if (!dir) return -ENOENT; @@ -243,23 +241,9 @@ int isofs_lookup(struct inode * dir, struct dentry * dentry) if (!S_ISDIR(dir->i_mode)) return -ENOENT; - /* If mounted with check=relaxed (and most likely norock), - * then first convert this name to lower case. - */ - if (dir->i_sb->u.isofs_sb.s_name_check == 'r' && - (lcname = kmalloc(dentry->d_name.len, GFP_KERNEL)) != NULL) { - int i; - char c; + dentry->d_op = dir->i_sb->s_root->d_op; - for (i=0; i<dentry->d_name.len; i++) { - c = dentry->d_name.name[i]; - if (c >= 'A' && c <= 'Z') c |= 0x20; - lcname[i] = c; - } - bh = isofs_find_entry(dir, lcname, dentry->d_name.len, &ino); - kfree(lcname); - } else - bh = isofs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &ino); + bh = isofs_find_entry(dir, dentry, &ino); inode = NULL; if (bh) { diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index eec09e3ad..bbfdefc71 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -51,34 +51,39 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) } /* - * Initialize arguments for GRANTED call + * Initialize arguments for GRANTED call. The nlm_rqst structure + * has been cleared already. */ int nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) { - struct nlm_args *argp = &call->a_args; - struct nlm_lock *alock = &argp->lock; - void *data = NULL; - - if (lock->oh.len > NLMCLNT_OHSIZE - && !(data = kmalloc(lock->oh.len, GFP_KERNEL))) - return 0; + call->a_args.cookie = nlm_cookie++; + call->a_args.lock = *lock; + call->a_args.lock.caller = system_utsname.nodename; + + /* set default data area */ + call->a_args.lock.oh.data = call->a_owner; + + if (lock->oh.len > NLMCLNT_OHSIZE) { + void *data = kmalloc(lock->oh.len, GFP_KERNEL); + if (!data) + return 0; + call->a_args.lock.oh.data = (u8 *) data; + } - argp->cookie = nlm_cookie++; - argp->lock = *lock; - alock->caller = system_utsname.nodename; - if (data) - alock->oh.data = (u8 *) data; - else - alock->oh.data = call->a_owner; - memcpy(alock->oh.data, lock->oh.data, lock->oh.len); + memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len); return 1; } void nlmclnt_freegrantargs(struct nlm_rqst *call) { - kfree(call->a_args.lock.caller); + /* + * Check whether we allocated memory for the owner. + */ + if (call->a_args.lock.oh.data != (u8 *) call->a_owner) { + kfree(call->a_args.lock.oh.data); + } } /* @@ -404,9 +409,15 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) static int nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) { + struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; int status; + /* No monitor, no lock: see nlmclnt_lock(). + * Since this is an UNLOCK, don't try to setup monitoring here. */ + if (!host->h_monitored) + return -ENOLCK; + /* Clean the GRANTED flag now so the lock doesn't get * reclaimed while we're stuck in the unlock call. */ fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 4f94d8845..6bdcef4e8 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -36,7 +36,7 @@ #define NLMDBG_FACILITY NLMDBG_SVC #define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE) -#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP)) +#define ALLOWED_SIGS (sigmask(SIGKILL)) extern struct svc_program nlmsvc_program; struct nlmsvc_binding * nlmsvc_ops = NULL; @@ -80,6 +80,12 @@ lockd(struct svc_rqst *rqstp) current->pgrp = 1; sprintf(current->comm, "lockd"); + /* Process request with signals blocked. */ + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL)); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + /* kick rpciod */ rpciod_up(); @@ -160,19 +166,8 @@ lockd(struct svc_rqst *rqstp) nlmsvc_ops->exp_getclient(&rqstp->rq_addr); } - /* Process request with signals blocked. */ - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ALLOWED_SIGS); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - svc_process(serv, rqstp); - spin_lock_irq(¤t->sigmask_lock); - sigemptyset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - /* Unlock export hash tables */ if (nlmsvc_ops) nlmsvc_ops->exp_unlock(); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 69a9eeb21..ac650b5f3 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -158,10 +158,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, /* Set notifier function for VFS, and init args */ lock->fl.fl_notify = nlmsvc_notify_blocked; - if (!nlmclnt_setgrantargs(&block->b_call, lock)) { - kfree(block); - goto failed; - } + if (!nlmclnt_setgrantargs(&block->b_call, lock)) + goto failed_free; block->b_call.a_args.cookie = cookie; /* see above */ dprintk("lockd: created block %p...\n", block); @@ -182,6 +180,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, return block; +failed_free: + kfree(block); failed: nlm_release_host(host); return NULL; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 4c407817b..dc3e63347 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -31,6 +31,7 @@ static struct file_operations minix_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync /* default fsync */ }; diff --git a/fs/minix/file.c b/fs/minix/file.c index a44a635b0..f6ddda021 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -42,6 +42,7 @@ static struct file_operations minix_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ minix_sync_file /* fsync */ }; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index bec1c55a2..caa2dc261 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -70,6 +70,7 @@ static struct file_operations ncp_dir_operations = ncp_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index d26fee881..50d91a2b2 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -252,6 +252,7 @@ static struct file_operations ncp_file_operations = ncp_ioctl, /* ioctl */ ncp_mmap, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ ncp_fsync, /* fsync */ }; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 66b33161f..7cc5ae160 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -29,11 +29,6 @@ #include <asm/segment.h> /* for fs functions */ -#define NFS_MAX_AGE 10*HZ /* max age for dentry validation */ - -/* needed by smbfs as well ... move to dcache? */ -extern void nfs_renew_times(struct dentry *); - #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ @@ -79,6 +74,7 @@ static struct file_operations nfs_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ nfs_dir_open, /* open - revalidate */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; @@ -375,6 +371,7 @@ nfs_free_dircache(void) nfs_invalidate_dircache_sb(NULL); } +#define NFS_REVALIDATE_INTERVAL (5*HZ) /* * This is called every time the dcache has a lookup hit, * and we should check whether we can really trust that @@ -383,47 +380,80 @@ nfs_free_dircache(void) * NOTE! The hit can be a negative hit too, don't assume * we have an inode! * - * The decision to drop the dentry should probably be - * smarter than this. Right now we believe in directories - * for 10 seconds, and in normal files for five.. + * If the dentry is older than the revalidation interval, + * we do a new lookup and verify that the dentry is still + * correct. */ static int nfs_lookup_revalidate(struct dentry * dentry) { + struct dentry * parent = dentry->d_parent; + struct inode * inode = dentry->d_inode; unsigned long time = jiffies - dentry->d_time; - unsigned long max = 5*HZ; + int error; + struct nfs_fh fhandle; + struct nfs_fattr fattr; - if (dentry->d_inode) { - if (is_bad_inode(dentry->d_inode)) { + if (inode && is_bad_inode(inode)) { #ifdef NFS_PARANOIA printk("nfs_lookup_validate: %s/%s has dud inode\n", -dentry->d_parent->d_name.name, dentry->d_name.name); +parent->d_name.name, dentry->d_name.name); #endif - goto bad; - } - if (S_ISDIR(dentry->d_inode->i_mode)) - max = NFS_MAX_AGE; + goto out_bad; + } + + if (time < NFS_REVALIDATE_INTERVAL) + goto out_valid; + /* + * Don't bother looking up a negative dentry ... + */ + if (!inode) + goto out_bad; + + if (IS_ROOT(dentry)) + goto out_valid; + /* + * Do a new lookup and check the dentry attributes. + */ + error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), + dentry->d_name.name, &fhandle, &fattr); + if (error) { +printk("nfs_lookup_revalidate: error=%d\n", error); + goto out_bad; } - return (time < max) || IS_ROOT(dentry); -bad: + /* Inode number matches? */ + if (fattr.fileid != inode->i_ino) { +printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n", +parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid); + goto out_bad; + } + /* Filehandle matches? */ + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { +printk("nfs_lookup_revalidate: %s/%s fh changed\n", +parent->d_name.name, dentry->d_name.name); + goto out_bad; + } + +out_valid: + return 1; +out_bad: return 0; } /* * This is called from dput() when d_count is going to 0. - * We use it to clean up silly-renamed files, and to check - * for dentries that have already expired. + * We use it to clean up silly-renamed files. */ static void nfs_dentry_delete(struct dentry *dentry) { + dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_flags); + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { int error; dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; -#ifdef NFS_DEBUG_VERBOSE -printk("nfs_dentry_delete: unlinking %s/%s\n", -dentry->d_parent->d_name.name, dentry->d_name.name); -#endif /* Unhash it first */ d_drop(dentry); error = nfs_safe_remove(dentry); @@ -432,14 +462,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name, error); } - /* - * Check whether to expire the dentry ... - */ - else { - unsigned long age = jiffies - dentry->d_time; - if (age > NFS_MAX_AGE) - d_drop(dentry); - } #ifdef NFS_PARANOIA /* @@ -464,6 +486,10 @@ inode->i_ino, inode->i_count, inode->i_nlink); */ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { + dfprintk(VFS, "NFS: dentry_iput(%s/%s, cnt=%d, ino=%ld)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count, inode->i_ino); + if (NFS_WRITEBACK(inode)) { #ifdef NFS_PARANOIA printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n", @@ -522,17 +548,12 @@ static void show_dentry(struct list_head * dlist) #endif /* - * Whenever a lookup succeeds, we know the parent directories - * are all valid, so we want to update the dentry timestamps. + * Whenever an NFS operation succeeds, we know that the dentry + * is valid, so we update the revalidation timestamp. */ -void nfs_renew_times(struct dentry * dentry) +static inline void nfs_renew_times(struct dentry * dentry) { - for (;;) { - dentry->d_time = jiffies; - if (dentry == dentry->d_parent) - break; - dentry = dentry->d_parent; - } + dentry->d_time = jiffies; } static int nfs_lookup(struct inode *dir, struct dentry * dentry) @@ -545,11 +566,6 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry) dfprintk(VFS, "NFS: lookup(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_lookup: inode is NULL or not a directory\n"); - return -ENOENT; - } - error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) goto out; @@ -634,12 +650,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) struct nfs_fh fhandle; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, dentry->d_name.name); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_create: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name); error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) @@ -674,12 +685,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde struct nfs_fh fhandle; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, dentry->d_name.name); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_mknod: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name); if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; @@ -711,12 +717,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) struct nfs_fh fhandle; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, dentry->d_name.name); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_mkdir: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name); error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) @@ -753,12 +754,7 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) int error, rehash = 0; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, dentry->d_name.name); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_rmdir: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name); error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) @@ -867,6 +863,10 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry) struct dentry *sdentry; int error = -EIO; + dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count); + /* * Note that a silly-renamed file can be deleted once it's * no longer in use -- it's just an ordinary file now. @@ -939,6 +939,10 @@ static int nfs_safe_remove(struct dentry *dentry) struct inode *inode = dentry->d_inode; int error, rehash = 0; + dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + inode->i_ino); + /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; if (inode) { @@ -1009,12 +1013,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) int error; dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, dentry->d_name.name); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_unlink: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name); error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) @@ -1038,12 +1037,7 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) int error; dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", - dir->i_dev, dir->i_ino, dentry->d_name.name, symname); - - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_symlink: inode is NULL or not a directory\n"); - return -ENOENT; - } + dir->i_dev, dir->i_ino, dentry->d_name.name, symname); error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) @@ -1095,11 +1089,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) old_dentry->d_parent->d_name.name, old_dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name); - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("nfs_link: dir is NULL or not a directory\n"); - return -ENOENT; - } - error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) goto out; @@ -1153,22 +1142,13 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; + struct dentry *dentry = NULL; int error, rehash = 0, update = 1; -#ifdef NFS_DEBUG_VERBOSE -printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n", -old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count, -new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); -#endif - if (!old_dir || !S_ISDIR(old_dir->i_mode)) { - printk("nfs_rename: old inode is NULL or not a directory\n"); - return -ENOENT; - } - - if (!new_dir || !S_ISDIR(new_dir->i_mode)) { - printk("nfs_rename: new inode is NULL or not a directory\n"); - return -ENOENT; - } + dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", + old_dentry->d_parent->d_name.name, old_dentry->d_name.name, + new_dentry->d_parent->d_name.name, new_dentry->d_name.name, + new_dentry->d_count); error = -ENAMETOOLONG; if (old_dentry->d_name.len > NFS_MAXNAMLEN || @@ -1178,16 +1158,43 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); /* * First check whether the target is busy ... we can't * safely do _any_ rename if the target is in use. + * + * For files, make a copy of the dentry and then do a + * silly-rename. If the silly-rename succeeds, the + * copied dentry is hashed and becomes the new target. + * + * For directories, prune any unused children. */ - if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs)) - shrink_dcache_parent(new_dentry); error = -EBUSY; - if (new_dentry->d_count > 1) { + if (new_dentry->d_count > 1 && new_inode) { + if (S_ISREG(new_inode->i_mode)) { + int err; + /* copy the target dentry's name */ + dentry = d_alloc(new_dentry->d_parent, + &new_dentry->d_name); + if (!dentry) + goto out; + + /* silly-rename the existing target ... */ + err = nfs_sillyrename(new_dir, new_dentry); + if (!err) { + new_dentry = dentry; + new_inode = NULL; + /* hash the replacement target */ + d_add(new_dentry, NULL); + } + } else if (!list_empty(&new_dentry->d_subdirs)) { + shrink_dcache_parent(new_dentry); + } + + /* dentry still busy? */ + if (new_dentry->d_count > 1) { #ifdef NFS_PARANOIA printk("nfs_rename: target %s/%s busy, d_count=%d\n", new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); #endif - goto out; + goto out; + } } /* @@ -1231,7 +1238,7 @@ old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count); #endif goto out; } - if (new_dentry->d_count > 1) { + if (new_dentry->d_count > 1 && new_inode) { #ifdef NFS_PARANOIA printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n", new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); @@ -1274,7 +1281,11 @@ new_inode->i_count, new_inode->i_nlink); if (update) d_move(old_dentry, new_dentry); } + out: + /* new dentry created? */ + if (dentry) + dput(dentry); return error; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 9ec883aff..973ad52ec 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -35,6 +35,7 @@ static int nfs_file_mmap(struct file *, struct vm_area_struct *); static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *); static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *); +static int nfs_file_flush(struct file *); static int nfs_file_close(struct inode *, struct file *); static int nfs_fsync(struct file *, struct dentry *dentry); @@ -47,6 +48,7 @@ static struct file_operations nfs_file_operations = { NULL, /* ioctl - default */ nfs_file_mmap, /* mmap */ NULL, /* no special open is needed */ + nfs_file_flush, /* flush */ nfs_file_close, /* release */ nfs_fsync, /* fsync */ NULL, /* fasync */ @@ -84,13 +86,7 @@ struct inode_operations nfs_file_inode_operations = { #endif /* - * Flush all dirty pages, and check for write errors. - * - * Note that since the file close operation is called only by the - * _last_ process to close the file, we need to flush _all_ dirty - * pages. This also means that there is little sense in checking - * for errors for this specific process -- we should probably just - * clear all errors. + * Sync the file.. */ static int nfs_file_close(struct inode *inode, struct file *file) @@ -106,6 +102,22 @@ nfs_file_close(struct inode *inode, struct file *file) return status; } +/* + * Flush all dirty pages, and check for write errors. + * + * We should probably do this better - this does get called at every + * close, so we should probably just flush the changes that "this" + * file has done and report on only those. + * + * Right now we use the "flush everything" approach. Overkill, but + * works. + */ +static int +nfs_file_flush(struct file *file) +{ + return nfs_file_close(file->f_dentry->d_inode, file); +} + static ssize_t nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos) { diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e89abdbce..597821270 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -652,6 +652,7 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) int status = 0; struct nfs_fattr fattr; + /* Don't bother revalidating if we've done it recently */ if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) goto out; @@ -746,6 +747,20 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) goto out_changed; /* + * If we have pending write-back entries, we don't want + * to look at the size the server sends us too closely.. + * In particular, ignore the server if it tells us that + * the file is smaller or older than we locally think it + * is.. + */ + if (NFS_WRITEBACK(inode)) { + if (inode->i_size > fattr->size) + fattr->size = inode->i_size; + if (inode->i_mtime > fattr->mtime.seconds) + fattr->mtime.seconds = inode->i_mtime; + } + + /* * If the size or mtime changed from outside, we want * to invalidate the local caches immediately. */ diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 81da8f996..3ecb9bfa6 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -78,6 +78,7 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr) clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clnt->cl_oneshot = 1; + clnt->cl_intr = 1; } return clnt; } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index c317f330b..f8658f37f 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -56,21 +56,12 @@ #include <linux/nfs_fs.h> #include <asm/uaccess.h> -/* - * NOTE! We must NOT default to soft-mounting: that breaks too many - * programs that depend on POSIX behaviour of uninterruptible reads - * and writes. - * - * Until we have a per-mount soft/hard mount policy that we can honour - * we must default to hard mounting! - */ -#define IS_SOFT 0 - #define NFS_PARANOIA 1 #define NFSDBG_FACILITY NFSDBG_PAGECACHE static void nfs_wback_lock(struct rpc_task *task); static void nfs_wback_result(struct rpc_task *task); +static void nfs_cancel_request(struct nfs_wreq *req); /* * Cache parameters @@ -258,7 +249,7 @@ nfs_find_dentry_request(struct inode *inode, struct dentry *dentry) req = head = NFS_WRITEBACK(inode); while (req != NULL) { - if (req->wb_dentry == dentry) { + if (req->wb_dentry == dentry && !WB_CANCELLED(req)) { found = 1; break; } @@ -442,11 +433,15 @@ schedule_write_request(struct nfs_wreq *req, int sync) sync = 1; if (sync) { + sigset_t oldmask; + struct rpc_clnt *clnt = NFS_CLIENT(inode); dprintk("NFS: %4d schedule_write_request (sync)\n", task->tk_pid); /* Page is already locked */ req->wb_flags |= NFS_WRITE_LOCKED; + rpc_clnt_sigmask(clnt, &oldmask); rpc_execute(task); + rpc_clnt_sigunmask(clnt, &oldmask); } else { dprintk("NFS: %4d schedule_write_request (async)\n", task->tk_pid); @@ -467,17 +462,20 @@ wait_on_write_request(struct nfs_wreq *req) { struct wait_queue wait = { current, NULL }; struct page *page = req->wb_page; - int retval; + int retval; + sigset_t oldmask; + struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); + rpc_clnt_sigmask(clnt, &oldmask); add_wait_queue(&page->wait, &wait); atomic_inc(&page->count); for (;;) { - current->state = IS_SOFT ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; + current->state = TASK_INTERRUPTIBLE; retval = 0; if (!PageLocked(page)) break; retval = -ERESTARTSYS; - if (IS_SOFT && signalled()) + if (signalled()) break; schedule(); } @@ -485,6 +483,7 @@ wait_on_write_request(struct nfs_wreq *req) current->state = TASK_RUNNING; /* N.B. page may have been unused, so we must use free_page() */ free_page(page_address(page)); + rpc_clnt_sigunmask(clnt, &oldmask); return retval; } @@ -534,6 +533,10 @@ nfs_updatepage(struct file *file, struct page *page, const char *buffer, if ((req = find_write_request(inode, page)) != NULL) { if (update_write_request(req, offset, count)) { /* N.B. check for a fault here and cancel the req */ + /* + * SECURITY - copy_from_user must zero the + * rest of the data after a fault! + */ copy_from_user(page_addr + offset, buffer, count); goto updated; } @@ -583,8 +586,11 @@ done: transfer_page_lock(req); /* rpc_execute(&req->wb_task); */ if (sync) { - /* N.B. if signalled, result not ready? */ - wait_on_write_request(req); + /* if signalled, ensure request is cancelled */ + if ((count = wait_on_write_request(req)) != 0) { + nfs_cancel_request(req); + status = count; + } if ((count = nfs_write_error(inode)) < 0) status = count; } @@ -716,29 +722,21 @@ int nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len) { struct nfs_wreq *last = NULL; - int result = 0, cancel = 0; + int result = 0; dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n", inode->i_dev, inode->i_ino, current->pid, offset, len); - if (IS_SOFT && signalled()) { - nfs_cancel_dirty(inode, pid); - cancel = 1; - } - for (;;) { - if (IS_SOFT && signalled()) { - if (!cancel) - nfs_cancel_dirty(inode, pid); - result = -ERESTARTSYS; - break; - } - /* Flush all pending writes for the pid and file region */ last = nfs_flush_pages(inode, pid, offset, len, 0); if (last == NULL) break; - wait_on_write_request(last); + result = wait_on_write_request(last); + if (result) { + nfs_cancel_dirty(inode,pid); + break; + } } return result; @@ -889,6 +887,9 @@ nfs_wback_result(struct rpc_task *task) /* Update attributes as result of writeback. * Beware: when UDP replies arrive out of order, we * may end up overwriting a previous, bigger file size. + * + * When the file size shrinks we cancel all pending + * writebacks. */ if (fattr->mtime.seconds >= inode->i_mtime) { if (fattr->size < inode->i_size) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 0d214eecb..e30953bb2 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -392,14 +392,15 @@ out: int exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) { - struct svc_export *exp = NULL; - struct svc_fh fh; + struct svc_export *exp; struct dentry *dentry; struct inode *inode; + struct svc_fh fh; dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); - if (!(exp = exp_get(clp, dev, ino))) + exp = exp_get(clp, dev, ino); + if (!exp) return -EPERM; dentry = exp->ex_dentry; @@ -414,8 +415,11 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) dev, ino, inode->i_dev, inode->i_ino); } - dget(dentry); - fh_compose(&fh, exp, dentry); + /* + * fh must be initialized before calling fh_compose + */ + fh_init(&fh); + fh_compose(&fh, exp, dget(dentry)); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index ccca2cc7c..e23f48e5b 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -22,12 +22,13 @@ static u32 nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp) { - struct svc_fh fh; u32 nfserr; + struct svc_fh fh; + /* must initialize before using! */ + fh_init(&fh); fh.fh_handle = *f; fh.fh_export = NULL; - fh.fh_dverified = 0; nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp); if (!nfserr) diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ce89d60bf..b19fd711c 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -1107,7 +1107,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n", exp->ex_dev, exp->ex_ino, - dentry->d_parent->d_name.name, dentry->d_name.name, + parent->d_name.name, dentry->d_name.name, (inode ? inode->i_ino : 0)); /* @@ -1115,7 +1115,12 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) * may not be done on error paths, but the cleanup must call fh_put. * Fix this soon! */ + if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) { + printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n", + parent->d_name.name, dentry->d_name.name); + } fh_init(fhp); + fhp->fh_handle.fh_dcookie = dentry; if (inode) { fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 0164b6a7e..d8da2f463 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -36,8 +36,8 @@ #define NFSDDBG_FACILITY NFSDDBG_SVC #define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) -#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP)) -#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGTERM)) +#define ALLOWED_SIGS (sigmask(SIGKILL)) +#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT)) extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index bf5b8505e..bb489912b 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -385,6 +385,7 @@ struct file_operations ntfs_file_operations_nommap = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ @@ -506,6 +507,7 @@ struct file_operations ntfs_file_operations = { NULL, /* ioctl */ generic_file_mmap, NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ @@ -546,6 +548,7 @@ struct file_operations ntfs_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ @@ -770,19 +770,17 @@ asmlinkage int sys_creat(const char * pathname, int mode) /* * Called when retiring the last use of a file pointer. */ -int __fput(struct file *filp) +void __fput(struct file *filp) { struct dentry * dentry = filp->f_dentry; struct inode * inode = dentry->d_inode; - int error = 0; if (filp->f_op && filp->f_op->release) - error = filp->f_op->release(inode, filp); + filp->f_op->release(inode, filp); filp->f_dentry = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); dput(dentry); - return error; } /* @@ -791,6 +789,7 @@ int __fput(struct file *filp) */ int close_fp(struct file *filp, fl_owner_t id) { + int retval; struct dentry *dentry = filp->f_dentry; if (filp->f_count == 0) { @@ -799,7 +798,11 @@ int close_fp(struct file *filp, fl_owner_t id) } if (dentry->d_inode) locks_remove_posix(filp, id); - return fput(filp); + retval = 0; + if (filp->f_op && filp->f_op->flush) + retval = filp->f_op->flush(filp); + fput(filp); + return retval; } /* @@ -304,6 +304,7 @@ struct file_operations connecting_fifo_fops = { pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ pipe_read_open, + NULL, /* flush */ pipe_read_release, NULL }; @@ -317,6 +318,7 @@ struct file_operations read_fifo_fops = { pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ pipe_read_open, + NULL, /* flush */ pipe_read_release, NULL }; @@ -330,6 +332,7 @@ struct file_operations write_fifo_fops = { pipe_ioctl, NULL, /* mmap */ pipe_write_open, + NULL, /* flush */ pipe_write_release, NULL }; @@ -343,6 +346,7 @@ struct file_operations rdwr_fifo_fops = { pipe_ioctl, NULL, /* mmap */ pipe_rdwr_open, + NULL, /* flush */ pipe_rdwr_release, NULL }; @@ -356,6 +360,7 @@ struct file_operations read_pipe_fops = { pipe_ioctl, NULL, /* no mmap on pipes.. surprise */ pipe_read_open, + NULL, /* flush */ pipe_read_release, NULL }; @@ -369,6 +374,7 @@ struct file_operations write_pipe_fops = { pipe_ioctl, NULL, /* mmap */ pipe_write_open, + NULL, /* flush */ pipe_write_release, NULL }; @@ -382,6 +388,7 @@ struct file_operations rdwr_pipe_fops = { pipe_ioctl, NULL, /* mmap */ pipe_rdwr_open, + NULL, /* flush */ pipe_rdwr_release, NULL }; diff --git a/fs/proc/array.c b/fs/proc/array.c index cde538846..eeb933628 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -485,16 +485,18 @@ static unsigned long get_wchan(struct task_struct *p) return 0; #if defined(__i386__) { - unsigned long ebp, eip; + unsigned long ebp, esp, eip; unsigned long stack_page; int count = 0; - stack_page = 4096 + (unsigned long)p; - if (!stack_page) + stack_page = (unsigned long)p; + esp = p->tss.esp; + if (!stack_page || esp < stack_page || esp >= 8188+stack_page) return 0; - ebp = p->tss.ebp; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + ebp = *(unsigned long *) esp; do { - if (ebp < stack_page || ebp >= 4092+stack_page) + if (ebp < stack_page || ebp >= 8188+stack_page) return 0; eip = *(unsigned long *) (ebp+4); if (eip < first_sched || eip >= last_sched) @@ -543,14 +545,12 @@ static unsigned long get_wchan(struct task_struct *p) unsigned long fp, pc; unsigned long stack_page; int count = 0; - extern int sys_pause (void); - stack_page = p->kernel_stack_page; - if (!stack_page) - return 0; + stack_page = (unsigned long)p; fp = ((struct switch_stack *)p->tss.ksp)->a6; do { - if (fp < stack_page || fp >= 4088+stack_page) + if (fp < stack_page+sizeof(struct task_struct) || + fp >= 8184+stack_page) return 0; pc = ((unsigned long *)fp)[1]; /* FIXME: This depends on the order of these functions. */ @@ -1414,6 +1414,7 @@ static struct file_operations proc_array_operations = { NULL, /* array_ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; @@ -1461,6 +1462,7 @@ static struct file_operations proc_arraylong_operations = { NULL, /* array_ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/base.c b/fs/proc/base.c index edfe8b758..c9b2d8649 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -24,6 +24,7 @@ static struct file_operations proc_base_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 57f99bfd7..5409e2d67 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -30,6 +30,7 @@ static struct file_operations proc_fd_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index a9b00f1f5..54b16f84b 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -42,6 +42,7 @@ static struct file_operations proc_file_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index e21c0cd1c..ff8ec4877 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -54,6 +54,7 @@ static struct file_operations proc_kmsg_operations = { NULL, /* kmsg_ioctl */ NULL, /* mmap */ kmsg_open, + NULL, /* flush */ kmsg_release, NULL /* can't fsync */ }; diff --git a/fs/proc/link.c b/fs/proc/link.c index 05ab85030..df5ea85e6 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -31,6 +31,7 @@ static struct file_operations proc_fd_link_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* very special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 6478dab77..8a8ec9bc0 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -316,6 +316,7 @@ static struct file_operations proc_mem_operations = { NULL, /* mem_ioctl */ mem_mmap, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/net.c b/fs/proc/net.c index 408c8e0d4..a6d8c5616 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -92,6 +92,7 @@ static struct file_operations proc_net_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index 041e493d2..9bde82e82 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -268,6 +268,7 @@ static struct file_operations omirr_operations = { NULL, /* omirr_ioctl */ NULL, /* mmap */ omirr_open, + NULL, /* flush */ omirr_release, NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index 0380e1899..b3871059e 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -479,6 +479,7 @@ static struct file_operations openpromfs_prop_ops = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ property_release, /* no special release code */ NULL /* can't fsync */ }; @@ -512,6 +513,7 @@ static struct file_operations openpromfs_nodenum_ops = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; @@ -545,6 +547,7 @@ static struct file_operations openprom_alias_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/proc/root.c b/fs/proc/root.c index 999783ee5..e2889d123 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -50,6 +50,7 @@ static struct file_operations proc_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; @@ -117,6 +118,7 @@ static struct file_operations proc_root_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* no fsync */ }; @@ -276,6 +278,7 @@ static struct file_operations proc_openprom_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; @@ -911,20 +914,19 @@ int proc_readdir(struct file * filp, * tasklist lock while doing this, and we must release it before * we actually do the filldir itself, so we use a temp buffer.. */ -static int get_pid_list(unsigned int index, unsigned int *pids) +static int get_pid_list(int index, unsigned int *pids) { struct task_struct *p; - int nr = FIRST_PROCESS_ENTRY; int nr_pids = 0; + index -= FIRST_PROCESS_ENTRY; read_lock(&tasklist_lock); for_each_task(p) { - int pid; - if (nr++ < index) - continue; - pid = p->pid; + int pid = p->pid; if (!pid) continue; + if (--index >= 0) + continue; pids[nr_pids] = pid; nr_pids++; if (nr_pids >= PROC_MAXPIDS) diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index 3bc4b1d59..6f3ad0770 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -50,6 +50,7 @@ static struct file_operations proc_scsi_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* can't fsync */ }; diff --git a/fs/qnx4/.cvsignore b/fs/qnx4/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/qnx4/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/qnx4/BUGS b/fs/qnx4/BUGS new file mode 100644 index 000000000..64d81b4bb --- /dev/null +++ b/fs/qnx4/BUGS @@ -0,0 +1,21 @@ +Last update: 03-07-1998 + +- Files in a subdir can't be accessed, I think that the inode information + is not correctly copied at some point. Solved 06-06-1998, Richard. + +- At some point the mounted device can't be unmounted. I think that somewhere + in the code a buffer is not given free. + +- Make the '..' entry work, I give it a great chance that the above bug + (not given free) has something to do with this one, after a 'ls -l' + the mounted device can't be unmounted and that's where the '..' entry + is accessed. + Seems to be solved 21-06-1998, Frank. + +- File read function not correct, after the first block it goes beserk. + Solved 21-06-1998, Frank. + +- This fs will not work if not built as a module. + Solved 25-06-1998, Frank. + +- Write/truncate/delete functions don't update the bitmap. diff --git a/fs/qnx4/Makefile b/fs/qnx4/Makefile new file mode 100644 index 000000000..1988bb8ff --- /dev/null +++ b/fs/qnx4/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the linux qnx4-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 .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := qnx4.o +O_OBJS := inode.o dir.o namei.o file.o bitmap.o symlinks.o truncate.o \ +fsync.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/qnx4/README b/fs/qnx4/README new file mode 100644 index 000000000..e7300730b --- /dev/null +++ b/fs/qnx4/README @@ -0,0 +1,9 @@ + + This is a snapshot of the QNX4 filesystem for Linux. + Please send diffs and remarks to <qnxfs@rtc-one.net> . + +Credits : + +Richard "Scuba" A. Frowijn <scuba@wxs.nl> +Frank "Jedi/Sector One" Denis <j@4u.net> + diff --git a/fs/qnx4/TODO b/fs/qnx4/TODO new file mode 100644 index 000000000..fab3623f8 --- /dev/null +++ b/fs/qnx4/TODO @@ -0,0 +1,31 @@ +Name : QNX4 TODO list +Last update: 29-06-1998 + + - qnx4_checkroot (inode.c), currently there's a look for the '/' in + the root direntry, if so then the current mounted device is a qnx4 + partition. This has to be rewritten with a look for 'QNX4' in the + bootblock, it seems to me the savest way to ensure that the mounted + device is in fact a QNX4 partition. + Done 20-06-1998, Frank. But some disks (like QNX install floppies) + don't have 'QNX4' in their bootblock. + + - Bitmap functions. To find out the free space, largest free block, etc. + Partly done (RO), Richard, 05/06/1998. Optimized 20-06-1998, Frank. + + - Symbolic links. symlinks.c have to be rewritten. + + - Extended files. + + - Complete write, unlink and truncate functions : the bitmap should be +updated. + + - Porting to linux 2.1.99+ with dcache support. 20-06-1998, Frank. + + - Don't rewrite the file_read function : use the generic_file_read hook, + and write readpage instead. Done on 21-06-1998, Frank. + + - Write dinit and dcheck. + + - Solving the bugs. + + - Use le32_to_cpu and vice-versa for portability. diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c new file mode 100644 index 000000000..b73f36deb --- /dev/null +++ b/fs/qnx4/bitmap.c @@ -0,0 +1,196 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 28-05-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : basic optimisations. + * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap . + * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/bitops.h> + +int qnx4_new_block(struct super_block *sb) +{ + return 0; +} + +void count_bits(const register char *bmPart, register int size, + int *const tf) +{ + char b; + int tot = *tf; + + if (size > QNX4_BLOCK_SIZE) { + size = QNX4_BLOCK_SIZE; + } + do { + b = *bmPart++; + if ((b & 1) == 0) + tot++; + if ((b & 2) == 0) + tot++; + if ((b & 4) == 0) + tot++; + if ((b & 8) == 0) + tot++; + if ((b & 16) == 0) + tot++; + if ((b & 32) == 0) + tot++; + if ((b & 64) == 0) + tot++; + if ((b & 128) == 0) + tot++; + size--; + } while (size != 0); + *tf = tot; +} + +unsigned long qnx4_count_free_blocks(struct super_block *sb) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int total = 0; + int total_free = 0; + int offset = 0; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + + while (total < size) { + if ((bh = bread(sb->s_dev, start + offset, QNX4_BLOCK_SIZE)) == NULL) { + printk("qnx4: I/O error in counting free blocks\n"); + break; + } + count_bits(bh->b_data, size - total, &total_free); + brelse(bh); + total += QNX4_BLOCK_SIZE; + } + + return total_free; +} + +unsigned long qnx4_count_free_inodes(struct super_block *sb) +{ + return qnx4_count_free_blocks(sb) * QNX4_INODES_PER_BLOCK; /* FIXME */ +} + +int qnx4_is_free(struct super_block *sb, int block) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + const char *g; + int ret = -EIO; + + start += block / (QNX4_BLOCK_SIZE * 8); + QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n", + (unsigned long) block, (unsigned long) start)); + (void) size; /* CHECKME */ + bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE); + if (bh == NULL) { + return -EIO; + } + g = bh->b_data + (block % QNX4_BLOCK_SIZE); + if (((*g) & (1 << (block % 8))) == 0) { + QNX4DEBUG(("qnx4: is_free -> block is free\n")); + ret = 1; + } else { + QNX4DEBUG(("qnx4: is_free -> block is busy\n")); + ret = 0; + } + brelse(bh); + + return ret; +} + +int qnx4_bmap(struct inode *inode, int block) +{ + QNX4DEBUG(("qnx4: bmap on block [%d]\n", block)); + if (block < 0) { + return 0; + } + return !qnx4_is_free(inode->i_sb, block); +} + +#ifdef CONFIG_QNX4FS_RW + +int qnx4_set_bitmap(struct super_block *sb, int block, int busy) +{ + int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int size = sb->u.qnx4_sb.BitMap->di_size; + struct buffer_head *bh; + char *g; + + start += block / (QNX4_BLOCK_SIZE * 8); + QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n", + (unsigned long) block, (unsigned long) start)); + (void) size; /* CHECKME */ + bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE); + if (bh == NULL) { + return -EIO; + } + g = bh->b_data + (block % QNX4_BLOCK_SIZE); + if (busy == 0) { + (*g) &= ~(1 << (block % 8)); + } else { + (*g) |= (1 << (block % 8)); + } + mark_buffer_dirty(bh, 1); + brelse(bh); + + return 0; +} + +static void qnx4_clear_inode(struct inode *inode) +{ + struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i; + + memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved); + qnx4_ino->i_size = 0; + qnx4_ino->i_num_xtnts = 0; + qnx4_ino->i_mode = 0; + qnx4_ino->i_status = 0; +} + +void qnx4_free_inode(struct inode *inode) +{ + if (!inode) { + return; + } + if (!inode->i_dev) { + printk("free_inode: inode has no device\n"); + return; + } + if (inode->i_count > 1) { + printk("free_inode: inode has count=%d\n", inode->i_count); + return; + } + if (inode->i_nlink) { + printk("free_inode: inode has nlink=%d\n", inode->i_nlink); + return; + } + if (!inode->i_sb) { + printk("free_inode: inode on nonexistent device\n"); + return; + } + if (inode->i_ino < 1) { + printk("free_inode: inode 0 or nonexistent inode\n"); + return; + } + qnx4_clear_inode(inode); + clear_inode(inode); +} + +#endif diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c new file mode 100644 index 000000000..119930667 --- /dev/null +++ b/fs/qnx4/dir.c @@ -0,0 +1,126 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 28-05-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> + +#include <asm/segment.h> + +static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir); + +static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned int offset; + struct buffer_head *bh; + struct qnx4_inode_entry *de; + long blknum; + int i; + int size; + + blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 + + ((filp->f_pos >> 6) >> 3); + + if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) { + return -EBADF; + } + QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); + QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos)); + QNX4DEBUG(("BlkNum = %ld\n", (long) blknum)); + + while (filp->f_pos < inode->i_size) { + bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE); + i = (filp->f_pos - (((filp->f_pos >> 6) >> 3) << 9)) & 0x3f; + while (i < QNX4_INODES_PER_BLOCK) { + offset = i * QNX4_DIR_ENTRY_SIZE; + de = (struct qnx4_inode_entry *) (bh->b_data + offset); + size = strlen(de->di_fname); + if (size) { + + QNX4DEBUG(("qnx4_readdir:%s\n", de->di_fname)); + + if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) { + if (de->di_status) { + if (filldir(dirent, de->di_fname, size, filp->f_pos, de->di_first_xtnt.xtnt_blk) < 0) { + brelse(bh); + return 0; + } + } + } + } + i++; + filp->f_pos += QNX4_DIR_ENTRY_SIZE; + } + brelse(bh); + blknum++; + } + UPDATE_ATIME(inode); + + return 0; +} + +static struct file_operations qnx4_dir_operations = +{ + NULL, /* lseek - default */ + NULL, /* read */ + NULL, /* write - bad */ + qnx4_readdir, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special flush code */ + NULL, /* no special release code */ + file_fsync, /* default fsync */ + NULL, /* default fasync */ + NULL, /* default check_media_change */ + NULL, /* default revalidate */ +}; + +struct inode_operations qnx4_dir_inode_operations = +{ + &qnx4_dir_operations, +#ifdef CONFIG_QNX4FS_RW + qnx4_create, +#else + NULL, /* create */ +#endif + qnx4_lookup, + NULL, /* link */ +#ifdef CONFIG_QNX4FS_RW + qnx4_unlink, /* unlink */ +#else + NULL, +#endif + NULL, /* symlink */ + NULL, /* mkdir */ +#ifdef CONFIG_QNX4FS_RW + qnx4_rmdir, /* rmdir */ +#else + NULL, +#endif + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c new file mode 100644 index 000000000..f26733d9a --- /dev/null +++ b/fs/qnx4/file.c @@ -0,0 +1,265 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 25-05-1998 by Richard Frowijn : first release. + * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read. + * 27-06-1998 by Frank Denis : file overwriting. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/qnx4_fs.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/fcntl.h> +#include <linux/stat.h> +#include <linux/locks.h> +#include <linux/mm.h> +#include <linux/pagemap.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#include <linux/fs.h> +#include <linux/qnx4_fs.h> + +static int qnx4_readpage(struct file *file, struct page *page); + +#ifdef CONFIG_QNX4FS_RW +static ssize_t qnx4_file_write(struct file *filp, const char *buf, + size_t count, loff_t * ppos) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + struct qnx4_inode_info *qnx4_ino; + struct buffer_head *bh; + ssize_t result = -EBUSY, c; + off_t pos; + unsigned long start, block, extent_end; + char *p; + + QNX4DEBUG(("qnx4: file_write(%s/%s (%d), %lu@%lu)\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + inode->i_count, (unsigned long) count, (unsigned long) *ppos)); + if (inode == NULL) { + printk("qnx4: NULL inode for file_write\n"); + return -EINVAL; + } + qnx4_ino = &inode->u.qnx4_i; + if (S_ISREG(inode->i_mode) == 0) { + printk("qnx4: write to non-file, mode %07o\n", inode->i_mode); + return -EINVAL; + } + if (count == 0) { + goto out; + } + if (filp->f_flags & O_APPEND) { + pos = inode->i_size; + } else { + pos = *ppos; + } + start = qnx4_ino->i_first_xtnt.xtnt_blk + ((pos >> 9) * 0) - 1; + result = 0; + extent_end = start + qnx4_ino->i_first_xtnt.xtnt_size - 1; + QNX4DEBUG(("qnx4: extent length : [%lu] bytes\n", + qnx4_ino->i_first_xtnt.xtnt_size)); + while (result < count) { + block = start + pos / QNX4_BLOCK_SIZE; + if (block > extent_end) { + if (qnx4_is_free(inode->i_sb, block) <= 0) { + printk("qnx4: next inode is busy -> write aborted.\n"); + result = -ENOSPC; + break; + } + } + if ((bh = bread(inode->i_dev, block, + QNX4_BLOCK_SIZE)) == NULL) { + printk("qnx4: I/O error on write.\n"); + result = -EIO; + goto out; + } + if (bh == NULL) { + if (result != 0) { + result = -ENOSPC; + } + break; + } + if (block > extent_end) { + qnx4_set_bitmap(inode->i_sb, block, 1); + extent_end++; + qnx4_ino->i_first_xtnt.xtnt_size = extent_end - start + 1; + } + c = QNX4_BLOCK_SIZE - (pos % QNX4_BLOCK_SIZE); + if (c > count - result) { + c = count - result; + } + if (c != QNX4_BLOCK_SIZE && buffer_uptodate(bh) == 0) { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (buffer_uptodate(bh) == 0) { + brelse(bh); + if (result != 0) { + result = -EIO; + } + break; + } + } + p = bh->b_data + (pos % QNX4_BLOCK_SIZE); + c -= copy_from_user(p, buf, c); + if (c == 0) { + brelse(bh); + if (result == 0) { + result = -EFAULT; + } + break; + } + update_vm_cache(inode, pos, p, c); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 0); + brelse(bh); + pos += c; + buf += c; + result += c; + } + if (pos > inode->i_size) { + inode->i_size = pos; + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + *ppos = pos; + mark_inode_dirty(inode); + + out: + return result; +} +#endif + +/* + * We have moostly NULL's here: the current defaults are ok for + * the qnx4 filesystem. + */ +static struct file_operations qnx4_file_operations = +{ + NULL, /* lseek - default */ + generic_file_read, /* read */ +#ifdef CONFIG_QNX4FS_RW + qnx4_file_write, /* write */ +#else + NULL, +#endif + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* no special flush code */ + NULL, /* release */ +#ifdef CONFIG_QNX4FS_RW + qnx4_sync_file, /* fsync */ +#else + NULL, +#endif + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations qnx4_file_inode_operations = +{ + &qnx4_file_operations, /* default file operations */ +#ifdef CONFIG_QNX4FS_RW + qnx4_create, /* create */ +#else + NULL, +#endif + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + qnx4_readpage, /* readpage */ + NULL, /* writepage */ + qnx4_bmap, /* bmap */ +#ifdef CONFIG_QNX4FS_RW + qnx4_truncate, /* truncate */ +#else + NULL, +#endif + NULL, /* permission */ + NULL /* smap */ +}; + +static int qnx4_readpage(struct file *file, struct page *page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i; + unsigned long buf; + unsigned long offset, avail, readlen; + unsigned long start; + unsigned long count; + struct buffer_head *bh; + int res = -EIO; + + QNX4DEBUG(("qnx4: readpage offset=[%ld]\n", (long) page->offset)); + + if (qnx4_ino->i_xblk != 0) { + printk("qnx4: sorry, this file is extended, don't know how to handle it (yet) !\n"); + return -EIO; + } + atomic_inc(&page->count); + set_bit(PG_locked, &page->flags); + buf = page_address(page); + clear_bit(PG_uptodate, &page->flags); + clear_bit(PG_error, &page->flags); + offset = page->offset; + + if (offset < inode->i_size) { + res = 0; + avail = inode->i_size - offset; + readlen = MIN(avail, PAGE_SIZE); + start = qnx4_ino->i_first_xtnt.xtnt_blk + (offset >> 9) - 1; + count = PAGE_SIZE / QNX4_BLOCK_SIZE; + do { + QNX4DEBUG(("qnx4: reading page starting at [%ld]\n", (long) start)); + if ((bh = bread(inode->i_dev, start, QNX4_BLOCK_SIZE)) == NULL) { + printk("qnx4: data corrupted or I/O error.\n"); + res = -EIO; + } else { + memcpy((void *) buf, bh->b_data, QNX4_BLOCK_SIZE); + } + buf += QNX4_BLOCK_SIZE; + start++; + count--; + } while (count != 0); + } + if (res != 0) { + set_bit(PG_error, &page->flags); + memset((void *) buf, 0, PAGE_SIZE); + } else { + set_bit(PG_uptodate, &page->flags); + } + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); +/* free_page(buf); */ + + return res; +} diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c new file mode 100644 index 000000000..826ee8a09 --- /dev/null +++ b/fs/qnx4/fsync.c @@ -0,0 +1,166 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 24-03-1998 by Richard Frowijn : first release. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/locks.h> + +#include <linux/fs.h> +#include <linux/qnx4_fs.h> + +#include <asm/segment.h> +#include <asm/system.h> + +#define blocksize QNX4_BLOCK_SIZE + +/* + * The functions for qnx4 fs file synchronization. + */ + +#ifdef CONFIG_QNX4FS_RW + +static int sync_block(struct inode *inode, unsigned short *block, int wait) +{ + struct buffer_head *bh; + unsigned short tmp; + + if (!*block) + return 0; + tmp = *block; + bh = get_hash_table(inode->i_dev, *block, blocksize); + if (!bh) + return 0; + if (*block != tmp) { + brelse(bh); + return 1; + } + 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); + bh->b_count--; + return 0; +} + +static int sync_iblock(struct inode *inode, unsigned short *iblock, + struct buffer_head **bh, int wait) +{ + int rc; + unsigned short tmp; + + *bh = NULL; + tmp = *iblock; + if (!tmp) + return 0; + rc = sync_block(inode, iblock, wait); + if (rc) + return rc; + *bh = bread(inode->i_dev, tmp, blocksize); + if (tmp != *iblock) { + brelse(*bh); + *bh = NULL; + return 1; + } + if (!*bh) + return -1; + return 0; +} + +static int sync_direct(struct inode *inode, int wait) +{ + int i; + int rc, err = 0; + + for (i = 0; i < 7; i++) { + rc = sync_block(inode, + (unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + return err; +} + +static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait) +{ + int i; + struct buffer_head *ind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, iblock, &ind_bh, wait); + if (rc || !ind_bh) + return rc; + + for (i = 0; i < 512; i++) { + rc = sync_block(inode, + ((unsigned short *) ind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse(ind_bh); + return err; +} + +static int sync_dindirect(struct inode *inode, unsigned short *diblock, + int wait) +{ + int i; + struct buffer_head *dind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < 512; i++) { + rc = sync_indirect(inode, + ((unsigned short *) dind_bh->b_data) + i, + wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + brelse(dind_bh); + return err; +} + +int qnx4_sync_file(struct file *file, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int wait, err = 0; + + (void) file; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + + for (wait = 0; wait <= 1; wait++) { + err |= sync_direct(inode, wait); + } + err |= qnx4_sync_inode(inode); + return (err < 0) ? -EIO : 0; +} + +#endif diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c new file mode 100644 index 000000000..626167044 --- /dev/null +++ b/fs/qnx4/inode.c @@ -0,0 +1,464 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 01-06-1998 by Richard Frowijn : first release. + * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc. + * 30-06-1998 by Frank Denis : first step to write inodes. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/qnx4_fs.h> +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/init.h> + +#include <asm/uaccess.h> + +#define QNX4_VERSION 4 +#define QNX4_BMNAME ".bitmap" +#define CHECK_BOOT_SIGNATURE 0 + +static struct super_operations qnx4_sops; + +#ifdef CONFIG_QNX4FS_RW + +int qnx4_sync_inode(struct inode *inode) +{ + int err = 0; +# if 0 + struct buffer_head *bh; + + bh = qnx4_update_inode(inode); + if (bh && buffer_dirty(bh)) + { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + { + printk ("IO error syncing qnx4 inode [%s:%08lx]\n", + kdevname(inode->i_dev), inode->i_ino); + err = -1; + } + brelse (bh); + } else if (!bh) { + err = -1; + } +# endif + + return err; +} + +static void qnx4_delete_inode(struct inode *inode) +{ + QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); + inode->i_size = 0; + qnx4_truncate(inode); + qnx4_free_inode(inode); +} + +static void qnx4_write_super(struct super_block *sb) +{ + QNX4DEBUG(("qnx4: write_super\n")); + sb->s_dirt = 0; +} + +static void qnx4_put_inode(struct inode *inode) +{ + if (inode->i_nlink != 0) { + return; + } + inode->i_size = 0; +} + +static void qnx4_write_inode(struct inode *inode) +{ + struct qnx4_inode_entry *raw_inode; + int block, ino; + struct buffer_head *bh; + ino = inode->i_ino; + + QNX4DEBUG(("qnx4: write inode 1.\n")); + if (inode->i_nlink == 0) { + return; + } + if (!ino) { + printk("qnx4: bad inode number on dev %s: %d is out of range\n", + kdevname(inode->i_dev), ino); + return; + } + QNX4DEBUG(("qnx4: write inode 2.\n")); + block = ino / QNX4_INODES_PER_BLOCK; + if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) { + printk("qnx4: major problem: unable to read inode from dev " + "%s\n", kdevname(inode->i_dev)); + return; + } + raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + + (ino % QNX4_INODES_PER_BLOCK); + raw_inode->di_mode = inode->i_mode; + raw_inode->di_uid = inode->i_uid; + raw_inode->di_gid = inode->i_gid; + raw_inode->di_nlink = inode->i_nlink; + raw_inode->di_size = inode->i_size; + raw_inode->di_mtime = inode->i_mtime; + raw_inode->di_atime = inode->i_atime; + raw_inode->di_ctime = inode->i_ctime; + raw_inode->di_first_xtnt.xtnt_size = inode->i_blocks; + mark_buffer_dirty(bh, 1); + brelse(bh); +} + +#endif + +static struct super_block *qnx4_read_super(struct super_block *, void *, int); +static void qnx4_put_super(struct super_block *sb); +static void qnx4_read_inode(struct inode *); +static int qnx4_remount(struct super_block *sb, int *flags, char *data); +static int qnx4_statfs(struct super_block *, struct statfs *, int); + +static struct super_operations qnx4_sops = +{ + qnx4_read_inode, +#ifdef CONFIG_QNX4FS_RW + qnx4_write_inode, +#else + NULL, +#endif +#ifdef CONFIG_QNX4FS_RW + qnx4_put_inode, + qnx4_delete_inode, + NULL, /* notify_change */ +#else + NULL, /* put_inode */ + NULL, /* delete_inode */ + NULL, /* notify_change */ +#endif + qnx4_put_super, +#ifdef CONFIG_QNX4FS_RW + qnx4_write_super, +#else + NULL, +#endif + qnx4_statfs, + qnx4_remount, + NULL /* clear_inode */ +}; + +static int qnx4_remount(struct super_block *sb, int *flags, char *data) +{ + struct qnx4_sb_info *qs; + + qs = &sb->u.qnx4_sb; + qs->Version = QNX4_VERSION; + if (*flags & MS_RDONLY) { + return 0; + } + mark_buffer_dirty(qs->sb_buf, 1); + + return 0; +} + +struct buffer_head *inode_getblk(struct inode *inode, int nr, + int create) +{ + int tmp; + int tst; + struct buffer_head *result = NULL; + + tst = nr; + repeat: + tmp = tst; + if (tmp) { + result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE); + if (tmp == tst) { + return result; + } + brelse(result); + goto repeat; + } + if (!create) { + return NULL; + } +#if 0 + tmp = qnx4_new_block(inode->i_sb); + if (!tmp) { + return NULL; + } + result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE); + if (tst) { + qnx4_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + tst = tmp; +#endif + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return result; +} + +struct buffer_head *qnx4_bread(struct inode *inode, int block, int create) +{ + struct buffer_head *bh; + + bh = inode_getblk(inode, block, create); + if (!bh || buffer_uptodate(bh)) { + return bh; + } + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (buffer_uptodate(bh)) { + return bh; + } + brelse(bh); + + return NULL; +} + +static int qnx4_statfs(struct super_block *sb, + struct statfs *buf, int bufsize) +{ + struct statfs tmp; + + memset(&tmp, 0, sizeof tmp); + tmp.f_type = sb->s_magic; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8; + tmp.f_bfree = qnx4_count_free_blocks(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = 0x00; /* change this !!! */ + tmp.f_ffree = qnx4_count_free_inodes(sb); + tmp.f_namelen = QNX4_NAME_MAX; + + return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0; +} + +/* + * Check the root directory of the filesystem to make sure + * it really _is_ a qnx4 filesystem, and to check the size + * of the directory entry. + */ +static const char *qnx4_checkroot(struct super_block *s) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *rootdir; + int rd, rl; + 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 { + QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(s->s_dev))); + rd = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk - 1; + rl = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size; + for (j = 0; j < rl; j++) { + bh = bread(s->s_dev, rd + j, QNX4_BLOCK_SIZE); /* root dir, first block */ + if (bh == NULL) { + return "unable to read root entry."; + } + for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { + rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); + if (rootdir->di_fname != NULL) { + QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname)); + if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) { + found = 1; + s->u.qnx4_sb.BitMap = rootdir; /* keep bitmap inode known */ + break; + } + } + } + brelse(bh); + if (found != 0) { + break; + } + } + if (found == 0) { + return "bitmap file not found."; + } + } + return NULL; +} + +static struct super_block *qnx4_read_super(struct super_block *s, + void *data, int silent) +{ + struct buffer_head *bh; + kdev_t dev = s->s_dev; +#if CHECK_BOOT_SIGNATURE + char *tmpc; +#endif + const char *errmsg; + + MOD_INC_USE_COUNT; + lock_super(s); + set_blocksize(dev, QNX4_BLOCK_SIZE); + s->s_blocksize = QNX4_BLOCK_SIZE; + s->s_blocksize_bits = 9; + s->s_dev = dev; + +#if CHECK_BOOT_SIGNATURE + bh = bread(dev, 0, QNX4_BLOCK_SIZE); + if (!bh) { + printk("qnx4: unable to read the boot sector\n"); + goto outnobh; + } + 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"); + goto out; + } + brelse(bh); +#endif + bh = bread(dev, 1, QNX4_BLOCK_SIZE); + if (!bh) { + printk("qnx4: unable to read the superblock\n"); + goto outnobh; + } + s->s_op = &qnx4_sops; + s->s_magic = QNX4_SUPER_MAGIC; +#ifndef CONFIG_QNX4FS_RW + s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ +#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), NULL); + if (s->s_root == NULL) { + printk("qnx4: get inode failed\n"); + goto out; + } + errmsg = qnx4_checkroot(s); + if (errmsg != NULL) { + printk("qnx4: %s\n", errmsg); + goto out; + } + brelse(bh); + unlock_super(s); + s->s_dirt = 1; + + return s; + + out: + brelse(bh); + outnobh: + s->s_dev = 0; + unlock_super(s); + MOD_DEC_USE_COUNT; + + return NULL; +} + +static void qnx4_put_super(struct super_block *sb) +{ + MOD_DEC_USE_COUNT; + return; +} + +static void qnx4_read_inode(struct inode *inode) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *raw_inode; + int block, ino; + + ino = inode->i_ino; + inode->i_op = NULL; + inode->i_mode = 0; + + QNX4DEBUG(("Reading inode : [%d]\n", ino)); + if (!ino) { + printk("qnx4: bad inode number on dev %s: %d is out of range\n", + kdevname(inode->i_dev), ino); + return; + } + block = ino / QNX4_INODES_PER_BLOCK; + + if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) { + printk("qnx4: major problem: unable to read inode from dev " + "%s\n", kdevname(inode->i_dev)); + return; + } + raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + + (ino % QNX4_INODES_PER_BLOCK); + + inode->i_mode = raw_inode->di_mode; + inode->i_uid = raw_inode->di_uid; + inode->i_gid = raw_inode->di_gid; + inode->i_nlink = raw_inode->di_nlink; + inode->i_size = raw_inode->di_size; + inode->i_mtime = raw_inode->di_mtime; + inode->i_atime = raw_inode->di_atime; + inode->i_ctime = raw_inode->di_ctime; + inode->i_blocks = raw_inode->di_first_xtnt.xtnt_size; + inode->i_blksize = QNX4_DIR_ENTRY_SIZE; + + memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE); + inode->i_op = &qnx4_file_inode_operations; + if (S_ISREG(inode->i_mode)) { + inode->i_op = &qnx4_file_inode_operations; + } else { + if (S_ISDIR(inode->i_mode)) { + inode->i_op = &qnx4_dir_inode_operations; + } else { + if (S_ISLNK(inode->i_mode)) { + inode->i_op = &qnx4_symlink_inode_operations; + } else { + if (S_ISCHR(inode->i_mode)) { + inode->i_op = &chrdev_inode_operations; + } else { + if (S_ISBLK(inode->i_mode)) { + inode->i_op = &blkdev_inode_operations; + } else { + if (S_ISFIFO(inode->i_mode)) { + init_fifo(inode); + } + } + } + } + } + } + brelse(bh); +} + +static struct file_system_type qnx4_fs_type = +{ + "qnx4", + FS_REQUIRES_DEV, + qnx4_read_super, + NULL +}; + +__initfunc(int init_qnx4_fs(void)) +{ + printk("QNX4 filesystem v0.2 registered.\n"); + return register_filesystem(&qnx4_fs_type); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + return init_qnx4_fs(); +} + +void cleanup_module(void) +{ + unregister_filesystem(&qnx4_fs_type); +} + +#endif diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c new file mode 100644 index 000000000..99cbf05eb --- /dev/null +++ b/fs/qnx4/namei.c @@ -0,0 +1,279 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 01-06-1998 by Richard Frowijn : first release. + * 21-06-1998 by Frank Denis : dcache support, fixed error codes. + * 04-07-1998 by Frank Denis : first step for rmdir/unlink. + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/qnx4_fs.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/errno.h> + +#include <asm/segment.h> + +/* + * check if the filename is correct. For some obscure reason, qnx writes a + * new file twice in the directory entry, first with all possible options at 0 + * and for a second time the way it is, they want us not to access the qnx + * filesystem when whe are using linux. + */ +static int qnx4_match(int len, const char *name, + struct buffer_head *bh, unsigned long *offset) +{ + struct qnx4_inode_entry *de; + int namelen; + + if (bh == NULL) { + printk("qnx4: matching unassigned buffer !\n"); + return 0; + } + de = (struct qnx4_inode_entry *) (bh->b_data + *offset); + *offset += QNX4_DIR_ENTRY_SIZE; + if ((de->di_status & 0x08) == 0x08) { + namelen = QNX4_NAME_MAX; + } else { + namelen = _SHORT_NAME_MAX; + } + /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ + if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) { + return 1; + } + if (len != strlen(de->di_fname)) { + return 0; + } + if (strncmp(name, de->di_fname, len) == 0) { + if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) { + if (de->di_status) { + return 1; + } + } + } + return 0; +} + +static struct buffer_head *qnx4_find_entry(int len, struct inode *dir, + const char *name, struct qnx4_inode_entry **res_dir, int *ino) +{ + unsigned long block, offset, blkofs; + struct buffer_head *bh; + + *res_dir = NULL; + if (!dir || !dir->i_sb) { + if (!dir) { + printk("qnx4: NULL dir.\n"); + } else { + printk("qnx4: no superblock on dir.\n"); + } + return NULL; + } + bh = NULL; + blkofs = dir->u.qnx4_i.i_first_xtnt.xtnt_blk - 1; + offset = block = 0; + while (block * QNX4_BLOCK_SIZE + offset < dir->i_size) { + if (!bh) { + bh = qnx4_bread(dir, block + blkofs, 0); + if (!bh) { + block++; + continue; + } + } + *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset); + if (qnx4_match(len, name, bh, &offset)) { + *ino = (block + blkofs) * QNX4_INODES_PER_BLOCK + + (offset / QNX4_DIR_ENTRY_SIZE) - 1; + return bh; + } + if (offset < bh->b_size) { + continue; + } + brelse(bh); + bh = NULL; + offset = 0; + block++; + } + brelse(bh); + *res_dir = NULL; + return NULL; +} + +int qnx4_lookup(struct inode *dir, struct dentry *dentry) +{ + int ino; + struct qnx4_inode_entry *de; + struct qnx4_link_info *lnk; + struct buffer_head *bh; + const char *name = dentry->d_name.name; + int len = dentry->d_name.len; + struct inode *foundinode; + + if (!dir) { + return -EBADF; + } + if (!S_ISDIR(dir->i_mode)) { + return -EBADF; + } + if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) { + return -ENOENT; + } + /* The entry is linked, let's get the real info */ + if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { + lnk = (struct qnx4_link_info *) de; + ino = (lnk->dl_inode_blk - 1) * QNX4_INODES_PER_BLOCK + + lnk->dl_inode_ndx; + } + brelse(bh); + + if ((foundinode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + return -EACCES; + } + d_add(dentry, foundinode); + + return 0; +} + +#ifdef CONFIG_QNX4FS_RW +int qnx4_create(struct inode *dir, struct dentry *dentry, int mode) +{ + QNX4DEBUG(("qnx4: qnx4_create\n")); + if (dir == NULL) { + return -ENOENT; + } + return -ENOSPC; +} + +int qnx4_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *de; + struct inode *inode; + int retval; + int ino; + + QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); + bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, + &de, &ino); + if (bh == NULL) { + return -ENOENT; + } + if ((inode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + retval = -EACCES; + goto end_rmdir; + } + retval = -EPERM; + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) { + QNX4DEBUG(("qnx4: rmdir->capabilities\n")); + goto end_rmdir; + } + if (inode->i_dev != dir->i_dev) { + QNX4DEBUG(("qnx4: rmdir->different devices\n")); + goto end_rmdir; + } + if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ + QNX4DEBUG(("qnx4: inode==dir\n")); + goto end_rmdir; + } + if (!S_ISDIR(inode->i_mode)) { + QNX4DEBUG(("qnx4: rmdir->not a directory\n")); + retval = -ENOTDIR; + goto end_rmdir; + } +#if 0 + if (!empty_dir(inode)) { + retval = -ENOTEMPTY; + goto end_rmdir; + } +#endif + if (dentry->d_count > 1) { + retval = -EBUSY; + goto end_rmdir; + } + if (inode->i_nlink != 2) { + QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); + } + QNX4DEBUG(("qnx4: deleting directory\n")); + de->di_status = 0; + memset(de->di_fname, 0, sizeof de->di_fname); + de->di_mode = 0; + mark_buffer_dirty(bh, 1); + inode->i_nlink = 0; + mark_inode_dirty(inode); + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_nlink--; + mark_inode_dirty(dir); + d_delete(dentry); + retval = 0; + + end_rmdir: + brelse(bh); + + return retval; +} + +int qnx4_unlink(struct inode *dir, struct dentry *dentry) +{ + struct buffer_head *bh; + struct qnx4_inode_entry *de; + struct inode *inode; + int retval; + int ino; + + QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); + bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, + &de, &ino); + if (bh == NULL) { + return -ENOENT; + } + if ((inode = iget(dir->i_sb, ino)) == NULL) { + QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); + retval = -EACCES; + goto end_unlink; + } + retval = -EPERM; + if (S_ISDIR(inode->i_mode)) { + goto end_unlink; + } + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) { + goto end_unlink; + } + if (!inode->i_nlink) { + QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", + kdevname(inode->i_dev), + inode->i_ino, inode->i_nlink)); + inode->i_nlink = 1; + } + de->di_status = 0; + memset(de->di_fname, 0, sizeof de->di_fname); + de->di_mode = 0; + mark_buffer_dirty(bh, 1); + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); + inode->i_nlink--; + inode->i_ctime = dir->i_ctime; + mark_inode_dirty(inode); + d_delete(dentry); + retval = 0; + + end_unlink: + brelse(bh); + + return retval; +} +#endif diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c new file mode 100644 index 000000000..f590d04a6 --- /dev/null +++ b/fs/qnx4/symlinks.c @@ -0,0 +1,121 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 28-05-1998 by Richard Frowijn : first release. + * 21-06-1998 by Frank Denis : ugly changes to make it compile on Linux 2.1.99+ + */ + +/* THIS FILE HAS TO BE REWRITTEN */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/qnx4_fs.h> +#include <linux/stat.h> + +#include <asm/segment.h> +#include <asm/uaccess.h> + +static int qnx4_readlink(struct dentry *, char *, int); +static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *); + +/* + * symlinks can't do much... + */ +struct inode_operations qnx4_symlink_inode_operations = +{ + NULL, /* no file-operations */ +#ifdef CONFIG_QNX4FS_RW + qnx4_create, /* create */ +#else + NULL, +#endif + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + qnx4_readlink, /* readlink */ + qnx4_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static struct dentry *qnx4_follow_link(struct dentry *dentry, + struct dentry *base) +{ +#if 0 + struct inode *inode = dentry->d_inode; + struct buffer_head *bh; + + if (!inode) { + return ERR_PTR(-ENOENT); + } + if (current->link_count > 5) { + return ERR_PTR(-ELOOP); + } + if (!(bh = qnx4_bread(inode, 0, 0))) { + return ERR_PTR(-EIO); + } + current->link_count++; + current->link_count--; + brelse(bh); + return 0; +#else + printk("qnx4: qnx4_follow_link needs to be fixed.\n"); + return ERR_PTR(-EIO); +#endif +} + +static int qnx4_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh; + int i; + char c; + struct qnx4_inode_info *qnx4_ino; + + QNX4DEBUG(("qnx4: qnx4_readlink() called\n")); + + if (buffer == NULL || inode == NULL || !S_ISLNK(inode->i_mode)) { + return -EINVAL; + } + qnx4_ino = &inode->u.qnx4_i; + if (buflen > 1023) { + buflen = 1023; + } + bh = bread(inode->i_dev, qnx4_ino->i_first_xtnt.xtnt_blk, + QNX4_BLOCK_SIZE); + QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n", + bh->b_data)); + if (bh == NULL) { + QNX4DEBUG(("qnx4: NULL symlink bh\n")); + return 0; + } + if (bh->b_data[0] != 0) { + i = 0; + while (i < buflen && (c = bh->b_data[i])) { + i++; + put_user(c, buffer++); + } + brelse(bh); + return i; + } else { + brelse(bh); + memcpy(buffer, "fixme", 5); + return 5; + } +} diff --git a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c new file mode 100644 index 000000000..a25bf1daf --- /dev/null +++ b/fs/qnx4/truncate.c @@ -0,0 +1,38 @@ +/* + * QNX4 file system, Linux implementation. + * + * Version : 0.1 + * + * Using parts of the xiafs filesystem. + * + * History : + * + * 30-06-1998 by Frank DENIS : ugly filler. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/qnx4_fs.h> +#include <linux/fs.h> +#include <linux/locks.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_QNX4FS_RW + +void qnx4_truncate(struct inode *inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) { + return; + } + if (!(S_ISDIR(inode->i_mode))) { + /* TODO */ + } + QNX4DEBUG(("qnx4: qnx4_truncate called\n")); + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} + +#endif diff --git a/fs/read_write.c b/fs/read_write.c index 1f5c69e67..e36a2d1e1 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -288,7 +288,7 @@ asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector, file = fget(fd); if (!file) goto bad_file; - if (file->f_mode & FMODE_READ) + if (file->f_op && file->f_op->read && (file->f_mode & FMODE_READ)) ret = do_readv_writev(VERIFY_WRITE, file, vector, count); fput(file); @@ -309,7 +309,7 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, file = fget(fd); if (!file) goto bad_file; - if (file->f_mode & FMODE_WRITE) { + if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) { down(&file->f_dentry->d_inode->i_sem); ret = do_readv_writev(VERIFY_READ, file, vector, count); up(&file->f_dentry->d_inode->i_sem); diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 86c9208ad..aa361336a 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -30,17 +30,18 @@ * correct namelen for statfs * spotted by Bill Hawes: * readlink shouldn't iput() + * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir() + * exposed a problem in readdir + * 2.1.107 code-freeze spellchecker run + * Aug 1998 2.1.118+ VFS changes */ /* todo: * - see Documentation/filesystems/romfs.txt - * - use malloced memory for file names? - * - quicklist routines from fs/namei.c, get_page is possibly not - * intended to be used now + * - use allocated, not stack memory for file names? * - considering write access... * - network (tftp) files? - * - in the ancient times something leaked to made umounts - * impossible, but I've not seen it in the last months + * - merge back some _op tables */ /* @@ -492,6 +493,7 @@ static struct file_operations romfs_file_operations = { NULL, /* ioctl */ generic_file_mmap, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ @@ -529,6 +531,7 @@ static struct file_operations romfs_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index ed547eee6..92cd94363 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -40,6 +40,7 @@ static struct file_operations smb_dir_operations = smb_ioctl, /* ioctl */ NULL, /* mmap */ smb_dir_open, /* open(struct inode *, struct file *) */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 0b886da90..3c3e87aa6 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -389,6 +389,7 @@ static struct file_operations smb_file_operations = smb_ioctl, /* ioctl */ smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*) */ smb_file_open, /* open(struct inode*, struct file*) */ + NULL, /* flush */ smb_file_release, /* release(struct inode*, struct file*) */ smb_fsync, /* fsync(struct file*, struct dentry*) */ NULL, /* fasync(struct file*, int) */ diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 2334ff729..b61bb3277 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -1961,11 +1961,13 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) smb_init_dirent(server, fattr); /* - * Win 95 is painfully slow at returning trans2 getattr info, - * so we provide the SMB_FIX_OLDATTR option switch. + * Select whether to use core or trans2 getattr. */ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) { - if (server->mnt->version & SMB_FIX_OLDATTR) + /* + * Win 95 appears to break with the trans2 getattr. + */ + if (server->mnt->version & (SMB_FIX_OLDATTR|SMB_FIX_WIN95)) goto core_attr; if (server->mnt->version & SMB_FIX_DIRATTR) result = smb_proc_getattr_ff(server, dir, fattr); diff --git a/fs/super.c b/fs/super.c index 07aa03a63..843464421 100644 --- a/fs/super.c +++ b/fs/super.c @@ -68,7 +68,10 @@ static int do_remount_sb(struct super_block *sb, int flags, char * data); /* this is initialized in init/main.c */ kdev_t ROOT_DEV; -struct super_block super_blocks[NR_SUPER]; +int nr_super_blocks = 0; +int max_super_blocks = NR_SUPER; +LIST_HEAD(super_blocks); + static struct file_system_type *file_systems = (struct file_system_type *) NULL; struct vfsmount *vfsmntlist = (struct vfsmount *) NULL; static struct vfsmount *vfsmnttail = (struct vfsmount *) NULL, @@ -446,7 +449,9 @@ void sync_supers(kdev_t dev) { struct super_block * sb; - for (sb = super_blocks + 0 ; sb < super_blocks + NR_SUPER ; sb++) { + for (sb = sb_entry(super_blocks.next); + sb != sb_entry(&super_blocks); + sb = sb_entry(sb->s_list.next)) { if (!sb->s_dev) continue; if (dev && sb->s_dev != dev) @@ -471,15 +476,15 @@ struct super_block * get_super(kdev_t dev) if (!dev) return NULL; restart: - s = 0+super_blocks; - while (s < NR_SUPER+super_blocks) + s = sb_entry(super_blocks.next); + while (s != sb_entry(&super_blocks)) if (s->s_dev == dev) { wait_on_super(s); if (s->s_dev == dev) return s; goto restart; } else - s++; + s = sb_entry(s->s_list.next); return NULL; } @@ -519,16 +524,28 @@ out: */ static struct super_block *get_empty_super(void) { - struct super_block *s = 0+super_blocks; + struct super_block *s; - for (; s < NR_SUPER+super_blocks; s++) { + for (s = sb_entry(super_blocks.next); + s != sb_entry(&super_blocks); + s = sb_entry(s->s_list.next)) { if (s->s_dev) continue; if (!s->s_lock) return s; printk("VFS: empty superblock %p locked!\n", s); } - return NULL; + /* Need a new one... */ + if (nr_super_blocks >= max_super_blocks) + return NULL; + s = kmalloc(sizeof(struct super_block), GFP_USER); + if (s) { + nr_super_blocks++; + memset(s, 0, sizeof(struct super_block)); + INIT_LIST_HEAD(&s->s_dirty); + list_add (&s->s_list, super_blocks.prev); + } + return s; } static struct super_block * read_super(kdev_t dev,const char *name,int flags, @@ -1112,7 +1129,7 @@ clean_up: goto dput_and_out; } -__initfunc(static void do_mount_root(void)) +void __init mount_root(void) { struct file_system_type * fs_type; struct super_block * sb; @@ -1120,7 +1137,7 @@ __initfunc(static void do_mount_root(void)) struct inode * d_inode = NULL; struct file filp; int retval; - + #ifdef CONFIG_ROOT_NFS if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { ROOT_DEV = 0; @@ -1207,27 +1224,11 @@ __initfunc(static void do_mount_root(void)) } -__initfunc(void mount_root(void)) -{ - struct super_block * sb = super_blocks; - int i; - - memset(super_blocks, 0, sizeof(super_blocks)); - /* - * Initialize the dirty inode list headers for the super blocks - */ - for (i = NR_SUPER ; i-- ; sb++) - INIT_LIST_HEAD(&sb->s_dirty); - - do_mount_root(); -} - - #ifdef CONFIG_BLK_DEV_INITRD extern int initmem_freed; -__initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) +static int __init do_change_root(kdev_t new_root_dev,const char *put_old) { kdev_t old_root_dev; struct vfsmount *vfsmnt; @@ -1242,7 +1243,7 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) return -EBUSY; } ROOT_DEV = new_root_dev; - do_mount_root(); + mount_root(); dput(old_root); dput(old_pwd); #if 1 diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index e93f8bb11..ee99164fb 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -38,6 +38,7 @@ static struct file_operations sysv_dir_operations = { NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ file_fsync /* default fsync */ }; diff --git a/fs/sysv/file.c b/fs/sysv/file.c index f4cf216c5..d60be8fa5 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -48,6 +48,7 @@ static struct file_operations sysv_file_operations = { NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ sysv_sync_file /* fsync */ }; diff --git a/fs/ufs/acl.c b/fs/ufs/acl.c index 934e474a1..2b6768352 100644 --- a/fs/ufs/acl.c +++ b/fs/ufs/acl.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998 * Daniel Pirkl <daniel.pirkl@email.cz> - * Charles Uiversity, Faculty of Mathematics and Physics + * Charles University, Faculty of Mathematics and Physics * * from * @@ -16,8 +16,7 @@ */ /* - * This file will contain the Access Control Lists management for the - * second extended file system. + * This file will contain the Access Control Lists management for UFS */ #include <linux/errno.h> @@ -39,8 +38,8 @@ int ufs_permission (struct inode * inode, int mask) * Nobody gets write access to a file on a readonly-fs */ if ((mask & S_IWOTH) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) && - IS_RDONLY(inode)) + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) && + IS_RDONLY(inode)) return -EROFS; /* * Nobody gets write access to an immutable file @@ -57,7 +56,7 @@ int ufs_permission (struct inode * inode, int mask) mode >>= 3; /* * Access is always granted for root. We now check last, - * though, for BSD process accounting correctness + * though, for BSD process accounting correctness */ if (((mode & mask & S_IRWXO) == mask) || fsuser()) return 0; diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 1022ad383..6567c515b 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -32,7 +32,7 @@ #define UFSDM \ ufs_print_cylinder_stuff (ucg, swab); \ printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), \ -swab32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \ +SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \ printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), \ SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \ printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), \ @@ -133,7 +133,7 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); - } + } sb->s_dirt = 1; unlock_super (sb); @@ -211,7 +211,7 @@ do_more: cylno = ufs_cbtocylno(i); INC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(i))); INC_SWAB32(ubh_cg_blktot(ucpi, cylno)); - } + } UFSDM @@ -220,7 +220,7 @@ do_more: if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); - } + } if (overflow) { fragment += count; @@ -249,7 +249,7 @@ failed: mark_buffer_dirty (bh, 1); \ if (IS_SYNC(inode)) { \ ll_rw_block (WRITE, 1, &bh); \ - wait_on_buffer (bh); \ + wait_on_buffer (bh); \ } \ brelse (bh); \ } @@ -328,7 +328,7 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, * allocate new fragment */ if (oldcount == 0) { - result = ufs_alloc_fragments (inode, cgno, goal, count, err); + result = ufs_alloc_fragments (inode, cgno, goal, count, err); if (result) { *p = SWAB32(result); *err = 0; @@ -373,7 +373,7 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, request = uspi->s_fpb; if (SWAB32(usb1->fs_cstotal.cs_nffree) < uspi->s_dsize * (uspi->s_minfree - 2) / 100) - break; + break; usb1->fs_optim = SWAB32(UFS_OPTSPACE); break; } @@ -532,7 +532,7 @@ unsigned ufs_alloc_fragments (struct inode * inode, unsigned cgno, } /* - * 3. brute force search + * 3. brute force search * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step ) */ cgno = (oldcg + 1) % uspi->s_ncg; @@ -642,7 +642,7 @@ unsigned ufs_alloccg_block (struct inode * inode, goto norot; } goal = ufs_blknum (goal); - goal = ufs_dtogd (goal); + goal = ufs_dtogd (goal); /* * If the requested block is available, use it. @@ -652,7 +652,7 @@ unsigned ufs_alloccg_block (struct inode * inode, goto gotit; } - /*** This function should be optimalized later ***/ + /*** This function should be optimized later ***/ norot: result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb); diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index ee0e85674..470847dca 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406 + * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406 * * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen <nkbj@image.dk> partially based @@ -46,14 +46,14 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) /* Isn't that already done in the upper layer??? - * the VFS layer really needs some explicit documentation! - */ + * the VFS layer really needs some explicit documentation! + */ if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; swab = sb->u.ufs_sb.s_swab; - flags = sb->u.ufs_sb.s_flags; + flags = sb->u.ufs_sb.s_flags; UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos)) @@ -63,14 +63,14 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) while (!error && !stored && filp->f_pos < inode->i_size) { lblk = (filp->f_pos) >> sb->s_blocksize_bits; - /* XXX - ufs_bmap() call needs error checking */ - blk = ufs_bmap(inode, lblk); + /* XXX - ufs_bmap() call needs error checking */ + blk = ufs_bmap(inode, lblk); bh = bread (sb->s_dev, blk, sb->s_blocksize); if (!bh) { - /* XXX - error - skip to the next block */ - printk("ufs_readdir: " + /* XXX - error - skip to the next block */ + printk("ufs_readdir: " "dir inode %lu has a hole at offset %lu\n", - inode->i_ino, (unsigned long int)filp->f_pos); + inode->i_ino, (unsigned long int)filp->f_pos); filp->f_pos += sb->s_blocksize - offset; continue; } @@ -103,23 +103,23 @@ revalidate: while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ufs_dir_entry *) (bh->b_data + offset); - /* XXX - put in a real ufs_check_dir_entry() */ - if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) { + /* XXX - put in a real ufs_check_dir_entry() */ + if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) { /* SWAB16() was unneeded -- compare to 0 */ - filp->f_pos = (filp->f_pos & - (sb->s_blocksize - 1)) + - sb->s_blocksize; - brelse(bh); - return stored; - } + filp->f_pos = (filp->f_pos & + (sb->s_blocksize - 1)) + + sb->s_blocksize; + brelse(bh); + return stored; + } #if 0 /* XXX */ if (!ext2_check_dir_entry ("ext2_readdir", inode, de, /* XXX - beware about de having to be swabped somehow */ bh, offset)) { /* On error, skip the f_pos to the - next block. */ + next block. */ filp->f_pos = (filp->f_pos & - (sb->s_blocksize - 1)) + + (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; @@ -139,7 +139,7 @@ revalidate: UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino))) UFSD(("namlen %u\n", ufs_namlen(de))) error = filldir(dirent, de->d_name, ufs_namlen(de), - filp->f_pos, SWAB32(de->d_ino)); + filp->f_pos, SWAB32(de->d_ino)); if (error) break; if (version != inode->i_version) @@ -199,6 +199,7 @@ static struct file_operations ufs_dir_operations = { NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ + NULL, /* flush */ NULL, /* release */ file_fsync, /* fsync */ NULL, /* fasync */ diff --git a/fs/ufs/file.c b/fs/ufs/file.c index a6fa50377..d6bd7c139 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -61,6 +61,7 @@ static struct file_operations ufs_file_operations = { NULL, /* ioctl */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ ufs_release_file, /* release */ NULL, /* fsync */ NULL, /* fasync */ diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 123a76a75..afea7180a 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -91,7 +91,7 @@ static inline unsigned ufs_block_bmap (struct buffer_head * bh, unsigned nr, return 0; tmp = SWAB32(((u32 *) bh->b_data)[nr >> uspi->s_fpbshift]) + (nr & uspi->s_fpbmask); brelse (bh); - UFSD(("EXIT, resutl %u\n", tmp)) + UFSD(("EXIT, result %u\n", tmp)) return tmp; } @@ -254,7 +254,7 @@ repeat: */ else /* (lastblock > block) */ { if (lastblock && (tmp = SWAB32(inode->u.ufs_i.i_u1.i_data[lastblock-1]))) - goal = tmp + uspi->s_fpb; + goal = tmp + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, fragment - blockoff, goal, uspi->s_fpb, err); } @@ -322,10 +322,10 @@ repeat: *err = -EFBIG; return NULL; } - if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb)) - goal = tmp + uspi->s_fpb; - else - goal = bh->b_blocknr + uspi->s_fpb; + if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb)) + goal = tmp + uspi->s_fpb; + else + goal = bh->b_blocknr + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err); if (!tmp) { if (SWAB32(*p)) { @@ -345,7 +345,7 @@ repeat: inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); brelse (bh); - UFSD(("EXIT, resutl %u\n", tmp + blockoff)) + UFSD(("EXIT, result %u\n", tmp + blockoff)) return result; } @@ -492,7 +492,7 @@ void ufs_read_inode (struct inode * inode) } /* - * Linux i_size can be 32 on some architektures. We will mark + * Linux i_size can be 32 on some architectures. We will mark * big files as read only and let user access first 32 bits. */ inode->u.ufs_i.i_size = SWAB64(ufs_inode->ui_size); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 309b31665..9542d2770 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -266,9 +266,9 @@ static struct buffer_head * ufs_add_entry (struct inode * dir, de = (struct ufs_dir_entry *) bh->b_data; *err = -ENOSPC; while (1) { - if ((char *)de >= SECTOR_SIZE + bh->b_data) { + if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) { fragoff = offset & ~uspi->s_fmask; - if (fragoff != 0 && fragoff != SECTOR_SIZE) + if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE) ufs_error (sb, "ufs_add_entry", "internal error" " fragoff %u", fragoff); if (!fragoff) { @@ -285,9 +285,9 @@ static struct buffer_head * ufs_add_entry (struct inode * dir, } de = (struct ufs_dir_entry *) (bh->b_data + fragoff); de->d_ino = SWAB32(0); - de->d_reclen = SWAB16(SECTOR_SIZE); + de->d_reclen = SWAB16(UFS_SECTOR_SIZE); de->d_u.d_namlen = SWAB16(0); - dir->i_size = offset + SECTOR_SIZE; + dir->i_size = offset + UFS_SECTOR_SIZE; mark_inode_dirty(dir); } else { de = (struct ufs_dir_entry *) bh->b_data; @@ -384,11 +384,11 @@ static int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir, return 0; } i += SWAB16(de->d_reclen); - if (i == SECTOR_SIZE) pde = NULL; + if (i == UFS_SECTOR_SIZE) pde = NULL; else pde = de; de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); - if (i == SECTOR_SIZE && SWAB16(de->d_reclen) == 0) + if (i == UFS_SECTOR_SIZE && SWAB16(de->d_reclen) == 0) break; } UFSD(("EXIT\n")) @@ -537,7 +537,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) goto out; inode->i_op = &ufs_dir_inode_operations; - inode->i_size = SECTOR_SIZE; + inode->i_size = UFS_SECTOR_SIZE; dir_block = ufs_bread (inode, 0, 1, &err); if (!dir_block) { inode->i_nlink--; /* is this nlink == 0? */ @@ -545,7 +545,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) iput (inode); return err; } - inode->i_blocks = sb->s_blocksize / SECTOR_SIZE; + inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE; de = (struct ufs_dir_entry *) dir_block->b_data; de->d_ino = SWAB32(inode->i_ino); de->d_u.d_namlen = SWAB16(1); @@ -553,7 +553,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) strcpy (de->d_name, "."); de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); de->d_ino = SWAB32(dir->i_ino); - de->d_reclen = SWAB16(SECTOR_SIZE - UFS_DIR_REC_LEN(1)); + de->d_reclen = SWAB16(UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1)); de->d_u.d_namlen = SWAB16(2); strcpy (de->d_name, ".."); inode->i_nlink = 2; @@ -605,7 +605,7 @@ static int ufs_empty_dir (struct inode * inode) if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) || !(bh = ufs_bread (inode, 0, 0, &err))) { - ufs_warning (inode->i_sb, "empty_dir", + ufs_warning (inode->i_sb, "empty_dir", "bad directory (dir #%lu) - no data block", inode->i_ino); return 1; @@ -614,7 +614,7 @@ static int ufs_empty_dir (struct inode * inode) de1 = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); if (SWAB32(de->d_ino) != inode->i_ino || !SWAB32(de1->d_ino) || strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) { - ufs_warning (inode->i_sb, "empty_dir", + ufs_warning (inode->i_sb, "empty_dir", "bad directory (dir #%lu) - no `.' or `..'", inode->i_ino); return 1; @@ -625,7 +625,7 @@ static int ufs_empty_dir (struct inode * inode) if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { brelse (bh); bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err); - if (!bh) { + if (!bh) { ufs_error (sb, "empty_dir", "directory #%lu contains a hole at offset %lu", inode->i_ino, offset); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index b68af5771..d4426ad5d 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -15,7 +15,7 @@ * Gertjan van Wingerde <gertjan@cs.vu.nl> * * Clean swab support on 19970406 by - * Francois-Rene Rideau <rideau@ens.fr> + * Francois-Rene Rideau <fare@tunes.org> * * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen <nkbj@image.dk> partially based @@ -26,10 +26,10 @@ * * write support Daniel Pirkl <daniel.pirkl@email.cz> 1998 * - */ #include <linux/module.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> @@ -130,7 +130,7 @@ void ufs_print_cylinder_stuff(struct ufs_cylinder_group *cg, unsigned swab) /* * Called while file system is mounted, read super block - * and create important imtermal structures. + * and create important internal structures. */ struct super_block * ufs_read_super ( struct super_block * sb, @@ -148,7 +148,7 @@ struct super_block * ufs_read_super ( unsigned flags, swab; s64 tmp; static unsigned offsets[] = {0, 96, 160}; /* different superblock locations */ - + UFSD(("ENTER\n")) uspi = NULL; @@ -156,13 +156,12 @@ struct super_block * ufs_read_super ( base = space = NULL; sb->u.ufs_sb.s_ucg = NULL; flags = 0; - swab = 0; /* sb->s_dev and sb->s_flags are set by our caller * data is the mystery argument to sys_mount() * * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt, - * and s_type when we return. + * and s_type when we return. */ MOD_INC_USE_COUNT; @@ -183,7 +182,7 @@ struct super_block * ufs_read_super ( i = 0; uspi->s_sbbase = offsets[i]; -again: +again: set_blocksize (sb->s_dev, block_size); /* @@ -199,30 +198,47 @@ again: /* * Check ufs magic number + * This code uses goto, because it's a lesser evil than unbalanced + * structure in conditional code. Brought to you by Fare' as a minimal + * hack to live with Daniel's (unnecessary, IMNSHO) manual swab + * optimization -- see swab.h. */ - if (usb3->fs_magic != UFS_MAGIC) { - switch (le32_to_cpup(&usb3->fs_magic)) { +#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */ + switch (usb3->fs_magic) { case UFS_MAGIC: - swab = UFS_LITTLE_ENDIAN; break; + swab = UFS_NATIVE_ENDIAN; + goto magic_found; case UFS_CIGAM: - swab = UFS_BIG_ENDIAN; break; - default: - /* - * Try another super block location - */ - if (++i < sizeof(offsets)/sizeof(unsigned)) { - ubh_brelse2(ubh); - ubh = NULL; - uspi->s_sbbase = offsets[i]; - goto again; - } - else { - printk("ufs_read_super: super block loacation not in { 0, 96, 160} or bad magic number\n"); - goto failed; - } - } + swab = UFS_SWABBED_ENDIAN; + goto magic_found; } - +#else /* bytesex perversion */ + switch (le32_to_cpup(&usb3->fs_magic)) { + case UFS_MAGIC: + swab = UFS_LITTLE_ENDIAN; + goto magic_found; + case UFS_CIGAM: + swab = UFS_BIG_ENDIAN; + goto magic_found; + } +#endif + /* + * Magic number not found -- try another super block location + */ + if (++i < sizeof(offsets)/sizeof(unsigned)) { + ubh_brelse2(ubh); + ubh = NULL; + uspi->s_sbbase = offsets[i]; + goto again; + } else { + printk("ufs_read_super: " + "super block location not in " + "{ 0, 96, 160} " + "or bad magic number\n"); + goto failed; + } + magic_found: + /* * Check block and fragment sizes */ @@ -231,23 +247,27 @@ again: uspi->s_sbsize = SWAB32(usb1->fs_sbsize); if (uspi->s_bsize != 4096 && uspi->s_bsize != 8192) { - printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", uspi->s_bsize); + printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", + uspi->s_bsize); goto failed; } if (uspi->s_fsize != 512 && uspi->s_fsize != 1024) { - printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize); - goto failed; + printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", + uspi->s_fsize); + goto failed; } /* - * Block size is not 1024, set block_size to 512, - * free buffers and read it again + * Block size is not 1024. Free buffers, set block_size and + * super_block_size to superblock-declared values, and try again. */ if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) { ubh_brelse2(ubh); ubh = NULL; uspi->s_fmask = SWAB32(usb1->fs_fmask); uspi->s_fshift = SWAB32(usb1->fs_fshift); + block_size = uspi->s_fsize; + super_block_size = uspi->s_sbsize; goto again; } @@ -255,54 +275,59 @@ again: ufs_print_super_stuff (usb1, usb2, usb3, swab); #endif /* - * Check file system type + * Check file system flavor */ flags |= UFS_VANILLA; /* XXX more consistency check */ UFSD(("ufs_read_super: maxsymlinklen 0x%8.8x\n", usb3->fs_u.fs_44.fs_maxsymlinklen)) if (usb3->fs_u.fs_44.fs_maxsymlinklen >= 0) { if (usb3->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) { - UFSD(("44BSD\n")) + UFSD(("Flavor: 44BSD\n")) flags |= UFS_44BSD; sb->s_flags |= MS_RDONLY; } else { - UFSD(("OLD\n")) + UFSD(("Flavor: OLD\n")) sb->s_flags |= UFS_OLD; /* 4.2BSD */ } } else if (uspi->s_sbbase > 0) { - UFSD(("NEXT\n")) + UFSD(("Flavor: NEXT\n")) flags |= UFS_NEXT; sb->s_flags |= MS_RDONLY; } else { - UFSD(("SUN\n")) + UFSD(("Flavor: SUN\n")) flags |= UFS_SUN; - } + } /* - * Check, if file system was correctly unmounted. + * Check whether file system was correctly unmounted. * If not, make it read only. */ - if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) || - ((flags & UFS_ST_MASK) == UFS_ST_OLD) || - ((flags & UFS_ST_MASK) == UFS_ST_NEXT) || - (((flags & UFS_ST_MASK) == UFS_ST_SUN) && - ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) { + if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) || + ((flags & UFS_ST_MASK) == UFS_ST_OLD) || + ((flags & UFS_ST_MASK) == UFS_ST_NEXT) || + (((flags & UFS_ST_MASK) == UFS_ST_SUN) && + ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) { switch(usb1->fs_clean) { - case UFS_FSCLEAN: - UFSD(("fs is clean\n")) - break; - case UFS_FSSTABLE: - UFSD(("fs is stable\n")) - break; - case UFS_FSACTIVE: + case UFS_FSACTIVE: /* 0x00 */ printk("ufs_read_super: fs is active\n"); sb->s_flags |= MS_RDONLY; break; - case UFS_FSBAD: + case UFS_FSCLEAN: /* 0x01 */ + UFSD(("ufs_read_super: fs is clean\n")) + break; + case UFS_FSSTABLE: + UFSD(("ufs_read_super: fs is stable\n")) + break; + case UFS_FSOSF1: /* 0x03 */ + /* XXX - is this the correct interpretation under DEC OSF/1? */ + printk("ufs_read_super: " + "fs is clean and stable (OSF/1)\n"); + break; + case UFS_FSBAD: /* 0xFF */ printk("ufs_read_super: fs is bad\n"); sb->s_flags |= MS_RDONLY; break; - default: + default: printk("ufs_read_super: can't grok fs_clean 0x%x\n", usb1->fs_clean); sb->s_flags |= MS_RDONLY; @@ -335,7 +360,7 @@ again: /* s_bsize already set */ /* s_fsize already set */ uspi->s_fpb = SWAB32(usb1->fs_frag); - uspi->s_minfree = SWAB32(usb1->fs_minfree); + uspi->s_minfree = SWAB32(usb1->fs_minfree); uspi->s_bmask = SWAB32(usb1->fs_bmask); uspi->s_fmask = SWAB32(usb1->fs_fmask); uspi->s_bshift = SWAB32(usb1->fs_bshift); @@ -345,7 +370,7 @@ again: /* s_sbsize already set */ uspi->s_csmask = SWAB32(usb1->fs_csmask); uspi->s_csshift = SWAB32(usb1->fs_csshift); - uspi->s_nindir = SWAB32(usb1->fs_nindir); + uspi->s_nindir = SWAB32(usb1->fs_nindir); uspi->s_inopb = SWAB32(usb1->fs_inopb); uspi->s_nspf = SWAB32(usb1->fs_nspf); uspi->s_npsect = SWAB32(usb1->fs_npsect); @@ -360,11 +385,11 @@ again: uspi->s_ipg = SWAB32(usb1->fs_ipg); uspi->s_fpg = SWAB32(usb1->fs_fpg); uspi->s_cpc = SWAB32(usb2->fs_cpc); - ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1]; + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1]; uspi->s_qbmask = SWAB64(tmp); - ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0]; - ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1]; + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1]; uspi->s_qfmask = SWAB64(tmp); uspi->s_postblformat = SWAB32(usb3->fs_postblformat); uspi->s_nrpos = SWAB32(usb3->fs_nrpos); @@ -372,7 +397,7 @@ again: uspi->s_rotbloff = SWAB32(usb3->fs_rotbloff); /* - * Compute another fraquently used values + * Compute another frequently used values */ uspi->s_fpbmask = uspi->s_fpb - 1; uspi->s_apbshift = uspi->s_bshift - 2; @@ -382,15 +407,18 @@ again: uspi->s_2apb = 1 << uspi->s_2apbshift; uspi->s_3apb = 1 << uspi->s_3apbshift; uspi->s_apbmask = uspi->s_apb - 1; - uspi->s_nspfshift = uspi->s_fshift - SECTOR_BITS; + uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS; uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift; uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift; - + + /* we could merge back s_swab and s_flags by having + foo.s_flags = flags | swab; here, and #defining + s_swab to s_flags & UFS_BYTESEX in swab.h */ sb->u.ufs_sb.s_flags = flags; sb->u.ufs_sb.s_swab = swab; sb->u.ufs_sb.s_rename_lock = 0; sb->u.ufs_sb.s_rename_wait = NULL; - + sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); /* @@ -419,6 +447,7 @@ again: /* * Read cylinder group (we read only first fragment from block * at this time) and prepare internal data structures for cg caching. + * XXX - something here fails on CDROMs from DEC! */ if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL))) goto failed; @@ -669,7 +698,7 @@ static struct file_system_type ufs_fs_type = { }; -int init_ufs_fs(void) +__initfunc(int init_ufs_fs(void)) { return(register_filesystem(&ufs_fs_type)); } diff --git a/fs/ufs/swab.h b/fs/ufs/swab.h index 534c26981..590b8f1a4 100644 --- a/fs/ufs/swab.h +++ b/fs/ufs/swab.h @@ -1,7 +1,7 @@ /* - * linux/fs/ufs/ufs_swab.h + * linux/fs/ufs/swab.h * - * Copyright (C) 1997 Francois-Rene Rideau <rideau@ens.fr> + * Copyright (C) 1997, 1998 Francois-Rene Rideau <fare@tunes.org> * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz> */ @@ -14,6 +14,29 @@ * in case there are ufs implementations that have strange bytesexes, * you'll need to modify code here as well as in ufs_super.c and ufs_fs.h * to support them. + * + * WE ALSO ASSUME A REMOTELY SANE ARCHITECTURE BYTESEX. + * We are not ready to confront insane bytesexual perversions where + * conversion to/from little/big-endian is not an involution. + * That is, we require that XeYZ_to_cpu(x) == cpu_to_XeYZ(x) + * + * NOTE that swab macros depend on a variable (or macro) swab being in + * scope and properly initialized (usually from sb->u.ufs_sb.s_swab). + * Its meaning depends on whether the architecture is sane-endian or not. + * For sane architectures, it's a flag taking values UFS_NATIVE_ENDIAN (0) + * or UFS_SWABBED_ENDIAN (1), indicating whether to swab or not. + * For pervert architectures, it's either UFS_LITTLE_ENDIAN or + * UFS_BIG_ENDIAN whose meaning you'll have to guess. + * + * It is important to keep these conventions in synch with ufs_fs.h + * and super.c. Failure to do so (initializing swab to 0 both for + * NATIVE_ENDIAN and LITTLE_ENDIAN) led to nasty crashes on big endian + * machines reading little endian UFSes. Search for "swab =" in super.c. + * + * I also suspect the whole UFS code to trust the on-disk structures + * much too much, which might lead to losing badly when mounting + * inconsistent partitions as UFS filesystems. fsck required (but of + * course, no fsck.ufs has yet to be ported from BSD to Linux as of 199808). */ #include <linux/ufs_fs.h> @@ -28,23 +51,23 @@ #define SWAB64(x) ufs_swab64(swab,x) /* - * We often use swabing, when we want to increment/decrement some value, so these - * macros might become handy and increase readability. (Daniel) + * We often use swabing, when we want to increment/decrement some value, + * so these macros might become handy and increase readability. (Daniel) */ -#define INC_SWAB16(x) x=ufs_swab16_add(swab,x,1) -#define INC_SWAB32(x) x=ufs_swab32_add(swab,x,1) -#define INC_SWAB64(x) x=ufs_swab64_add(swab,x,1) -#define DEC_SWAB16(x) x=ufs_swab16_add(swab,x,-1) -#define DEC_SWAB32(x) x=ufs_swab32_add(swab,x,-1) -#define DEC_SWAB64(x) x=ufs_swab64_add(swab,x,-1) -#define ADD_SWAB16(x,y) x=ufs_swab16_add(swab,x,y) -#define ADD_SWAB32(x,y) x=ufs_swab32_add(swab,x,y) -#define ADD_SWAB64(x,y) x=ufs_swab64_add(swab,x,y) -#define SUB_SWAB16(x,y) x=ufs_swab16_add(swab,x,-(y)) -#define SUB_SWAB32(x,y) x=ufs_swab32_add(swab,x,-(y)) -#define SUB_SWAB64(x,y) x=ufs_swab64_add(swab,x,-(y)) +#define INC_SWAB16(x) ((x)=ufs_swab16_add(swab,x,1)) +#define INC_SWAB32(x) ((x)=ufs_swab32_add(swab,x,1)) +#define INC_SWAB64(x) ((x)=ufs_swab64_add(swab,x,1)) +#define DEC_SWAB16(x) ((x)=ufs_swab16_add(swab,x,-1)) +#define DEC_SWAB32(x) ((x)=ufs_swab32_add(swab,x,-1)) +#define DEC_SWAB64(x) ((x)=ufs_swab64_add(swab,x,-1)) +#define ADD_SWAB16(x,y) ((x)=ufs_swab16_add(swab,x,y)) +#define ADD_SWAB32(x,y) ((x)=ufs_swab32_add(swab,x,y)) +#define ADD_SWAB64(x,y) ((x)=ufs_swab64_add(swab,x,y)) +#define SUB_SWAB16(x,y) ((x)=ufs_swab16_add(swab,x,-(y))) +#define SUB_SWAB32(x,y) ((x)=ufs_swab32_add(swab,x,-(y))) +#define SUB_SWAB64(x,y) ((x)=ufs_swab64_add(swab,x,-(y))) -#ifndef __PDP_ENDIAN +#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */ extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) { if (swab) return swab16(x); @@ -81,21 +104,21 @@ extern __inline__ __const__ __u64 ufs_swab64_add(unsigned swab, __u64 x, __u64 y else return x + y; } -#else /* __PDP_ENDIAN */ +#else /* bytesexual perversion -- BEWARE! Read note at top of file! */ extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) { - if (swab & UFS_LITTLE_ENDIAN) + if (swab == UFS_LITTLE_ENDIAN) return le16_to_cpu(x); else return be16_to_cpu(x); } extern __inline__ __const__ __u32 ufs_swab32(unsigned swab, __u32 x) { - if (swab & UFS_LITTLE_ENDIAN) + if (swab == UFS_LITTLE_ENDIAN) return le32_to_cpu(x); else return be32_to_cpu(x); } extern __inline__ __const__ __u64 ufs_swab64(unsigned swab, __u64 x) { - if (swab & UFS_LITTLE_ENDIAN) + if (swab == UFS_LITTLE_ENDIAN) return le64_to_cpu(x); else return be64_to_cpu(x); @@ -109,6 +132,6 @@ extern __inline__ __const__ __u32 ufs_swab32_add(unsigned swab, __u32 x, __u32 y extern __inline__ __const__ __u64 ufs_swab64_add(unsigned swab, __u64 x, __u64 y) { return ufs_swab64(swab, ufs_swab64(swab, x) + y); } -#endif /* __PDP_ENDIAN */ +#endif /* byte sexuality */ #endif /* _UFS_SWAB_H */ diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index 9d18c5f53..f1004733a 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -109,7 +109,7 @@ static int ufs_readlink (struct dentry * dentry, char * buffer, int buflen) i++; if (copy_to_user(buffer, link, i)) i = -EFAULT; - UPDATE_ATIME(inode); + UPDATE_ATIME(inode); if (bh) brelse (bh); UFSD(("ENTER\n")) diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index c6dca7358..3fec735a2 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998 * Daniel Pirkl <daniel.pirkl@email.cz> - * Charles Uiversity, Faculty of Mathematics and Physics + * Charles University, Faculty of Mathematics and Physics * * from * @@ -68,7 +68,7 @@ static int ufs_trunc_direct (struct inode * inode) struct ufs_sb_private_info * uspi; struct buffer_head * bh; u32 * p; - unsigned frag1, frag2, frag3, frag4, block1, block2; + unsigned frag1, frag2, frag3, frag4, block1, block2; unsigned frag_to_free, free_count; unsigned i, j, tmp; int retry; diff --git a/fs/ufs/util.c b/fs/ufs/util.c index a0348c6f4..527ba02aa 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998 * Daniel Pirkl <daniel.pirkl@email.cz> - * Charles Uiversity, Faculty of Mathematics and Physics + * Charles University, Faculty of Mathematics and Physics */ #include <linux/malloc.h> @@ -194,4 +194,3 @@ void _ubh_memcpyubh_(struct ufs_sb_private_info * uspi, bhno++; } } - diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 8f5e66727..7443f9fc9 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -11,7 +11,7 @@ /* - * some usefull marcos + * some useful macros */ #define in_range(b,first,len) ((b)>=(first)&&(b)<(first)+(len)) #define howmany(x,y) (((x)+(y)-1)/(y)) @@ -28,7 +28,7 @@ : (usb3)->fs_u.fs_44.fs_state /* 4.4BSD way */) /* - * namlen, it's format depends of flags + * namlen, its format depends of flags */ #define ufs_namlen(de) _ufs_namlen_(de,flags,swab) static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsigned swab) { @@ -43,7 +43,7 @@ static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsi * Here is how the uid is computed: * if the file system is 4.2BSD, get it from oldids. * if it has sun extension and oldids is USEEFT, get it from ui_sun. - * if it is 4.4 or Hurd, get it from ui_44 (which is the same as ui_hurd). + * if it is 4.4 or Hurd, get it from ui_44 (which is the same as from ui_hurd). */ #define ufs_uid(inode) _ufs_uid_(inode,flags,swab) static inline __u32 _ufs_uid_(struct ufs_inode * inode, unsigned flags, unsigned swab) { @@ -72,13 +72,13 @@ static inline __u32 _ufs_gid_(struct ufs_inode * inode, unsigned flags, unsigned } /* - * marcros used for retyping + * macros used to avoid needless retyping */ #define UCPI_UBH ((struct ufs_buffer_head *)ucpi) #define USPI_UBH ((struct ufs_buffer_head *)uspi) /* - * This functions manipulate with ufs_buffers + * These functions manipulate ufs buffers */ #define ubh_bread(dev,fragment,size) _ubh_bread_(uspi,dev,fragment,size) extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); @@ -106,11 +106,11 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head #define ubh_get_usb_second(ubh) \ ((struct ufs_super_block_second *)(ubh)-> \ - bh[SECTOR_SIZE >> uspi->s_fshift]->b_data + (SECTOR_SIZE & ~uspi->s_fmask)) + bh[UFS_SECTOR_SIZE >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE & ~uspi->s_fmask)) #define ubh_get_usb_third(ubh) \ ((struct ufs_super_block_third *)((ubh)-> \ - bh[SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (SECTOR_SIZE*2 & ~uspi->s_fmask))) + bh[UFS_SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE*2 & ~uspi->s_fmask))) #define ubh_get_ucg(ubh) \ ((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data)) @@ -160,7 +160,7 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head SWAB32((usb)->fs_cstotal.cs_nffree) - (uspi->s_dsize * (percentreserved) / 100)) /* - * Macros for access to cylinder group array structures + * Macros to access cylinder group array structures */ #define ubh_cg_blktot(ucpi,cylno) \ (*((__u32*)ubh_get_addr(UCPI_UBH, (ucpi)->c_btotoff + ((cylno) << 2)))) @@ -170,12 +170,12 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head (ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 )))) /* - * Bitmap operation - * This functions work like classical bitmap operations. The diference - * is that we havn't the whole bitmap in one continuous part of memory, - * but in a few buffers. - * The parameter of each function is super_block, ufs_buffer_head and - * position of the begining of the bitmap. + * Bitmap operations + * These functions work like classical bitmap operations. + * The difference is that we don't have the whole bitmap + * in one contiguous chunk of memory, but in several buffers. + * The parameters of each function are super_block, ufs_buffer_head and + * position of the beginning of the bitmap. */ #define ubh_setbit(ubh,begin,bit) \ (*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) |= (1 << ((bit) & 7))) @@ -188,8 +188,11 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head #define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit)) -#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0) -#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset) +#define ubh_find_first_zero_bit(ubh,begin,size) \ + _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0) +#define ubh_find_next_zero_bit(ubh,begin,size,offset) \ + _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset) + static inline unsigned _ubh_find_next_zero_bit_( struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned size, unsigned offset) @@ -213,14 +216,17 @@ static inline unsigned _ubh_find_next_zero_bit_( return (base << (uspi->s_fshift + 3)) + offset - begin; } -#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block)) -#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block) +#define ubh_isblockclear(ubh,begin,block) \ + (!_ubh_isblockset_(uspi,ubh,begin,block)) +#define ubh_isblockset(ubh,begin,block) \ + _ubh_isblockset_(uspi,ubh,begin,block) + static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned block) { switch (uspi->s_fpb) { case 8: - return (*ubh_get_addr (ubh, begin + block) == 0xff); + return (*ubh_get_addr (ubh, begin + block) == 0xff); case 4: return (*ubh_get_addr (ubh, begin + (block >> 1)) == (0x0f << ((block & 0x01) << 2))); case 2: @@ -237,8 +243,8 @@ static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi, { switch (uspi->s_fpb) { case 8: - *ubh_get_addr (ubh, begin + block) = 0x00; - return; + *ubh_get_addr (ubh, begin + block) = 0x00; + return; case 4: *ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2)); return; @@ -257,8 +263,8 @@ static inline void _ubh_setblock_(struct ufs_sb_private_info * uspi, { switch (uspi->s_fpb) { case 8: - *ubh_get_addr(ubh, begin + block) = 0xff; - return; + *ubh_get_addr(ubh, begin + block) = 0xff; + return; case 4: *ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2)); return; @@ -295,7 +301,8 @@ static inline void ufs_fragacct (struct super_block * sb, unsigned blockmap, ADD_SWAB32(fraglist[fragsize], cnt); } -#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask) +#define ubh_scanc(ubh,begin,size,table,mask) \ + _ubh_scanc_(uspi,ubh,begin,size,table,mask) static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned size, unsigned char * table, unsigned char mask) { diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index 000fb698f..f4c8dfb53 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -1,17 +1,15 @@ # -# Makefile for the umsdos unix-like filesystem routines. +# Makefile for the umsdos Unix-like 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 .c file). +# unless it's something special (not a .c file). # -# Note 2! The CFLAGS definitions are now in the main makefile... +# 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 \ - rdir.o symlink.o emd.o - -#check.o + rdir.o symlink.o emd.o check.o M_OBJS := $(O_TARGET) diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt index 05d3001c5..f63a1144f 100644 --- a/fs/umsdos/README-WIP.txt +++ b/fs/umsdos/README-WIP.txt @@ -1,45 +1,50 @@ Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing (started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>) ---------- WARNING --------- WARNING --------- WARNING ----------- -THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY -YELLING, BUT ANY USE OF THIS MODULES MAY VERY WELL DESTROY YOUR +---------- WARNING --------- WARNING --------- WARNING ----------- +THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY +YELLING, BUT ANY USE OF THIS MODULE MAY VERY WELL DESTROY YOUR UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE. YOU'VE BEEN WARNED. ---------- WARNING --------- WARNING --------- WARNING ----------- +---------- WARNING --------- WARNING --------- WARNING ----------- -Current status (980428) - UMSDOS dentry-WIP-Beta 0.82-4: +Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7: (1) pure MSDOS (no --linux-.--- EMD file): -- readdir - works -- lookup - works -- read file - works ++ readdir - works ++ lookup - works ++ read file - works -- creat file - works -- write file - works -- mkdir - works -- rmdir - QUESTIONABLE. probable problem on non-empty dirs. ++ creat file - works ++ delete file - works ++ write file - works ++ rename file (same dir) - works ++ rename file (dif. dir) - works ++ rename dir (same dir) - works ++ rename dir (dif. dir) - works ++ mkdir - works +- rmdir - QUESTIONABLE. probable problem on non-empty dirs. Notes: possible very minor problems with dentry/inode/... kernel structures (very rare) (2) umsdos (with --linux-.--- EMD file): -- readdir - works. -- lookup - works. -- permissions/owners stuff - works -- long file names - works -- read file - works -- switching MSDOS/UMSDOS - works? -- switching UMSDOS/MSDOS - works? -- pseudo root things - COMPLETELY UNTESTED -- resolve symlink - seems to work fully now! -- dereference symlink - seems to work fully now! -- hard links - seems to work now -- special files (block/char device, fifos, sockets...) - seems to work ok. -- other ioctls - some UNTESTED -- dangling symlink - UNTESTED ! ++ readdir - works ++ lookup - works ++ permissions/owners stuff - works ++ long file names - works ++ read file - works +- switching MSDOS/UMSDOS - works? +- switching UMSDOS/MSDOS - works? +- pseudo root things - COMPLETELY UNTESTED (commented out currently!) ++ resolve symlink - works ++ dereference symlink - works +- hard links - broken again... ++ special files (block/char devices, FIFOs, sockets...) - seems to work. +- other ioctls - some UNTESTED ++ dangling symlink - works - create symlink - seems to work both on short & long names now ! - create hardlink - WARNING: NOT FIXED YET! @@ -57,65 +62,62 @@ Notes: possible very minor problems with dentry/inode/... kernel structures (ver - rmdir - may work, but readdir blocks linux afterwards. to be FIXED! - umssyncing - seems to work, but NEEDS MORE TESTING -- CVF-FAT stuff (compressed DOS filesystem) - there is some support from - Frank Gockel <gockel@sent13.uni-duisburg.de> to use it even under - umsdosfs. But I have no way of testing it -- please let me know if there - are problems that are specific to umsdos (eg. it works under msdosfs, but - not under umsdosfs) ++ CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank + Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I + have no way of testing it -- please let me know if there are problems specific + to umsdos (for instance, it works under msdosfs, but not under umsdosfs). -Notes: moderate dentry/inode kernel structures trashing. Probably some other -kernel structures compromised. Have SysRq support compiled in, and use -Sync/Emergency-remount-RO. And if you don't try mounting read/write - -you should have no big problems... +Notes: there is moderate trashing of dentry/inode kernel structures. Probably +some other kernel structures are compromised. You should have SysRq support +compiled in, and use Sync/Emergency-remount-RO. If you don't try mounting +read/write, you should have no big problems. When most things begin to work, +I'll get to finding/fixing those inode/dentry ?_count leakages. -Notes2: kernel structures trashing seems to be _MUCH_ lower if no -symlinks/hardlink present. (hardlinks seem to be biggest problem) +Note 4: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It may be +some error with dir->i_count++, or something related to iput(). See if +number changes if we access the directory in different ways. -Notes3: Notes2 is probably somewhat outdated now that hardlink/symlink stuff -is supposed to be fixed enough to work, but I haven't got the time to test -it. +Note 5: there is a problem with unmounting umsdosfs. It seems to stay +registered or something. Remounting the same device on any mount point with a +different fstype (such as msdos or vfat) ignores the new fstype and umsdosfs +kicks back in. Should be fixed in 0.82-6 (at least, with nothing in between)! +Much of inode/dentry corruption is fixed in 0.82-7, especially when mounting +read-only. -Note5: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It must be -some error with dir->i_count++, or something related to iput() ? See if -number changes if we access the dir in different ways.. - -Note6: there is problem with unmounting umsdosfs, it seems to stay -registered or something. Remounting same device on any mount point with -different fstype (like msdos or vfat) ignores fstype and umsdosfs kicks back -in. - -Note7: also we screwed umount(2)-ing the fs at times (EBUSY), by removing -all those iput/dput's. When rest of code is fixed, we'll put them back at -(hopefully) correct places. +Note 6: also we screwed umount(2)-ing the fs at times (EBUSY), by missing +some of those iput/dput's. When most of the rest of the code is fixed, we'll +put them back at the correct places (hopefully). Also much better in 0.82-7. ------------------------------------------------------------------------------ Some general notes: -There is great amount of kernel log messages. Use SysRq log-level 5 to turn -most of them off, or 4 to turn all but really fatal ones off. Probably good -idea to kill klogd/syslogd so it will only go to console. +There is a great amount of kernel log messages. Use SysRq log-level 5 to turn +most of them off, or 4 to turn all but really fatal ones off. Probably good +idea to kill klogd/syslogd so it will only go to console. You can also +comment out include/linux/umsdos_fs.h definition of UMS_DEBUG to get rid of +most debugging messages. Please don't turn it off without good reason. -It should work enough to test it, even enough to give you few chances to +It should work enough to test it, even enough to give you a few chances to umount/rmmod module, recompile it, and reinsert it again (but probably -screaming about still used inodes on device and stuff). This is first on +screaming about still used inodes on device and stuff). This is first on my list of things to get fixed, as it would greatly improve speed of compile/test/reboot/set_environment/recompile cycle by removing -'reboot/set_environment' component that now occures every few cycles. +'reboot/set_environment' component that now occurs every few cycles. -But I need some help from someone knowing about dentries/inodes use more -than I. If you can help, please contact me... I'm mostly worried about +I need some help from someone who knows more than I about the use of dentries +and inodes. If you can help, please contact me. I'm mostly worried about iget/iput and dget/dput, and deallocating temporary dentries we create. -should we destroy temp dentries ? using d_invalidate ? using d_drop ? just -dput them ? +Should we destroy temp dentries? using d_invalidate? using d_drop? just +dput them? -I'm unfortunatelly somewhat out of time to read linux-kernel, but I do check -for any messages having UMSDOS in subject, and read them. I might miss it in -all that volume, though. I should reply to any direct Email in few days. If -I don't - probably I never got your message. You can try mnalis@open.hr or -mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable one. +I'm unfortunately somewhat out of time to read linux-kernel, but I do check +for messages having "UMSDOS" in the subject, and read them. I might miss +some in all that volume, though. I should reply to any direct e-mail in few +days. If I don't, probably I never got your message. You can try +mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable. ------------------------------------------------------------------------------ @@ -126,34 +128,33 @@ some of my notes for myself /mn/: - iput: device 00:00 inode 318 still has aliases! problem. Check in iput() for device 0,0. Probably null pointer passed around when it shouldn't be ? - dput/iput problem... -- what about .dotfiles ? working ? multiple dots ? etc.... -- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular? +- What about .dotfiles? Are they working? How about multiple dots? +- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and similar? + +- should check what happen when multiple UMSDOSFS are mounted - chase down all "FIXME", "DELME", "CNT", check_dentry, check_inode, kill_dentry and fix it properly. -- umsdos_create_any - calling msdos_create will create dentry for shor name. Hmmmm..? -- kill_dentry - put it where is needed. Also dput() at needed places. - -- when should dput()/iput() be used ?!! +- umsdos_create_any - calling msdos_create will create dentry for short name. Hmmmm..? - what is dir->i_count++ ? locking directory ? should this be lock_parent or something ? -- i_binary=2 is for CVF (compressed filesystem). ++ i_binary=2 is for CVF (compressed filesystem). - SECURITY WARNING: short dentries should be invalidated, or they could be accessed instead of proper long names. -- I've put many check_dentry() calls to trace down problems. those should be - removed in final version. +- I've put many check_dentry_path(), check_inode() calls to trace down + problems. those should be removed in final version. -- iput()s with "FIXME?" comment are uncomented and probably ok. Those with - "FIXME??" should be tested but prolly work. Commented iput()s with +- iput()s with a "FIXME?" comment are uncommented and probably OK. Those with + "FIXME??" should be tested but probably work. Commented iput()s with any "FIXME" comments should probably be uncommented and tested. At some places we may need dput() instead of iput(), but that should be checked. -- as for iput() : (my only pointer so far. anyone else ?) ++ as for iput(): (my only pointer so far. anyone else?) >development I only know about iput. All functions that get an inode as >argument and don't return it have to call iput on it before exit, i.e. when diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index 717a0df39..a97f1cb91 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -1,12 +1,11 @@ /* * linux/fs/umsdos/check.c * - * + * Sanity-checking code */ #include <linux/signal.h> #include <linux/sched.h> -#include <linux/head.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> @@ -17,6 +16,7 @@ #include <asm/system.h> +#ifdef CHECK_PAGE_TABLES static int check_one_table (struct pde *page_dir) { if (pgd_none (*page_dir)) @@ -51,6 +51,184 @@ void check_page_tables (void) } } if (err) - printk ("\nErreur MM %d\n", err); + printk ("\nError MM %d\n", err); + } +} +#endif + + +#if UMS_DEBUG +/* + * check a superblock + */ + +void check_sb (struct super_block *sb, const char c) +{ + if (sb) { + Printk ((" (has %c_sb=%d, %d)", + c, MAJOR (sb->s_dev), MINOR (sb->s_dev))); + } else { + Printk ((" (%c_sb is NULL)", c)); + } +} + +/* + * check an inode + */ + +void check_inode (struct inode *inode) +{ + if (inode) { + Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)", + inode->i_ino, inode->i_count)); + check_sb (inode->i_sb, 'i'); + + if (inode->i_dentry.next) { /* FIXME: does this work ? */ + Printk ((" (has i_dentry)")); + } else { + Printk ((" (NO i_dentry)")); + } + + 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")); + } +} + +/* + * checks all inode->i_dentry + * + */ +void checkd_inode (struct inode *inode) +{ + struct dentry *ret; + struct list_head *cur; + int count = 0; + if (!inode) { + printk (KERN_ERR "checkd_inode: inode is NULL!\n"); + return; + } + + Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino)); + cur = inode->i_dentry.next; + while (count++ < 10) { + PRINTK (("1...")); + if (!cur) { + Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n")); + return; + } + PRINTK (("2...")); + ret = list_entry (cur, struct dentry, d_alias); + PRINTK (("3...")); + if (cur == cur->next) { + Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n")); + return; + } + PRINTK (("4...")); + if (!ret) { + Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n")); + return; + } + PRINTK (("5... (ret=%p)...", ret)); + PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name))); + PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len)); + PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name)); + Printk ((KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name)); + PRINTK (("6...")); + cur = cur->next; + PRINTK (("7...")); +#if 1 + Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n")); + return; +#endif + } + Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n")); + return; +} + +/* + * internal part of check_dentry. does the real job. + * + */ + +void check_dent_int (struct dentry *dentry, int parent) +{ + if (parent) { + Printk ((KERN_DEBUG "* parent(%d) dentry: %.*s\n", + parent, (int) dentry->d_name.len, dentry->d_name.name)); + } else { + Printk ((KERN_DEBUG "* checking dentry: %.*s\n", + (int) dentry->d_name.len, dentry->d_name.name)); + } + check_inode (dentry->d_inode); + Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count)); + check_sb (dentry->d_sb, 'd'); + if (dentry->d_op == NULL) { + Printk ((" (d_op is NULL)\n")); + } else { + Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op)); } } + +/* + * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops. + * + */ + +void check_dentry_path (struct dentry *dentry, const char *desc) +{ + int count=0; + Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc)); + + if (!dentry) { + Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n")); + return; + } + if (IS_ERR(dentry)) { + Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n", + PTR_ERR(dentry))); + return; + } + + while (dentry && count < 10) { + check_dent_int (dentry, count++); + if (dentry == dentry->d_parent) { + Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n")); + break; + } + dentry = dentry->d_parent; + } + + if (count >= 10) { /* if infinite loop detected */ + Printk ((KERN_ERR + "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n")); + } + + if (!dentry) { + Printk ((KERN_ERR + "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n")); + } +} +#else +void check_sb (struct super_block *sb, const char c) {}; +void check_inode (struct inode *inode) {}; +void checkd_inode (struct inode *inode) {}; +void check_dentry_path (struct dentry *dentry, const char *desc) {}; +#endif /* UMS_DEBUG */ + diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 3f49d0c2e..5a7bcd854 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -19,71 +19,45 @@ #include <asm/uaccess.h> -#define PRINTK(x) -#define Printk(x) printk x - #define UMSDOS_SPECIAL_DIRFPOS 3 extern struct inode *pseudo_root; -/* P.T.Waltenberg - * I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS - * uses. It's easier to do once than hack all the other instances. Probably safer as well - */ - -/* FIXME: it returns inode with i_count of 0. this should be redesigned to return dentry instead, - and correct dentry (with correct d_parent) */ -int compat_umsdos_real_lookup (struct inode *dir, const char *name, int len, struct inode **inode) +/* + * This needs to have the parent dentry passed to it. + * N.B. Try to get rid of this soon! + */ +int compat_msdos_create (struct inode *dir, const char *name, int len, + int mode, struct inode **inode) { - int rv; - struct dentry *dentry; - unsigned long ino; + int ret; + struct dentry *dentry, *d_dir; - Printk ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: start\n")); check_inode (dir); - dentry = creat_dentry (name, len, NULL, NULL); - rv = umsdos_real_lookup (dir, dentry); - iput (dir); /* should be here, because umsdos_real_lookup does inc_count(dir) */ - - if (rv) { - Printk ((KERN_WARNING "compat_umsdos_real_lookup failed with %d\n", rv)); - return rv; - } - - if (!inode) { - Printk ((KERN_ERR "inode should be set here. Arrgh! segfaulting...\n")); + ret = -ENOMEM; + d_dir = geti_dentry (dir); + if (!d_dir) { +printk(KERN_ERR "compat_msdos_create: flaky i_dentry didn't work\n"); + goto out; } - - ino = dentry->d_inode->i_ino; - *inode = dentry->d_inode; - - dput (dentry); /* we are done with it: FIXME: does this work /mn/ ? */ - - check_dentry (dentry); - check_inode (dir); - - Printk ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: end\n")); - - return rv; -} - - -int compat_msdos_create (struct inode *dir, const char *name, int len, int mode, struct inode **inode) -{ - int rv; - struct dentry *dentry; - - check_inode (dir); - dentry = creat_dentry (name, len, NULL, NULL); - check_dentry (dentry); - rv = msdos_create (dir, dentry, mode); - check_dentry (dentry); + dget(d_dir); + dentry = creat_dentry (name, len, NULL, d_dir); + dput(d_dir); + if (!dentry) + goto out; + + check_dentry_path (dentry, "compat_msdos_create START"); + ret = msdos_create (dir, dentry, mode); + check_dentry_path (dentry, "compat_msdos_create END"); + if (ret) + goto out; if (inode != NULL) *inode = dentry->d_inode; check_inode (dir); - return rv; +out: + return ret; } @@ -91,7 +65,7 @@ int compat_msdos_create (struct inode *dir, const char *name, int len, int mode, * So grep * doesn't complain in the presence of directories. */ -int UMSDOS_dir_read (struct file *filp, char *buff, size_t size, loff_t *count) +int dummy_dir_read (struct file *filp, char *buff, size_t size, loff_t *count) { return -EISDIR; } @@ -120,7 +94,8 @@ static int umsdos_dir_once ( void *buf, struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf; if (d->count == 0) { - PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", dentry->d_len, dentry->d_name, offset)); + PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", + len, name, offset)); ret = d->filldir (d->dirbuf, name, len, offset, ino); d->stop = ret < 0; d->count = 1; @@ -132,32 +107,29 @@ static int umsdos_dir_once ( void *buf, /* * Read count directory entries from directory filp * Return a negative value from linux/errno.h. - * Return > 0 if success (The amount of byte written by filldir). + * Return > 0 if success (the number of bytes written by filldir). * * This function is used by the normal readdir VFS entry point and by * some function who try to find out info on a file from a pure MSDOS * inode. See umsdos_locate_ancestor() below. */ -static int umsdos_readdir_x ( - struct inode *dir, /* Point to a description of the super block */ - struct file *filp, /* Point to a directory which is read */ - void *dirbuf, /* Will hold count directory entry */ - /* but filled by the filldir function */ - int internal_read, /* Called for internal purpose */ - struct umsdos_dirent *u_entry, /* Optional umsdos entry */ - int follow_hlink, - filldir_t filldir) +static int umsdos_readdir_x (struct inode *dir, struct file *filp, + void *dirbuf, int internal_read, + struct umsdos_dirent *u_entry, + int follow_hlink, filldir_t filldir) { + struct dentry *demd; + off_t start_fpos; int ret = 0; + struct file new_filp; umsdos_startlookup (dir); - if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS - && pseudo_root - && dir == pseudo_root - && !internal_read) { - Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n")); + if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS && + dir == pseudo_root && !internal_read) { + +Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n")); /* * We don't need to simulate this pseudo directory * when umsdos_readdir_x is called for internal operation @@ -168,55 +140,16 @@ static int umsdos_readdir_x ( * linux root), it simulate a directory /DOS which points to * the real root of the file system. */ - if (filldir (dirbuf, "DOS", 3, UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) { + if (filldir (dirbuf, "DOS", 3, + UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) { filp->f_pos++; } - } else if (filp->f_pos < 2 || (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) { - - /* FIXME: that was in 2.0.x: else if (filp->f_pos < 2 || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)) - * I'm probably screwing up pseudo-root and stuff with this. It needs proper fix. - */ - + goto out_end; + } + + if (filp->f_pos < 2 || + (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) { - /* #Specification: readdir / . and .. - * The msdos filesystem manage the . and .. entry properly - * so the EMD file won't hold any info about it. - * - * In readdir, we assume that for the root directory - * the read position will be 0 for ".", 1 for "..". For - * a non root directory, the read position will be 0 for "." - * and 32 for "..". - */ - /* - * This is a trick used by the msdos file system (fs/msdos/dir.c) - * to manage . and .. for the root directory of a file system. - * Since there is no such entry in the root, fs/msdos/dir.c - * use the following: - * - * if f_pos == 0, return ".". - * if f_pos == 1, return "..". - * - * So let msdos handle it - * - * Since umsdos entries are much larger, we share the same f_pos. - * if f_pos is 0 or 1 or 32, we are clearly looking at . and - * .. - * - * As soon as we get f_pos == 2 or f_pos == 64, then back to - * 0, but this time we are reading the EMD file. - * - * Well, not so true. The problem, is that UMSDOS_REC_SIZE is - * also 64, so as soon as we read the first record in the - * EMD, we are back at offset 64. So we set the offset - * to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the - * .. entry from msdos. - * - * Now (linux 1.3), umsdos_readdir can read more than one - * entry even if we limit (umsdos_dir_once) to only one: - * It skips over hidden file. So we switch to - * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully - * the .. entry. - */ int last_f_pos = filp->f_pos; struct UMSDOS_DIR_ONCE bufk; @@ -231,134 +164,127 @@ static int umsdos_readdir_x ( filp->f_pos = UMSDOS_SPECIAL_DIRFPOS; if (u_entry != NULL) u_entry->flags = 0; - } else { - struct inode *emd_dir; + goto out_end; + } + + Printk (("umsdos_readdir_x: normal file /mn/?\n")); + + /* get the EMD dentry */ + demd = umsdos_get_emd_dentry(filp->f_dentry); + ret = PTR_ERR(demd); + if (IS_ERR(demd)) + goto out_end; + ret = 0; + if (!demd->d_inode) + goto out_dput; + + /* set up our private filp ... */ + fill_new_filp(&new_filp, demd); + new_filp.f_pos = filp->f_pos; + start_fpos = filp->f_pos; + + if (new_filp.f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1) + new_filp.f_pos = 0; +Printk (("f_pos %Ld i_size %ld\n", new_filp.f_pos, demd->d_inode->i_size)); + ret = 0; + while (new_filp.f_pos < demd->d_inode->i_size) { + off_t cur_f_pos = new_filp.f_pos; + struct umsdos_info info; + struct dentry *dret; + struct umsdos_dirent entry; - Printk (("umsdos_readdir_x: normal file /mn/?\n")); - emd_dir = umsdos_emd_dir_lookup (dir, 0); - if (emd_dir != NULL) { - off_t start_fpos = filp->f_pos; + ret = -EIO; + if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0) + break; - Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n", emd_dir->i_ino)); - if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1) - filp->f_pos = 0; - Printk (("f_pos %Ld i_size %ld\n", filp->f_pos, emd_dir->i_size)); - ret = 0; - while (filp->f_pos < emd_dir->i_size) { - struct umsdos_dirent entry; - off_t cur_f_pos = filp->f_pos; - - if (umsdos_emd_dir_readentry (emd_dir, filp, &entry) != 0) { - ret = -EIO; - break; - } else if (entry.name_len != 0) { - /* #Specification: umsdos / readdir - * umsdos_readdir() should fill a struct dirent with - * an inode number. The cheap way to get it is to - * do a lookup in the MSDOS directory for each - * entry processed by the readdir() function. - * This is not very efficient, but very simple. The - * other way around is to maintain a copy of the inode - * number in the EMD file. This is a problem because - * this has to be maintained in sync using tricks. - * Remember that MSDOS (the OS) does not update the - * modification time (mtime) of a directory. There is - * no easy way to tell that a directory was modified - * during a DOS session and synchronise the EMD file. - * - * Suggestion welcome. - * - * So the easy way is used! - */ - struct umsdos_info info; - struct inode *inode; - - int lret; - - umsdos_parse (entry.name, entry.name_len, &info); - info.f_pos = cur_f_pos; - umsdos_manglename (&info); - lret = compat_umsdos_real_lookup (dir, info.fake.fname, info.fake.len, &inode); - Printk (("Cherche inode de %s lret %d flags %d\n", info.fake.fname, lret, entry.flags)); - if (lret == 0 - && (entry.flags & UMSDOS_HLINK) - && follow_hlink) { - struct inode *rinode; - - Printk ((KERN_DEBUG "umsdos_hlink2inode now\n")); - lret = umsdos_hlink2inode (inode, &rinode); - inode = rinode; - } - if (lret == 0) { - /* #Specification: pseudo root / reading real root - * The pseudo root (/linux) is logically - * erased from the real root. This mean that - * ls /DOS, won't show "linux". This avoids - * infinite recursion /DOS/linux/DOS/linux while - * walking the file system. - */ - if (inode != pseudo_root - && (internal_read - || !(entry.flags & UMSDOS_HIDDEN))) { - Printk ((KERN_DEBUG "filldir now\n")); - if (filldir (dirbuf, entry.name, entry.name_len, cur_f_pos, inode->i_ino) < 0) { - filp->f_pos = cur_f_pos; - } - Printk (("Trouve ino %ld ", inode->i_ino)); - if (u_entry != NULL) - *u_entry = entry; - iput (inode); /* FIXME? */ - break; - } - Printk ((KERN_DEBUG " dir.c:Putting inode %lu with i_count=%d\n", inode->i_ino, inode->i_count)); - iput (inode); /* FIXME? */ - } else { - /* #Specification: umsdos / readdir / not in MSDOS - * During a readdir operation, if the file is not - * in the MSDOS directory anymore, the entry is - * removed from the EMD file silently. - */ - Printk (("'Silently' removing EMD for file\n")); - ret = umsdos_writeentry (dir, emd_dir, &info, 1); - if (ret != 0) { - break; - } - } - } + if (entry.name_len == 0) + goto remove_name; + + umsdos_parse (entry.name, entry.name_len, &info); + info.f_pos = cur_f_pos; + umsdos_manglename (&info); + dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname, + info.fake.len); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + break; + +Printk (("Looking for inode of %s/%s, flags=%x\n", +dret->d_parent->d_name.name, info.fake.fname, entry.flags)); + if ((entry.flags & UMSDOS_HLINK) && follow_hlink) { + dret = umsdos_solve_hlink (dret); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + break; + } + + /* #Specification: pseudo root / reading real root + * The pseudo root (/linux) is logically + * erased from the real root. This means that + * ls /DOS, won't show "linux". This avoids + * infinite recursion (/DOS/linux/DOS/linux/...) while + * walking the file system. + */ + if (dret->d_inode != pseudo_root && + (internal_read || !(entry.flags & UMSDOS_HIDDEN))) { + Printk ((KERN_DEBUG "filldir now\n")); + if (filldir (dirbuf, entry.name, entry.name_len, + cur_f_pos, dret->d_inode->i_ino) < 0) { + new_filp.f_pos = cur_f_pos; } - /* - * If the fillbuf has failed, f_pos is back to 0. - * To avoid getting back into the . and .. state - * (see comments at the beginning), we put back - * the special offset. - */ - if (filp->f_pos == 0) - filp->f_pos = start_fpos; - Printk ((KERN_DEBUG " dir.c:Putting emd_dir %lu with i_count=%d\n", emd_dir->i_ino, emd_dir->i_count)); - iput (emd_dir); /* FIXME? */ +Printk (("Found %s/%s(%ld)\n", +dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino)); + if (u_entry != NULL) + *u_entry = entry; + dput(dret); + break; } + dput(dret); + continue; + + remove_name: + /* #Specification: umsdos / readdir / not in MSDOS + * During a readdir operation, if the file is not + * in the MS-DOS directory any more, the entry is + * removed from the EMD file silently. + */ + Printk (("'Silently' removing EMD for file\n")); + ret = umsdos_delentry(filp->f_dentry, &info, 1); + if (ret) + break; + continue; } + /* + * If the fillbuf has failed, f_pos is back to 0. + * To avoid getting back into the . and .. state + * (see comments at the beginning), we put back + * the special offset. + */ + filp->f_pos = new_filp.f_pos; + if (filp->f_pos == 0) + filp->f_pos = start_fpos; +out_dput: + dput(demd); + +out_end: umsdos_endlookup (dir); - Printk (("read dir %p pos %Ld ret %d\n", dir, filp->f_pos, ret)); + Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n", + dir, filp->f_pos, ret)); return ret; } /* - * Read count directory entries from directory filp + * Read count directory entries from directory filp. * Return a negative value from linux/errno.h. - * Return 0 or positive if successful + * Return 0 or positive if successful. */ -static int UMSDOS_readdir ( - struct file *filp, /* Point to a directory which is read */ - void *dirbuf, /* Will hold directory entries */ - filldir_t filldir) +static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir) { struct inode *dir = filp->f_dentry->d_inode; - int ret = 0; - int count = 0; + int ret = 0, count = 0; struct UMSDOS_DIR_ONCE bufk; bufk.dirbuf = dirbuf; @@ -370,148 +296,112 @@ static int UMSDOS_readdir ( struct umsdos_dirent entry; bufk.count = 0; - Printk (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once)); - ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once); + PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", + dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once)); + ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, + umsdos_dir_once); if (bufk.count == 0) break; count += bufk.count; } - Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", ret, count, filp->f_pos)); + Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", + ret, count, filp->f_pos)); return count ? : ret; } /* - * Complete the inode content with info from the EMD file + * Complete the inode content with info from the EMD file. + * + * This function modifies the state of a dir inode. It decides + * whether the dir is a UMSDOS or DOS directory. This is done + * deeper in umsdos_patch_inode() called at the end of this function. + * + * Because it is does disk access, umsdos_patch_inode() may block. + * At the same time, another process may get here to initialise + * the same directory inode. There are three cases. + * + * 1) The inode is already initialised. We do nothing. + * 2) The inode is not initialised. We lock access and do it. + * 3) Like 2 but another process has locked the inode, so we try + * to lock it and check right afterward check whether + * initialisation is still needed. + * + * + * Thanks to the "mem" option of the kernel command line, it was + * possible to consistently reproduce this problem by limiting + * my memory to 4 MB and running X. + * + * Do this only if the inode is freshly read, because we will lose + * the current (updated) content. + * + * A lookup of a mount point directory yield the inode into + * the other fs, so we don't care about initialising it. iget() + * does this automatically. */ -void umsdos_lookup_patch ( - struct inode *dir, - struct inode *inode, - struct umsdos_dirent *entry, - off_t emd_pos) +void umsdos_lookup_patch (struct inode *dir, struct inode *inode, + struct umsdos_dirent *entry, off_t emd_pos) { - /* - * This function modify the state of a dir inode. It decides - * if the dir is a umsdos dir or a dos dir. This is done - * deeper in umsdos_patch_inode() called at the end of this function. - * - * umsdos_patch_inode() may block because it is doing disk access. - * At the same time, another process may get here to initialise - * the same dir inode. There is 3 cases. - * - * 1-The inode is already initialised. We do nothing. - * 2-The inode is not initialised. We lock access and do it. - * 3-Like 2 but another process has lock the inode, so we try - * to lock it and right after check if initialisation is still - * needed. - * - * - * Thanks to the mem option of the kernel command line, it was - * possible to consistently reproduce this problem by limiting - * my mem to 4 meg and running X. - */ - /* - * Do this only if the inode is freshly read, because we will lose - * the current (updated) content. - */ - /* - * A lookup of a mount point directory yield the inode into - * the other fs, so we don't care about initialising it. iget() - * does this automatically. + if (inode->i_sb != dir->i_sb) + goto out; + if (umsdos_isinit (inode)) + goto out; + + if (S_ISDIR (inode->i_mode)) + umsdos_lockcreate (inode); + if (umsdos_isinit (inode)) + goto out_unlock; + + if (S_ISREG (entry->mode)) + entry->mtime = inode->i_mtime; + inode->i_mode = entry->mode; + inode->i_rdev = to_kdev_t (entry->rdev); + inode->i_atime = entry->atime; + inode->i_ctime = entry->ctime; + inode->i_mtime = entry->mtime; + 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 + * MSDOS is already figuring out what to do for directories and + * does nothing for other files. For MSDOS, there are no hard links + * so all file carry nlink==1. UMSDOS use some info in the + * EMD file to plug the correct value. */ - - if (inode->i_sb == dir->i_sb && !umsdos_isinit (inode)) { - if (S_ISDIR (inode->i_mode)) - umsdos_lockcreate (inode); - if (!umsdos_isinit (inode)) { - /* #Specification: umsdos / lookup / inode info - * After successfully reading an inode from the MSDOS - * filesystem, we use the EMD file to complete it. - * We update the following field. - * - * uid, gid, atime, ctime, mtime, mode. - * - * We rely on MSDOS for mtime. If the file - * was modified during an MSDOS session, at least - * mtime will be meaningful. We do this only for regular - * file. - * - * We don't rely on MSDOS for mtime for directory because - * the MSDOS directory date is creation time (strange - * MSDOS behavior) which fit nowhere in the three UNIX - * time stamp. - */ - if (S_ISREG (entry->mode)) - entry->mtime = inode->i_mtime; - inode->i_mode = entry->mode; - inode->i_rdev = to_kdev_t (entry->rdev); - inode->i_atime = entry->atime; - inode->i_ctime = entry->ctime; - inode->i_mtime = entry->mtime; - inode->i_uid = entry->uid; - inode->i_gid = entry->gid; - /* #Specification: umsdos / conversion mode - * The msdos fs can do some inline conversion - * of the data of a file. It can translate - * silently from MsDOS text file format to Unix - * one (crlf -> lf) while reading, and the reverse - * while writing. This is activated using the mount - * option conv=.... - * - * This is not useful for Linux file in promoted - * directory. It can even be harmful. For this - * reason, the binary (no conversion) mode is - * always activated. - */ - /* #Specification: umsdos / conversion mode / todo - * A flag could be added to file and directories - * forcing an automatic conversion mode (as - * done with the msdos fs). - * - * This flag could be setup on a directory basis - * (instead of file) and all file in it would - * logically inherited. If the conversion mode - * is active (conv=) then the i_binary flag would - * be left untouched in those directories. - * - * It was proposed that the sticky bit was used - * to set this. The problem is that new file would - * be written incorrectly. The other problem is that - * the sticky bit has a meaning for directories. So - * another bit should be used (there is some space - * in the EMD file for it) and a special utilities - * would be used to assign the flag to a directory). - * I don't think it is useful to assign this flag - * on a single file. - */ - - MSDOS_I (inode)->i_binary = 1; - /* #Specification: umsdos / i_nlink - * The nlink field of an inode is maintain by the MSDOS file system - * for directory and by UMSDOS for other file. The logic is that - * MSDOS is already figuring out what to do for directories and - * does nothing for other files. For MSDOS, there are no hard link - * so all file carry nlink==1. UMSDOS use some info in the - * EMD file to plug the correct value. - */ - if (!S_ISDIR (entry->mode)) { - if (entry->nlink > 0) { - inode->i_nlink = entry->nlink; - } else { - printk (KERN_ERR "UMSDOS: lookup_patch entry->nlink < 1 ???\n"); - } - } - umsdos_patch_inode (inode, dir, emd_pos); + if (!S_ISDIR (entry->mode)) { + if (entry->nlink > 0) { + inode->i_nlink = entry->nlink; + } else { + printk (KERN_ERR + "UMSDOS: lookup_patch entry->nlink < 1 ???\n"); } - if (S_ISDIR (inode->i_mode)) - umsdos_unlockcreate (inode); - if (inode->u.umsdos_i.i_emd_owner == 0) - printk (KERN_WARNING "emd_owner still 0 ???\n"); } + umsdos_patch_inode (inode, dir, emd_pos); + +out_unlock: + if (S_ISDIR (inode->i_mode)) + umsdos_unlockcreate (inode); + if (inode->u.umsdos_i.i_emd_owner == 0) + printk (KERN_WARNING "UMSDOS: emd_owner still 0?\n"); +out: + return; } +/* + * The preferred interface to the above routine ... + */ +void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry, + off_t emd_pos) +{ + umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry, + emd_pos); +} + struct UMSDOS_DIRENT_K { off_t f_pos; /* will hold the offset of the entry in EMD */ @@ -523,8 +413,7 @@ struct UMSDOS_DIRENT_K { * Just to record the offset of one entry. */ -static int umsdos_filldir_k ( - void *buf, +static int umsdos_filldir_k ( void *buf, const char *name, int len, off_t offset, @@ -543,8 +432,7 @@ struct UMSDOS_DIR_SEARCH { ino_t search_ino; }; -static int umsdos_dir_search ( - void *buf, +static int umsdos_dir_search ( void *buf, const char *name, int len, off_t offset, @@ -566,19 +454,21 @@ static int umsdos_dir_search ( /* - * Locate entry of an inode in a directory. + * Locate the directory entry for a dentry in its parent directory. * Return 0 or a negative error code. * - * Normally, this function must succeed. It means a strange corruption + * Normally, this function must succeed. It means a strange corruption * in the file system if not. */ -int umsdos_inode2entry ( - struct inode *dir, - struct inode *inode, - struct umsdos_dirent *entry) -{ /* Will hold the entry */ - int ret = -ENOENT; +int umsdos_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry) +{ + struct dentry *parent = dentry->d_parent; + struct inode *inode = dentry->d_inode; + int ret = -ENOENT, err; + struct file filp; + struct UMSDOS_DIR_SEARCH bufsrch; + struct UMSDOS_DIRENT_K bufk; if (pseudo_root && inode == pseudo_root) { /* @@ -588,152 +478,115 @@ int umsdos_inode2entry ( memcpy (entry->name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN + 1); entry->name_len = UMSDOS_PSDROOT_LEN; ret = 0; - } else { - struct inode *emddir = umsdos_emd_dir_lookup (dir, 0); + goto out; + } - iput (emddir); /* FIXME? */ - if (emddir == NULL) { - /* This is a DOS directory */ - struct UMSDOS_DIR_SEARCH bufk; - struct file filp; - struct dentry *i2e; - - i2e = creat_dentry ("i2e.nul", 7, dir, NULL); - - fill_new_filp (&filp, i2e); - - Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n")); - filp.f_reada = 1; - filp.f_pos = 0; - bufk.entry = entry; - bufk.search_ino = inode->i_ino; - fat_readdir (&filp, &bufk, umsdos_dir_search); - if (bufk.found) { - ret = 0; - inode->u.umsdos_i.i_dir_owner = dir->i_ino; - inode->u.umsdos_i.i_emd_owner = 0; - umsdos_setup_dir_inode (inode); - } - } else { - /* skip . and .. see umsdos_readdir_x() */ - struct file filp; - struct dentry *i2e; - - i2e = creat_dentry ("i2e.nn", 6, dir, NULL); - fill_new_filp (&filp, i2e); - - filp.f_reada = 1; - filp.f_pos = UMSDOS_SPECIAL_DIRFPOS; - Printk ((KERN_ERR "umsdos_inode2entry skip...: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n")); - while (1) { - struct UMSDOS_DIRENT_K bufk; - - if (umsdos_readdir_x (dir, &filp, &bufk - ,1, entry, 0, umsdos_filldir_k) < 0) { - printk ("UMSDOS: can't locate inode %ld in EMD file???\n" - ,inode->i_ino); - break; - } else if (bufk.ino == inode->i_ino) { - ret = 0; - umsdos_lookup_patch (dir, inode, entry, bufk.f_pos); - break; - } - } + /* initialize the file */ + fill_new_filp (&filp, parent); + + if (!umsdos_have_emd(parent)) { + /* This is a DOS directory. */ + filp.f_pos = 0; + bufsrch.entry = entry; + bufsrch.search_ino = inode->i_ino; + fat_readdir (&filp, &bufsrch, umsdos_dir_search); + if (bufsrch.found) { + ret = 0; + inode->u.umsdos_i.i_dir_owner = parent->d_inode->i_ino; + inode->u.umsdos_i.i_emd_owner = 0; +if (!S_ISDIR(inode->i_mode)) +printk("UMSDOS: %s/%s not a directory!\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + /* N.B. why call this? not always a dir ... */ + umsdos_setup_dir(dentry); } + goto out; } + + /* skip . and .. see umsdos_readdir_x() */ + filp.f_pos = UMSDOS_SPECIAL_DIRFPOS; + while (1) { + err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1, + entry, 0, umsdos_filldir_k); + if (err < 0) { + printk ("UMSDOS: can't locate inode %ld in EMD??\n", + inode->i_ino); + break; + } + if (bufk.ino == inode->i_ino) { + ret = 0; + umsdos_lookup_patch_new(dentry, entry, bufk.f_pos); + break; + } + } +out: return ret; } - /* - * Locate the parent of a directory and the info on that directory - * Return 0 or a negative error code. + * Deprecated. Try to get rid of this soon! */ - -static int umsdos_locate_ancestor ( - struct inode *dir, - struct inode **result, - struct umsdos_dirent *entry) +int umsdos_inode2entry (struct inode *dir, struct inode *inode, + struct umsdos_dirent *entry) { - int ret; - - umsdos_patch_inode (dir, NULL, 0); - /* FIXME */ - ret = compat_umsdos_real_lookup (dir, "..", 2, result); - Printk (("result %d %p ", ret, *result)); - if (ret == 0) { - struct inode *adir = *result; + int ret = -ENOENT; + struct inode *emddir; + struct dentry *i2e; + struct file filp; + struct UMSDOS_DIR_SEARCH bufsrch; + struct UMSDOS_DIRENT_K bufk; - ret = umsdos_inode2entry (adir, dir, entry); + if (pseudo_root && inode == pseudo_root) { + /* + * Quick way to find the name. + * Also umsdos_readdir_x won't show /linux anyway + */ + memcpy (entry->name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN + 1); + entry->name_len = UMSDOS_PSDROOT_LEN; + ret = 0; + goto out; } - Printk (("\n")); - return ret; -} - - -/* - * Build the path name of an inode (relative to the file system. - * This function is need to set (pseudo) hard link. - * - * It uses the same strategy as the standard getcwd(). - */ -int umsdos_locate_path ( - struct inode *inode, - char *path) -{ - int ret = 0; - struct inode *dir = inode; - struct inode *root_inode; - char *bpath = (char *) kmalloc (PATH_MAX, GFP_KERNEL); + emddir = umsdos_emd_dir_lookup (dir, 0); + if (emddir == NULL) { + /* This is a DOS directory. */ + i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL); + fill_new_filp (&filp, i2e); + filp.f_reada = 1; + filp.f_pos = 0; + bufsrch.entry = entry; + bufsrch.search_ino = inode->i_ino; + fat_readdir (&filp, &bufsrch, umsdos_dir_search); + if (bufsrch.found) { + ret = 0; + inode->u.umsdos_i.i_dir_owner = dir->i_ino; + inode->u.umsdos_i.i_emd_owner = 0; + umsdos_setup_dir_inode (inode); + } + goto out; + } - root_inode = iget (inode->i_sb, UMSDOS_ROOT_INO); - if (bpath == NULL) { - ret = -ENOMEM; - } else { - struct umsdos_dirent entry; - char *ptbpath = bpath + PATH_MAX - 1; - - *ptbpath = '\0'; - Printk (("locate_path mode %x ", inode->i_mode)); - if (!S_ISDIR (inode->i_mode)) { - ret = umsdos_get_dirowner (inode, &dir); - Printk (("locate_path ret %d ", ret)); - if (ret == 0) { - ret = umsdos_inode2entry (dir, inode, &entry); - if (ret == 0) { - ptbpath -= entry.name_len; - memcpy (ptbpath, entry.name, entry.name_len); - Printk (("ptbpath :%.*s: ", entry.name_len, ptbpath)); - } - } - } else { - inc_count (dir); + /* skip . and .. see umsdos_readdir_x() */ + + i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL); + fill_new_filp (&filp, i2e); + filp.f_reada = 1; + filp.f_pos = UMSDOS_SPECIAL_DIRFPOS; + while (1) { + if (umsdos_readdir_x (dir, &filp, &bufk, 1, + entry, 0, umsdos_filldir_k) < 0) { + printk ("UMSDOS: can't locate inode %ld in EMD??\n", + inode->i_ino); + break; } - if (ret == 0) { - while (dir != root_inode) { - struct inode *adir; - - ret = umsdos_locate_ancestor (dir, &adir, &entry); - /* iput (dir); FIXME */ - dir = NULL; - Printk (("ancestor %d ", ret)); - if (ret == 0) { - *--ptbpath = '/'; - ptbpath -= entry.name_len; - memcpy (ptbpath, entry.name, entry.name_len); - dir = adir; - Printk (("ptbpath :%.*s: ", entry.name_len, ptbpath)); - } else { - break; - } - } + if (bufk.ino == inode->i_ino) { + ret = 0; + umsdos_lookup_patch (dir, inode, entry, bufk.f_pos); + break; } - strcpy (path, ptbpath); - kfree (bpath); } - Printk (("\n")); - iput (dir); /* FIXME?? */ + iput (emddir); +out: return ret; } @@ -742,9 +595,7 @@ int umsdos_locate_path ( * Return != 0 if an entry is the pseudo DOS entry in the pseudo root. */ -int umsdos_is_pseudodos ( - struct inode *dir, - struct dentry *dentry) +int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry) { /* #Specification: pseudo root / DOS hard coded * The pseudo sub-directory DOS in the pseudo root is hard coded. @@ -753,8 +604,7 @@ int umsdos_is_pseudodos ( * a reserved path and nobody will think of using such a path * for a package. */ - return pseudo_root - && dir == pseudo_root + return dir == pseudo_root && dentry->d_name.len == 3 && dentry->d_name.name[0] == 'D' && dentry->d_name.name[1] == 'O' @@ -763,267 +613,312 @@ int umsdos_is_pseudodos ( /* - * Check if a file exist in the current directory. - * Return 0 if ok, negative error code if not (ex: -ENOENT). + * Check whether a file exists in the current directory. + * Return 0 if OK, negative error code if not (ex: -ENOENT). + * + * fills dentry->d_inode with found inode, and increments its count. + * if not found, return -ENOENT. + */ +/* #Specification: umsdos / lookup + * A lookup for a file is done in two steps. First, we + * locate the file in the EMD file. If not present, we + * return an error code (-ENOENT). If it is there, we + * repeat the operation on the msdos file system. If + * this fails, it means that the file system is not in + * sync with the EMD file. We silently remove this + * entry from the EMD file, and return ENOENT. */ -int umsdos_lookup_x ( - struct inode *dir, - struct dentry *dentry, - int nopseudo) -{ /* Don't care about pseudo root mode */ - int ret = -ENOENT; - struct inode *root_inode; - int len = dentry->d_name.len; +int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo) +{ const char *name = dentry->d_name.name; + int len = dentry->d_name.len; + struct dentry *dret = NULL; + struct inode *inode; + int ret = -ENOENT; + struct umsdos_info info; - PRINTK ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu (i_count=%d), d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dir->i_count, dentry->d_parent)); /* FIXME /mn/ debug only */ - if (dentry->d_parent) - PRINTK ((KERN_DEBUG " d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name)); /* FIXME : delme /mn/ */ - - root_inode = iget (dir->i_sb, UMSDOS_ROOT_INO); - Printk ((KERN_ERR "umsdos_lookup_x (CNT!): entering root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count)); /* FIXME: DEBUG, DELME */ - - d_instantiate (dentry, NULL); umsdos_startlookup (dir); + /* this shouldn't happen ... */ if (len == 1 && name[0] == '.') { - d_add (dentry, dir); - inc_count (dir); - ret = 0; - } else if (len == 2 && name[0] == '.' && name[1] == '.') { - if (pseudo_root && dir == pseudo_root) { - /* #Specification: pseudo root / .. in real root - * Whenever a lookup is those in the real root for - * the directory .., and pseudo root is active, the - * pseudo root is returned. - */ - ret = 0; - d_add (dentry, pseudo_root); - inc_count (pseudo_root); - } else { - /* #Specification: locating .. / strategy - * We use the msdos filesystem to locate the parent directory. - * But it is more complicated than that. - * - * We have to step back even further to - * get the parent of the parent, so we can get the EMD - * of the parent of the parent. Using the EMD file, we can - * locate all the info on the parent, such a permissions - * and owner. - */ - - ret = compat_umsdos_real_lookup (dir, "..", 2, &dentry->d_inode); - Printk (("ancestor ret %d dir %p *result %p ", ret, dir, dentry->d_inode)); - if (ret == 0 - && dentry->d_inode != root_inode - && dentry->d_inode != pseudo_root) { - struct inode *aadir; - struct umsdos_dirent entry; - - ret = umsdos_locate_ancestor (dentry->d_inode, &aadir, &entry); - iput (aadir); /* FIXME */ - } - } - } else if (umsdos_is_pseudodos (dir, dentry)) { + printk("umsdos_lookup_x: UMSDOS broken, please report!\n"); + goto out; + } + + /* this shouldn't happen ... */ + if (len == 2 && name[0] == '.' && name[1] == '.') { + printk("umsdos_lookup_x: UMSDOS broken, please report!\n"); + goto out; + } + + if (umsdos_is_pseudodos (dir, dentry)) { /* #Specification: pseudo root / lookup(DOS) * A lookup of DOS in the pseudo root will always succeed * and return the inode of the real root. */ - d_add (dentry, root_inode); - inc_count (dentry->d_inode); - ret = 0; - } else { - struct umsdos_info info; + inode = iget(dir->i_sb, UMSDOS_ROOT_INO); + if (inode) + goto out_add; + ret = -ENOMEM; + goto out; + } - ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - if (ret == 0) - ret = umsdos_findentry (dir, &info, 0); - Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, info.fake.fname, info.f_pos, ret - ,info.fake.len)); - if (ret == 0) { - /* #Specification: umsdos / lookup - * A lookup for a file is done in two step. First, we locate - * the file in the EMD file. If not present, we return - * an error code (-ENOENT). If it is there, we repeat the - * operation on the msdos file system. If this fails, it means - * that the file system is not in sync with the emd file. - * We silently remove this entry from the emd file, - * and return ENOENT. - */ - struct inode *inode; - - ret = compat_umsdos_real_lookup (dir, info.fake.fname, info.fake.len, &inode); - - Printk ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %d with inode=%p\n", info.fake.len, info.fake.fname, ret, inode)); - - if (inode == NULL) { - printk (KERN_WARNING "UMSDOS: Erase entry %.*s, out of sync with MsDOS\n" - ,info.fake.len, info.fake.fname); - umsdos_delentry (dir, &info, S_ISDIR (info.entry.mode)); - } else { - Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", inode->i_ino)); - - /* we've found it. now put inode in dentry */ - d_add (dentry, inode); - - umsdos_lookup_patch (dir, inode, &info.entry, info.f_pos); - Printk (("lookup ino %ld flags %d\n", inode->i_ino, info.entry.flags)); - if (info.entry.flags & UMSDOS_HLINK) { - Printk ((KERN_DEBUG "umsdos_lookup_x: here goes HLINK\n")); - ret = umsdos_hlink2inode (inode, &dentry->d_inode); - } - if (pseudo_root && dentry->d_inode == pseudo_root && !nopseudo) { - /* #Specification: pseudo root / dir lookup - * For the same reason as readdir, a lookup in /DOS for - * the pseudo root directory (linux) will fail. - */ - /* - * This has to be allowed for resolving hard link - * which are recorded independently of the pseudo-root - * mode. - */ - Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n")); - iput (pseudo_root); /* FIXME?? */ - d_instantiate (dentry, NULL); /* FIXME: should be dput(dentry) ? */ - ret = -ENOENT; - } - } - } + ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + if (ret) + goto out; + ret = umsdos_findentry (dentry->d_parent, &info, 0); +Printk (("lookup %.*s pos %lu ret %d len %d ", +info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len)); + if (ret) + goto out; + + + dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, + info.fake.len); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + goto out; + if (!dret->d_inode) + goto out_remove; + + umsdos_lookup_patch_new(dret, &info.entry, info.f_pos); + + /* Check for a hard link */ + if (info.entry.flags & UMSDOS_HLINK) { +Printk (("checking hard link %s/%s, ino=%ld, flags=%x\n", +dret->d_parent->d_name.name, dret->d_name.name, +dret->d_inode->i_ino, info.entry.flags)); + dret = umsdos_solve_hlink (dret); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + goto out; + } + /* N.B. can dentry be negative after resolving hlinks? */ + + if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) { + /* #Specification: pseudo root / dir lookup + * For the same reason as readdir, a lookup in /DOS for + * the pseudo root directory (linux) will fail. + */ + /* + * This has to be allowed for resolving hard links + * which are recorded independently of the pseudo-root + * mode. + */ + Printk (("umsdos_lookup_x: untested Pseudo_root\n")); + ret = -ENOENT; + goto out_dput; + } else { + /* We've found it OK. Now put inode in dentry. */ + inode = dret->d_inode; } + + /* + * Hash the dentry with the inode. + */ +out_add: + inode->i_count++; + d_add (dentry, inode); + ret = 0; + +out_dput: + dput(dret); +out: umsdos_endlookup (dir); - PRINTK ((KERN_DEBUG "umsdos_lookup_x: returning %d : name=%.*s (i_count=%d), dir=%lu (i_count=%d)\n", ret, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_count, dir->i_ino, dir->i_count)); - Printk ((KERN_ERR "umsdos_lookup_x (CNT!): exiting root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count)); /* FIXME: DEBUG, DELME */ return ret; + +out_remove: + printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n", + dentry->d_name.name, info.fake.fname); + umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode)); + ret = -ENOENT; + goto out; } /* - * Check if a file exist in the current directory. - * Return 0 if ok, negative error code if not (ex: -ENOENT). - * + * Check whether a file exists in the current directory. + * Return 0 if OK, negative error code if not (ex: -ENOENT). * + * called by VFS. should fill dentry->d_inode (via d_add), and + * set (increment) dentry->d_inode->i_count. + * */ -int UMSDOS_lookup ( - struct inode *dir, - struct dentry *dentry) +int UMSDOS_lookup (struct inode *dir, struct dentry *dentry) { int ret; - check_dentry (dentry); ret = umsdos_lookup_x (dir, dentry, 0); - check_dentry (dentry); -#if 1 + /* Create negative dentry if not found. */ if (ret == -ENOENT) { - Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative dentry !\n")); - d_add (dentry, NULL); /* create negative dentry if not found */ + Printk ((KERN_DEBUG + "UMSDOS_lookup: converting -ENOENT to negative\n")); + d_add (dentry, NULL); ret = 0; } -#endif - return ret; } +/* + * Lookup or create a dentry from within the filesystem. + * + * We need to use this instead of lookup_dentry, as the + * directory semaphore lock is already held. + */ +struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len) +{ + struct dentry *result, *dentry; + int error; + struct qstr qstr; + + qstr.name = name; + qstr.len = len; + qstr.hash = full_name_hash(name, len); + result = d_lookup(parent, &qstr); + if (!result) { + result = ERR_PTR(-ENOMEM); + dentry = d_alloc(parent, &qstr); + if (dentry) { + result = dentry; + error = umsdos_real_lookup(parent->d_inode, result); + if (error) + goto out_fail; + } + } +out: + return result; + +out_fail: + dput(result); + result = ERR_PTR(error); + goto out; +} + /* - * Locate the inode pointed by a (pseudo) hard link - * Return 0 if ok, a negative error code if not. + * gets dentry which points to pseudo-hardlink + * + * it should try to find file it points to + * if file is found, it should dput() original dentry and return new one + * (with d_count = i_count = 1) + * Otherwise, it should return with error, with dput()ed original dentry. + * */ -int umsdos_hlink2inode (struct inode *hlink, struct inode **result) +struct dentry *umsdos_solve_hlink (struct dentry *hlink) { - struct inode *root_inode; + /* root is our root for resolving pseudo-hardlink */ + struct dentry *base = hlink->d_sb->s_root; + struct dentry *final, *dir, *dentry_dst; + char *path, *pt; + unsigned long len; int ret = -EIO; - struct dentry *dentry_src, *dentry_dst; - char *path; + struct file filp; -#if 0 /* FIXME: DELME */ - Printk (("FIXME: just test. hlink2inode returning -ENOENT\n /mn/\n")); - return -ENOENT; /* /mn/ FIXME just for test */ -#endif + check_dentry_path (hlink, "HLINK BEGIN hlink"); + final = ERR_PTR (-ENOMEM); path = (char *) kmalloc (PATH_MAX, GFP_KERNEL); - - root_inode = iget (hlink->i_sb, UMSDOS_ROOT_INO); - *result = NULL; - if (path == NULL) { - ret = -ENOMEM; - iput (hlink); /* FIXME? */ - } else { - struct file filp; - loff_t offs = 0; - - dentry_src = creat_dentry ("hlink-mn", 8, hlink, NULL); - - fill_new_filp (&filp, dentry_src); - filp.f_flags = O_RDONLY; - - Printk (("hlink2inode ")); - if (umsdos_file_read_kmem (hlink, &filp, path, hlink->i_size, &offs) == hlink->i_size) { - struct inode *dir; - char *pt = path; - - dir = root_inode; - path[hlink->i_size] = '\0'; - iput (hlink); /* FIXME? */ - inc_count (dir); - while (1) { - char *start = pt; - int len; - - while (*pt != '\0' && *pt != '/') - pt++; - len = (int) (pt - start); - if (*pt == '/') - *pt++ = '\0'; - /* FIXME. /mn/ fixed ? */ - - dentry_dst = creat_dentry (start, len, NULL, NULL); - - if (dir->u.umsdos_i.i_emd_dir == 0) { - /* This is a DOS directory */ - - Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name)); - ret = umsdos_rlookup_x (dir, dentry_dst, 1); - } else { - Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name)); - ret = umsdos_lookup_x (dir, dentry_dst, 1); - } - Printk ((" returned %d\n", ret)); - *result = dentry_dst->d_inode; /* /mn/ ok ? */ - - Printk (("h2n lookup :%s: -> %d ", start, ret)); - if (ret == 0 && *pt != '\0') { - dir = *result; - } else { - break; - } - } + if (path == NULL) + goto out; + + fill_new_filp (&filp, hlink); + filp.f_flags = O_RDONLY; + filp.f_pos = 0; + + Printk (("hlink2inode ")); + len = umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size); + if (len != hlink->d_inode->i_size) + goto out_noread; + + /* start at root dentry */ + dir = dget(base); + path[hlink->d_inode->i_size] = '\0'; + pt = path; + while (1) { + char *start = pt; + int len; + + while (*pt != '\0' && *pt != '/') pt++; + len = (int) (pt - start); + if (*pt == '/') *pt++ = '\0'; + + dentry_dst = umsdos_lookup_dentry(dir, start, len); + if (IS_ERR(dentry_dst)) + break; + if (dir->d_inode->u.umsdos_i.i_emd_dir == 0) { + /* This is a DOS directory */ + ret = umsdos_rlookup_x (dir->d_inode, dentry_dst, 1); } else { - Printk (("umsdos_hlink2inode: all those iput's() frighten me /mn/. Whatabout dput() ? FIXME!\n")); - iput (hlink); /* FIXME? */ + ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1); + } + Printk ((" returned %d\n", ret)); + dput (dir); /* dir no longer needed */ + dir = dentry_dst; + + Printk (("h2n lookup :%s: -> %d ", start, ret)); + final = ERR_PTR (ret); + if (ret != 0) { + /* path component not found! */ + break; } - Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, *result)); - kfree (path); + if (*pt == '\0') { /* we're finished! */ + final = umsdos_lookup_dentry(hlink->d_parent, + (char *) hlink->d_name.name, + hlink->d_name.len); + break; + } + } /* end while */ + /* + * See whether we found the path ... + */ + if (!IS_ERR(final)) { + if (!final->d_inode) { + d_instantiate(final, dir->d_inode); + /* we need inode to survive. */ + dir->d_inode->i_count++; + } else { + printk ("umsdos_solve_hlink: %s/%s already exists\n", + final->d_parent->d_name.name, + final->d_name.name); + } +printk ("umsdos_solve_hlink: ret = %d, %s/%s -> %s/%s\n", +ret, hlink->d_parent->d_name.name, hlink->d_name.name, +final->d_parent->d_name.name, final->d_name.name); } - return ret; -} + dput(dir); + +out_free: + kfree (path); + +out: + dput(hlink); /* original hlink no longer needed */ + check_dentry_path (hlink, "HLINK FIN hlink"); + check_dentry_path (final, "HLINK RET final"); + return final; + +out_noread: + printk("umsdos_solve_hlink: failed reading pseudolink!\n"); + goto out_free; +} static struct file_operations umsdos_dir_operations = { NULL, /* lseek - default */ - UMSDOS_dir_read, /* read */ + dummy_dir_read, /* read */ NULL, /* write - bad */ UMSDOS_readdir, /* readdir */ NULL, /* poll - default */ UMSDOS_ioctl_dir, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ - NULL /* fsync *//* in original NULL. changed to file_fsync. FIXME? /mn/ */ + NULL /* fsync */ }; struct inode_operations umsdos_dir_inode_operations = @@ -1040,9 +935,9 @@ struct inode_operations umsdos_dir_inode_operations = UMSDOS_rename, /* rename */ NULL, /* readlink */ NULL, /* followlink */ - generic_readpage, /* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */ + generic_readpage, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap *//* in original NULL. changed to fat_bmap. FIXME? /mn/ */ + fat_bmap, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index bf52a4d96..2f56b0404 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -20,35 +20,24 @@ #include <asm/delay.h> -#define PRINTK(x) -#define Printk(x) printk x /* * Read a file into kernel space memory * returns how many bytes read (from fat_file_read) */ -ssize_t umsdos_file_read_kmem ( struct inode *emd_dir, - struct file *filp, +ssize_t umsdos_file_read_kmem ( struct file *filp, char *buf, - size_t count, - loff_t *offs) + size_t count) { int ret; - struct dentry *old_dentry; mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); - old_dentry = filp->f_dentry; /* save it */ - filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL); - *offs = filp->f_pos; - - PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); - PRINTK ((KERN_DEBUG " using emd=%ld\n", emd_dir->i_ino)); + PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count)); PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); - PRINTK ((KERN_DEBUG " ofs=%ld\n", (unsigned long) *offs)); PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos)); PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary)); @@ -58,25 +47,11 @@ ssize_t umsdos_file_read_kmem ( struct inode *emd_dir, PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; - ret = fat_file_read (filp, buf, count, offs); - PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret)); - - filp->f_pos = *offs; /* we needed *filp only for this? grrrr... /mn/ */ - /* FIXME: I have no idea what f_pos is used for. It seems to be used this way before offs was introduced. - * this probably needs fixing /mn/ */ - d_drop (filp->f_dentry); /* FIXME: hmmmm... we should not dispose of it in this way ? */ - filp->f_dentry = old_dentry; /* restore orig. dentry (it is dentry of file we need info about. Dunno why it gets passed to us - * since we have no use for it, expect to store totally unrelated data of offset of EMD_FILE - * end not directory in it. But what the hell now... fat_file_read requires it also, but prolly expects - * it to be file* of EMD not file we want to read EMD entry about... ugh. complicated to explain :) /mn/ */ - - /* FIXME: we probably need to destroy original filp->f_dentry first ? Do we ? And how ? this way we leave all sorts of dentries, inodes etc. lying around */ - /* Also FIXME: all the same problems in umsdos_file_write_kmem */ + ret = fat_file_read (filp, buf, count, &filp->f_pos); + PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret)); - PRINTK ((KERN_DEBUG " (ret) using emd=%lu\n", emd_dir->i_ino)); PRINTK ((KERN_DEBUG " (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); - PRINTK ((KERN_DEBUG " (ret) ofs=%Lu\n", *offs)); PRINTK ((KERN_DEBUG " (ret) f_pos=%Lu\n", filp->f_pos)); PRINTK ((KERN_DEBUG " (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); PRINTK ((KERN_DEBUG " (ret) i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary)); @@ -89,9 +64,9 @@ ssize_t umsdos_file_read_kmem ( struct inode *emd_dir, { struct umsdos_dirent *mydirent = buf; - PRINTK ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid)); - PRINTK ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid)); - PRINTK ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name)); + Printk ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid)); + Printk ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid)); + Printk ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name)); } #endif @@ -102,25 +77,23 @@ ssize_t umsdos_file_read_kmem ( struct inode *emd_dir, /* * Write to file from kernel space. - * Does the real job, assumes all structures are initialized ! + * Does the real job, assumes all structures are initialized! */ ssize_t umsdos_file_write_kmem_real (struct file * filp, const char *buf, - size_t count, - loff_t * offs) + size_t count) { ssize_t ret; mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); - PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); + PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count)); PRINTK ((KERN_DEBUG " struct dentry=%p\n", filp->f_dentry)); PRINTK ((KERN_DEBUG " struct inode=%p\n", filp->f_dentry->d_inode)); PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); - PRINTK ((KERN_DEBUG " ofs=%ld\n", (unsigned long) *offs)); PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos)); PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary)); @@ -131,13 +104,13 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp, /* note: i_binary=2 is for CVF-FAT. We put it here, instead of * umsdos_file_write_kmem, since it is also wise not to compress symlinks - * (in unlikely event that they are > 512 bytes and can be compressed - * FIXME: should we set it when reading symlink too ? */ + * (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; - ret = fat_file_write (filp, buf, count, offs); - PRINTK ((KERN_DEBUG "fat_file_write returned with %ld!\n", ret)); + ret = fat_file_write (filp, buf, count, &filp->f_pos); + Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret)); set_fs (old_fs); return ret; @@ -145,61 +118,40 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp, /* - * Write to a file from kernel space + * Write to a file from kernel space. */ -ssize_t umsdos_file_write_kmem (struct inode * emd_dir, - struct file * filp, +ssize_t umsdos_file_write_kmem (struct file *filp, const char *buf, - size_t count, - loff_t * offs) + size_t count) { int ret; - struct dentry *old_dentry; - Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n")); - Printk ((KERN_DEBUG " using emd=%ld\n", emd_dir->i_ino)); - - old_dentry = filp->f_dentry; /* save it */ - filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL); - - *offs = filp->f_pos; /* FIXME, in read_kmem also: offs is not used so why pass it ?!!! /mn/ */ - - ret = umsdos_file_write_kmem_real (filp, buf, count, offs); - - d_drop (filp->f_dentry); - filp->f_pos = *offs; - filp->f_dentry = old_dentry; - + ret = umsdos_file_write_kmem_real (filp, buf, count); return ret; } - /* * Write a block of bytes into one EMD file. * The block of data is NOT in user space. * - * Return 0 if ok, a negative error code if not. + * Return 0 if OK, a negative error code if not. + * + * Note: buffer is in kernel memory, not in user space. */ -ssize_t umsdos_emd_dir_write ( struct inode * emd_dir, - struct file * filp, - char *buf, /* buffer in kernel memory, not in user space */ - size_t count, - loff_t * offs) +ssize_t umsdos_emd_dir_write ( struct file *filp, + char *buf, + size_t count) { int written; - loff_t myofs = 0; #ifdef __BIG_ENDIAN struct umsdos_dirent *d = (struct umsdos_dirent *) buf; -#endif - filp->f_flags = 0; -#ifdef __BIG_ENDIAN d->nlink = cpu_to_le16 (d->nlink); d->uid = cpu_to_le16 (d->uid); d->gid = cpu_to_le16 (d->gid); @@ -210,13 +162,11 @@ ssize_t umsdos_emd_dir_write ( struct inode * emd_dir, d->mode = cpu_to_le16 (d->mode); #endif - if (offs) - myofs = *offs; /* if offs is not NULL, read it */ - Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %d, %Ld\n", emd_dir, filp, buf, count, myofs)); - written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs); + filp->f_flags = 0; +Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n", +filp, buf, count, filp->f_pos)); + written = umsdos_file_write_kmem (filp, buf, count); Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n")); - if (offs) - *offs = myofs; /* if offs is not NULL, store myofs there */ #ifdef __BIG_ENDIAN d->nlink = le16_to_cpu (d->nlink); @@ -229,9 +179,10 @@ ssize_t umsdos_emd_dir_write ( struct inode * emd_dir, d->mode = le16_to_cpu (d->mode); #endif -#if 1 - if (written != count) - Printk ((KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count)); +#if UMS_DEBUG +if (written != count) +printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", +written, count); #endif return written != count ? -EIO : 0; @@ -240,18 +191,14 @@ ssize_t umsdos_emd_dir_write ( struct inode * emd_dir, /* - * Read a block of bytes from one EMD file. + * Read a block of bytes from one EMD file. * The block of data is NOT in user space. - * Return 0 if ok, -EIO if any error. + * Return 0 if OK, -EIO if any error. */ +/* buffer in kernel memory, not in user space */ -ssize_t umsdos_emd_dir_read (struct inode * emd_dir, - struct file * filp, - char *buf, /* buffer in kernel memory, not in user space */ - size_t count, - loff_t * offs) +ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count) { - loff_t myofs = 0; long int ret = 0; int sizeread; @@ -261,12 +208,11 @@ ssize_t umsdos_emd_dir_read (struct inode * emd_dir, #endif - if (offs) - myofs = *offs; /* if offs is not NULL, read it */ filp->f_flags = 0; - sizeread = umsdos_file_read_kmem (emd_dir, filp, buf, count, &myofs); + sizeread = umsdos_file_read_kmem (filp, buf, count); if (sizeread != count) { - printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n", filp->f_pos, sizeread, count); +printk ("UMSDOS: problem with EMD file: can't read pos = %Ld (%d != %d)\n", +filp->f_pos, sizeread, count); ret = -EIO; } #ifdef __BIG_ENDIAN @@ -279,170 +225,232 @@ ssize_t umsdos_emd_dir_read (struct inode * emd_dir, d->rdev = le16_to_cpu (d->rdev); d->mode = le16_to_cpu (d->mode); #endif - if (offs) - *offs = myofs; /* if offs is not NULL, store myofs there */ return ret; } +/* + * Create the EMD dentry for a directory. + */ +struct dentry *umsdos_get_emd_dentry(struct dentry *parent) +{ + struct dentry *demd; + demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE, + UMSDOS_EMD_NAMELEN); + return demd; +} /* - * Locate the EMD file in a directory . - * - * Return NULL if error. If ok, dir->u.umsdos_i.emd_inode + * Check whether a directory has an EMD file. */ - -struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat) +int umsdos_have_emd(struct dentry *dir) { - struct inode *ret = NULL; - int res; + struct dentry *demd = umsdos_get_emd_dentry (dir); + int found = 0; - Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n")); - if (dir->u.umsdos_i.i_emd_dir != 0) { - ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir); - Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n" - ,dir->u.umsdos_i.i_emd_dir, ret)); - } else { - PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE)); - res = compat_umsdos_real_lookup (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, &ret); - PRINTK ((KERN_DEBUG "-returned %d\n", res)); - Printk ((KERN_INFO "emd_dir_lookup ")); - if (ret != NULL) { - Printk (("Found --linux ")); - dir->u.umsdos_i.i_emd_dir = ret->i_ino; - } else if (creat) { - int code; - - Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? ")); - Printk (("avant create ")); - inc_count (dir); - - check_inode (ret); - code = compat_msdos_create (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, S_IFREG | 0777, &ret); - check_inode (ret); - Printk (("Creat EMD code %d ret %p ", code, ret)); - if (ret != NULL) { - Printk ((" ino=%lu", ret->i_ino)); - dir->u.umsdos_i.i_emd_dir = ret->i_ino; - } else { - printk (KERN_WARNING "UMSDOS: Can't create EMD file\n"); - } - } - if (ret != NULL) { - /* Disable UMSDOS_notify_change() for EMD file */ - ret->u.umsdos_i.i_emd_owner = 0xffffffff; - } - + if (!IS_ERR(demd)) { + if (demd->d_inode) + found = 1; + dput(demd); } - -#if 1 - Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret)); - if (ret != NULL) - Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino)); -#endif - return ret; + return found; } +/* + * Create the EMD file for a directory if it doesn't + * already exist. Returns 0 or and error code. + */ +int umsdos_make_emd(struct dentry *parent) +{ + struct dentry *demd = umsdos_get_emd_dentry(parent); + struct inode *inode; + int err = PTR_ERR(demd); + + if (IS_ERR(demd)) + goto out; + + /* already created? */ + inode = demd->d_inode; + if (inode) { + parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino; + err = 0; + goto out_dput; + } + +printk("umsdos_make_emd: creating %s/%s\n", +parent->d_name.name, demd->d_name.name); + + err = msdos_create(parent->d_inode, demd, S_IFREG | 0777); + if (err) { + printk (KERN_WARNING "UMSDOS: Can't create EMD file\n"); + goto out_dput; + } + inode = demd->d_inode; + parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino; + /* Disable UMSDOS_notify_change() for EMD file */ + inode->u.umsdos_i.i_emd_owner = 0xffffffff; + +out_dput: + dput(demd); +out: + return err; +} /* - * creates an EMD file + * Locate the EMD file in a directory. * - * Return NULL if error. If ok, dir->u.umsdos_i.emd_inode + * Return NULL if error, dir->u.umsdos_i.emd_inode if OK. + * Caller must iput() returned inode when finished with it! + * Note: deprecated; get rid of this soon! */ -struct inode *umsdos_emd_dir_create (struct inode *dir, struct dentry *dentry, int mode) +struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat) { struct inode *ret = NULL; + struct dentry *d_dir=NULL, *dlook=NULL; + int rv; + Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n")); + if (!dir) { + printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n"); + goto out; + } + check_inode (dir); + if (dir->u.umsdos_i.i_emd_dir != 0) { ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir); - Printk (("deja trouve %lu %p", dir->u.umsdos_i.i_emd_dir, ret)); - } else { + Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n", + dir->u.umsdos_i.i_emd_dir, ret)); + goto out; + } - int code; + PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", + UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE)); + d_dir = geti_dentry (dir); + if (!d_dir) { +printk("UMSDOS: flaky i_dentry hack failed\n"); + goto out; + } + dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir); + if (!dlook) + goto out; + rv = umsdos_real_lookup (dir, dlook); + + PRINTK ((KERN_DEBUG "-returned %d\n", rv)); + Printk ((KERN_INFO "emd_dir_lookup ")); + + ret = dlook->d_inode; + if (ret) { + Printk (("Found --linux ")); + dir->u.umsdos_i.i_emd_dir = ret->i_ino; + ret->i_count++; /* we'll need the inode */ + check_inode (ret); + } else if (creat) { + int code; + + Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? ")); Printk (("avant create ")); - inc_count (dir); - code = compat_msdos_create (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, S_IFREG | 0777, &ret); + + check_inode (ret); + code = compat_msdos_create (dir, UMSDOS_EMD_FILE, + UMSDOS_EMD_NAMELEN, + S_IFREG | 0777, &ret); + check_inode (ret); Printk (("Creat EMD code %d ret %p ", code, ret)); if (ret != NULL) { + Printk ((" ino=%lu", ret->i_ino)); dir->u.umsdos_i.i_emd_dir = ret->i_ino; } else { - printk ("UMSDOS: Can't create EMD file\n"); + printk (KERN_WARNING "UMSDOS: Can't create EMD file\n"); } } - + dput(dlook); + if (ret != NULL) { /* Disable UMSDOS_notify_change() for EMD file */ ret->u.umsdos_i.i_emd_owner = 0xffffffff; } + +out: +#if UMS_DEBUG + Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret)); + if (ret != NULL) + Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino)); +#endif return ret; } - /* * Read an entry from the EMD file. * Support variable length record. - * Return -EIO if error, 0 if ok. + * Return -EIO if error, 0 if OK. + * + * does not change {d,i}_count */ -int umsdos_emd_dir_readentry ( - struct inode *emd_dir, - struct file *filp, - struct umsdos_dirent *entry) +int umsdos_emd_dir_readentry (struct file *filp, struct umsdos_dirent *entry) { int ret; Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n")); - Printk (("umsdos_emd_dir_readentry /mn/: trying to lookup %.*s (ino=%lu) using EMD %lu\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, emd_dir->i_ino)); - ret = umsdos_emd_dir_read (emd_dir, filp, (char *) entry, UMSDOS_REC_SIZE, NULL); - if (ret == 0) { /* note /mn/: is this wrong? ret is always 0 or -EIO. but who knows. It used to work this way... */ + ret = umsdos_emd_dir_read (filp, (char *) entry, UMSDOS_REC_SIZE); + if (ret == 0) { /* if no error */ /* Variable size record. Maybe, we have to read some more */ int recsize = umsdos_evalrecsize (entry->name_len); - Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: FIXME if %d > %d?\n", recsize, UMSDOS_REC_SIZE)); if (recsize > UMSDOS_REC_SIZE) { - ret = umsdos_emd_dir_read (emd_dir, filp, ((char *) entry) + UMSDOS_REC_SIZE, recsize - UMSDOS_REC_SIZE, NULL); +Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: %d > %d!\n", +recsize, UMSDOS_REC_SIZE)); + ret = umsdos_emd_dir_read (filp, + ((char *) entry) + UMSDOS_REC_SIZE, + recsize - UMSDOS_REC_SIZE); } } - Printk (("umsdos_emd_dir_readentry /mn/: returning %d.\n", ret)); + Printk (("umsdos_emd_dir_readentry /mn/: ret=%d.\n", ret)); + if (entry && ret == 0) { +Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n", +(int) entry->name_len, (int) entry->name_len, entry->name)); + } return ret; } - /* * Write an entry in the EMD file. - * Return 0 if ok, -EIO if some error. + * Return 0 if OK, -EIO if some error. */ - -int umsdos_writeentry ( - struct inode *dir, - struct inode *emd_dir, - struct umsdos_info *info, - int free_entry) - -{ /* This entry is deleted, so Write all 0's */ - int ret = 0; - struct dentry *emd_dentry; - struct file filp; +static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, + int free_entry) +{ + struct inode *dir = parent->d_inode; struct umsdos_dirent *entry = &info->entry; + struct dentry *emd_dentry; + int ret; struct umsdos_dirent entry0; + struct file filp; - fill_new_filp (&filp, NULL); - - Printk (("umsdos_writeentry /mn/: entering...\n")); - emd_dentry = creat_dentry ("wremd_mn", 8, emd_dir, NULL); + emd_dentry = umsdos_get_emd_dentry(parent); + ret = PTR_ERR(emd_dentry); + if (IS_ERR(emd_dentry)) + goto out; + /* make sure there's an EMD file */ + ret = -EIO; + if (!emd_dentry->d_inode) { +printk("umsdos_writeentry: no EMD file in %s/%s\n", +parent->d_parent->d_name.name, parent->d_name.name); + goto out_dput; + } if (free_entry) { /* #Specification: EMD file / empty entries - * Unused entry in the EMD file are identify + * Unused entry in the EMD file are identified * by the name_len field equal to 0. However to * help future extension (or bug correction :-( ), * empty entries are filled with 0. @@ -450,34 +458,33 @@ int umsdos_writeentry ( memset (&entry0, 0, sizeof (entry0)); entry = &entry0; } else if (entry->name_len > 0) { - memset (entry->name + entry->name_len, '\0', sizeof (entry->name) - entry->name_len); + memset (entry->name + entry->name_len, '\0', + sizeof (entry->name) - entry->name_len); /* #Specification: EMD file / spare bytes * 10 bytes are unused in each record of the EMD. They - * are set to 0 all the time. So it will be possible + * are set to 0 all the time, so it will be possible * to do new stuff and rely on the state of those - * bytes in old EMD file around. + * bytes in old EMD files. */ memset (entry->spare, 0, sizeof (entry->spare)); } - Printk (("umsdos_writeentry /mn/: if passed...\n")); - if (!info) - printk (KERN_ERR "umsdosfs: /mn/ info is empty ! ooops...\n"); + fill_new_filp (&filp, emd_dentry); filp.f_pos = info->f_pos; filp.f_reada = 0; filp.f_flags = O_RDWR; - filp.f_dentry = emd_dentry; - filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */ - - ret = umsdos_emd_dir_write (emd_dir, &filp, (char *) entry, info->recsize, NULL); - Printk (("emd_dir_write returned with %d!\n", ret)); - if (ret != 0) { - printk ("UMSDOS: problem with EMD file. Can't write\n"); - } else { + + /* write the entry and update the parent timestamps */ + ret = umsdos_emd_dir_write (&filp, (char *) entry, info->recsize); + if (!ret) { dir->i_ctime = dir->i_mtime = CURRENT_TIME; - /* dir->i_dirt = 1; FIXME iput/dput ??? */ - } + mark_inode_dirty(dir); + } else + printk ("UMSDOS: problem with EMD file: can't write\n"); +out_dput: + dput(emd_dentry); +out: Printk (("umsdos_writeentry /mn/: returning %d...\n", ret)); return ret; } @@ -494,25 +501,19 @@ struct find_buffer { - - /* - * Fill the read buffer and take care of the byte remaining inside. - * Unread bytes are simply move to the beginning. + * Fill the read buffer and take care of the bytes remaining inside. + * Unread bytes are simply moved to the beginning. * - * Return -ENOENT if EOF, 0 if ok, a negative error code if any problem. + * Return -ENOENT if EOF, 0 if OK, a negative error code if any problem. */ -static int umsdos_fillbuf ( - struct inode *inode, - struct find_buffer *buf) +static int umsdos_fillbuf (struct find_buffer *buf) { - int ret = -ENOENT; + struct inode *inode = buf->filp.f_dentry->d_inode; int mustmove = buf->size - buf->pos; - int mustread; - int remain; - - PRINTK ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %lu, buf=%p\n", inode->i_ino, buf)); + int mustread, remain; + int ret = -ENOENT; if (mustmove > 0) { memcpy (buf->buffer, buf->buffer + buf->pos, mustmove); @@ -523,7 +524,8 @@ static int umsdos_fillbuf ( if (remain < mustread) mustread = remain; if (mustread > 0) { - ret = umsdos_emd_dir_read (inode, &buf->filp, buf->buffer + mustmove, mustread, NULL); + ret = umsdos_emd_dir_read (&buf->filp, buf->buffer + mustmove, + mustread); if (ret == 0) buf->size = mustmove + mustread; } else if (mustmove) { @@ -540,8 +542,6 @@ static int umsdos_fillbuf ( * store it. if info->entry.name_len == 0, search the first empty * slot (of the proper size). * - * Caller must do iput on *pt_emd_dir. - * * Return 0 if found, -ENOENT if not found, another error code if * other problem. * @@ -553,271 +553,282 @@ static int umsdos_fillbuf ( * To delete an entry, you find it, zero out the entry (memset) * and call umsdos_writeentry(). * - * All this to say that umsdos_writeentry must be call after this - * function since it rely on the f_pos field of info. + * All this to say that umsdos_writeentry must be called after this + * function since it relies on the f_pos field of info. + * + */ +/* #Specification: EMD file structure + * The EMD file uses a fairly simple layout. It is made of records + * (UMSDOS_REC_SIZE == 64). When a name can't be written in a single + * record, multiple contiguous records are allocated. */ -static int umsdos_find ( - struct inode *dir, - struct umsdos_info *info, /* Hold name and name_len */ - /* Will hold the entry found */ - struct inode **pt_emd_dir) /* Will hold the emd_dir inode or NULL if not found */ - +static int umsdos_find (struct dentry *parent, struct umsdos_info *info) { - /* #Specification: EMD file structure - * The EMD file uses a fairly simple layout. It is made of records - * (UMSDOS_REC_SIZE == 64). When a name can't be written is a single - * record, multiple contiguous record are allocated. - */ - int ret = -ENOENT; - struct inode *emd_dir; struct umsdos_dirent *entry = &info->entry; + int recsize = info->recsize; + struct dentry *demd; + struct inode *emd_dir; + int ret = -ENOENT; + struct find_buffer buf; + struct { + off_t posok; /* Position available to store the entry */ + int found; /* A valid empty position has been found. */ + off_t one; /* One empty position -> maybe <- large enough */ + int onesize; /* size of empty region starting at one */ + } empty; + +Printk (("umsdos_find: locating %s in %s/%s\n", +entry->name, parent->d_parent->d_name.name, parent->d_name.name)); + + /* + * Lookup the EMD file in the parent directory. + */ + demd = umsdos_get_emd_dentry(parent); + ret = PTR_ERR(demd); + if (IS_ERR(demd)) + goto out; + /* make sure there's an EMD file ... */ + ret = -ENOENT; + emd_dir = demd->d_inode; + if (!emd_dir) + goto out_dput; + +Printk(("umsdos_find: found EMD file %s/%s, ino=%p\n", +demd->d_parent->d_name.name, demd->d_name.name, emd_dir)); + + fill_new_filp (&buf.filp, demd); + + buf.pos = 0; + buf.size = 0; + + empty.found = 0; + empty.posok = emd_dir->i_size; + empty.onesize = 0; + while (1) { + struct umsdos_dirent *rentry = (struct umsdos_dirent *) + (buf.buffer + buf.pos); + int file_pos = buf.filp.f_pos - buf.size + buf.pos; + + if (buf.pos == buf.size) { + ret = umsdos_fillbuf (&buf); + if (ret < 0) { + /* Not found, so note where it can be added */ + info->f_pos = empty.posok; + break; + } + } else if (rentry->name_len == 0) { + /* We are looking for an empty section at least */ + /* as large as recsize. */ + if (entry->name_len == 0) { + info->f_pos = file_pos; + ret = 0; + break; + } else if (!empty.found) { + if (empty.onesize == 0) { + /* This is the first empty record of a section. */ + empty.one = file_pos; + } + /* grow the empty section */ + empty.onesize += UMSDOS_REC_SIZE; + if (empty.onesize == recsize) { + /* Here is a large enough section. */ + empty.posok = empty.one; + empty.found = 1; + } + } + buf.pos += UMSDOS_REC_SIZE; + } else { + int entry_size = umsdos_evalrecsize (rentry->name_len); - Printk (("umsdos_find: locating %.*s in dir %lu\n", entry->name_len, entry->name, dir->i_ino)); - - emd_dir = umsdos_emd_dir_lookup (dir, 1); - if (emd_dir != NULL) { - int recsize = info->recsize; - struct { - off_t posok; /* Position available to store the entry */ - int found; /* A valid empty position has been found */ - off_t one; /* One empty position -> maybe <- large enough */ - int onesize; /* size of empty region starting at one */ - } empty; - - /* Read several entries at a time to speed up the search */ - struct find_buffer buf; - struct dentry *dentry; - - dentry = creat_dentry ("umsfind-mn", 10, emd_dir, NULL); - - fill_new_filp (&buf.filp, dentry); - - buf.pos = 0; - buf.size = 0; - - empty.found = 0; - empty.posok = emd_dir->i_size; - empty.onesize = 0; - while (1) { - struct umsdos_dirent *rentry = (struct umsdos_dirent *) - (buf.buffer + buf.pos); - int file_pos = buf.filp.f_pos - buf.size + buf.pos; - - if (buf.pos == buf.size) { - ret = umsdos_fillbuf (emd_dir, &buf); + if (buf.pos + entry_size > buf.size) { + ret = umsdos_fillbuf (&buf); if (ret < 0) { /* Not found, so note where it can be added */ info->f_pos = empty.posok; break; } - } else if (rentry->name_len == 0) { - /* We are looking for an empty section at least */ - /* recsize large */ - if (entry->name_len == 0) { + } else { + empty.onesize = 0; /* Reset the free slot search. */ + if (entry->name_len == rentry->name_len + && memcmp (entry->name, rentry->name, rentry->name_len) == 0) { info->f_pos = file_pos; + *entry = *rentry; ret = 0; break; - } else if (!empty.found) { - if (empty.onesize == 0) { - /* This is the first empty record of a section */ - empty.one = file_pos; - } - /* grow the empty section */ - empty.onesize += UMSDOS_REC_SIZE; - if (empty.onesize == recsize) { - /* here is a large enough section */ - empty.posok = empty.one; - empty.found = 1; - } - } - buf.pos += UMSDOS_REC_SIZE; - } else { - int entry_size = umsdos_evalrecsize (rentry->name_len); - - if (buf.pos + entry_size > buf.size) { - ret = umsdos_fillbuf (emd_dir, &buf); - if (ret < 0) { - /* Not found, so note where it can be added */ - info->f_pos = empty.posok; - break; - } } else { - empty.onesize = 0; /* Reset the free slot search */ - if (entry->name_len == rentry->name_len - && memcmp (entry->name, rentry->name, rentry->name_len) - == 0) { - info->f_pos = file_pos; - *entry = *rentry; - ret = 0; - break; - } else { - buf.pos += entry_size; - } + buf.pos += entry_size; } } } - umsdos_manglename (info); } - *pt_emd_dir = emd_dir; + umsdos_manglename (info); + +out_dput: + dput(demd); +out: Printk (("umsdos_find: returning %d\n", ret)); return ret; } /* - * Add a new entry in the emd file - * Return 0 if ok or a negative error code. - * Return -EEXIST if the entry already exist. - * + * Add a new entry in the EMD file. + * Return 0 if OK or a negative error code. + * Return -EEXIST if the entry already exists. + * * Complete the information missing in info. + * + * N.B. What if the EMD file doesn't exist? */ -int umsdos_newentry ( - struct inode *dir, - struct umsdos_info *info) +int umsdos_newentry (struct dentry *parent, struct umsdos_info *info) { - struct inode *emd_dir; - int ret = umsdos_find (dir, info, &emd_dir); + int err, ret = -EEXIST; - if (ret == 0) { - ret = -EEXIST; - } else if (ret == -ENOENT) { - ret = umsdos_writeentry (dir, emd_dir, info, 0); + err = umsdos_find (parent, info); + if (err && err == -ENOENT) { + ret = umsdos_writeentry (parent, info, 0); Printk (("umsdos_writeentry EMD ret = %d\n", ret)); } - iput (emd_dir); /* FIXME? */ return ret; } /* * Create a new hidden link. - * Return 0 if ok, an error code if not. + * Return 0 if OK, an error code if not. */ -int umsdos_newhidden ( - struct inode *dir, - struct umsdos_info *info) +/* #Specification: hard link / hidden name + * When a hard link is created, the original file is renamed + * to a hidden name. The name is "..LINKNNN" where NNN is a + * number define from the entry offset in the EMD file. + */ +int umsdos_newhidden (struct dentry *parent, struct umsdos_info *info) { - struct inode *emd_dir; int ret; umsdos_parse ("..LINK", 6, info); info->entry.name_len = 0; - ret = umsdos_find (dir, info, &emd_dir); - iput (emd_dir); /* FIXME? */ + ret = umsdos_find (parent, info); if (ret == -ENOENT || ret == 0) { - /* #Specification: hard link / hidden name - * When a hard link is created, the original file is renamed - * to a hidden name. The name is "..LINKNNN" where NNN is a - * number define from the entry offset in the EMD file. - */ - info->entry.name_len = sprintf (info->entry.name, "..LINK%ld" - ,info->f_pos); + info->entry.name_len = sprintf (info->entry.name, + "..LINK%ld", info->f_pos); ret = 0; } return ret; } + + /* - * Remove an entry from the emd file - * Return 0 if ok, a negative error code otherwise. + * Remove an entry from the EMD file. + * Return 0 if OK, a negative error code otherwise. * * Complete the information missing in info. */ -int umsdos_delentry ( - struct inode *dir, - struct umsdos_info *info, - int isdir) +int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir) { - struct inode *emd_dir; - int ret = umsdos_find (dir, info, &emd_dir); + int ret; - if (ret == 0) { - if (info->entry.name_len != 0) { - if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) { - if (S_ISDIR (info->entry.mode)) { - ret = -EISDIR; - } else { - ret = -ENOTDIR; - } - } else { - ret = umsdos_writeentry (dir, emd_dir, info, 1); - } + ret = umsdos_find (parent, info); + if (ret) + goto out; + if (info->entry.name_len == 0) + goto out; + + if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) { + if (S_ISDIR (info->entry.mode)) { + ret = -EISDIR; + } else { + ret = -ENOTDIR; } + goto out; } - iput(emd_dir); /* FIXME? */ + ret = umsdos_writeentry (parent, info, 1); + +out: return ret; } /* - * Verify is a EMD directory is empty. - * Return 0 if not empty - * 1 if empty - * 2 if empty, no EMD file. + * Verify that an EMD directory is empty. + * Return: + * 0 if not empty, + * 1 if empty (except for EMD file), + * 2 if empty or no EMD file. */ -int umsdos_isempty (struct inode *dir) +int umsdos_isempty (struct dentry *dentry) { - struct dentry *dentry; - + struct dentry *demd; int ret = 2; - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0); + struct file filp; - /* If the EMD file does not exist, it is certainly empty :-) */ - if (emd_dir != NULL) { - struct file filp; + demd = umsdos_get_emd_dentry(dentry); + if (IS_ERR(demd)) + goto out; + /* If the EMD file does not exist, it is certainly empty. :-) */ + if (!demd->d_inode) + goto out_dput; - dentry = creat_dentry ("isempty-mn", 10, dir, NULL); - fill_new_filp (&filp, dentry); - filp.f_flags = O_RDONLY; + fill_new_filp (&filp, demd); + filp.f_flags = O_RDONLY; - ret = 1; - while (filp.f_pos < emd_dir->i_size) { - struct umsdos_dirent entry; + ret = 1; + while (filp.f_pos < demd->d_inode->i_size) { + struct umsdos_dirent entry; - if (umsdos_emd_dir_readentry (emd_dir, &filp, &entry) != 0) { - ret = 0; - break; - } else if (entry.name_len != 0) { - ret = 0; - break; - } + if (umsdos_emd_dir_readentry (&filp, &entry) != 0) { + ret = 0; + break; + } + if (entry.name_len != 0) { + ret = 0; + break; } - iput (emd_dir); /* FIXME? */ } + +out_dput: + dput(demd); +out: +printk("umsdos_isempty: checked %s/%s, empty=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); + return ret; } /* * Locate an entry in a EMD directory. - * Return 0 if ok, errcod if not, generally -ENOENT. + * Return 0 if OK, error code if not, generally -ENOENT. + * + * does not change i_count */ +/* 0: anything */ +/* 1: file */ +/* 2: directory */ -int umsdos_findentry ( - struct inode *dir, - struct umsdos_info *info, - int expect) -{ /* 0: anything */ - /* 1: file */ - /* 2: directory */ - struct inode *emd_dir; - int ret = umsdos_find (dir, info, &emd_dir); - - if (ret == 0) { - if (expect != 0) { - if (S_ISDIR (info->entry.mode)) { - if (expect != 2) - ret = -EISDIR; - } else if (expect == 2) { - ret = -ENOTDIR; - } +int umsdos_findentry (struct dentry *parent, struct umsdos_info *info, + int expect) +{ + int ret; + + ret = umsdos_find (parent, info); + if (ret) + goto out; + + if (expect != 0) { + if (S_ISDIR (info->entry.mode)) { + if (expect != 2) + ret = -EISDIR; + } else if (expect == 2) { + ret = -ENOTDIR; } } - iput (emd_dir); /* FIXME? */ +out: Printk (("umsdos_findentry: returning %d\n", ret)); return ret; } diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index a9267e122..5277c9548 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -19,10 +19,6 @@ #include <asm/uaccess.h> #include <asm/system.h> -#define PRINTK(x) -#define Printk(x) printk x - - /* * Read a file into user space memory */ @@ -86,6 +82,7 @@ struct file_operations umsdos_file_operations = NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; @@ -123,6 +120,7 @@ struct file_operations umsdos_file_operations_no_bmap = NULL, /* ioctl - default */ fat_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; @@ -160,6 +158,7 @@ struct file_operations umsdos_file_operations_readpage = NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* no special open is needed */ + NULL, /* flush */ NULL, /* release */ file_fsync /* fsync */ }; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 4bf2a65ce..ca20bb8c3 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -3,7 +3,6 @@ * * Written 1993 by Jacques Gelinas * Inspired from linux/fs/msdos/... by Werner Almesberger - * */ #include <linux/module.h> @@ -24,181 +23,60 @@ extern struct inode_operations umsdos_rdir_inode_operations; struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */ - - /* directory. See UMSDOS_readdir_x() */ - -/* #Specification: convention / PRINTK Printk and printk - * Here is the convention for the use of printk inside fs/umsdos - * - * printk carry important message (error or status). - * Printk is for debugging (it is a macro defined at the beginning of - * most source. - * PRINTK is a nulled Printk macro. - * - * This convention makes the source easier to read, and Printk easier - * to shut off. - */ -#define PRINTK(x) -#define Printk(x) printk x - - + /* directory. See UMSDOS_readdir_x() */ /* - * makes return inode->i_dentry - * + * returns inode->i_dentry + * Note: Deprecated; won't work reliably */ -inline struct dentry *geti_dentry (struct inode *inode) +struct dentry *geti_dentry (struct inode *inode) { struct dentry *ret; + if (!inode) { - printk (KERN_ERR "geti_dentry: inode is NULL!\n"); + printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n"); return NULL; } - ret = list_entry (inode->i_dentry.next, struct dentry, d_alias); /* FIXME: does this really work ? :) */ - Printk ((KERN_DEBUG "geti_dentry : inode %lu: i_dentry is %p\n", inode->i_ino, ret)); - return ret; -} - -/* - * makes inode->i_count++ - * - */ + if (list_empty(&inode->i_dentry)) { + printk (KERN_WARNING + "geti_dentry: WARNING: no dentry for inode %ld\n", + inode->i_ino); + return NULL; + } + ret = list_entry (inode->i_dentry.next, struct dentry, d_alias); -inline void inc_count (struct inode *inode) -{ - inode->i_count++; - Printk ((KERN_DEBUG "inc_count: inode %lu incremented count to %d\n", inode->i_ino, inode->i_count)); + PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n", + inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name)); + return ret; } /* - * makes empty filp - * + * Initialize a private filp */ - void fill_new_filp (struct file *filp, struct dentry *dentry) { - Printk (("/mn/ fill_new_filp: filling empty filp at %p\n", filp)); - if (dentry) - Printk ((" dentry=%.*s\n", (int) dentry->d_name.len, dentry->d_name.name)); - else - Printk ((" dentry is NULL ! you must fill it later...\n")); + if (!dentry) + printk("fill_new_filp: NULL dentry!\n"); memset (filp, 0, sizeof (struct file)); - - filp->f_pos = 0; filp->f_reada = 1; filp->f_flags = O_RDWR; filp->f_dentry = dentry; - filp->f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with SOMETHING */ + filp->f_op = &umsdos_file_operations; } -/* - * check a superblock - */ - -void check_sb (struct super_block *sb, const char c) -{ - if (sb) { - Printk ((" (has %c_sb=%d, %d)", c, MAJOR (sb->s_dev), MINOR (sb->s_dev))); - } else { - Printk ((" (%c_sb is NULL)", c)); - } -} /* - * check an inode - */ - -void check_inode (struct inode *inode) -{ - if (inode) { - Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)", inode->i_ino, inode->i_count)); - check_sb (inode->i_sb, 'i'); - - if (inode->i_dentry.next) { /* FIXME: does this work ? */ - Printk ((" (has i_dentry)")); - } else { - Printk ((" (NO i_dentry)")); - } - - 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")); - } -} - - -/* - * internal part of check_dentry. does the real job. - * - */ - -void check_dent_int (struct dentry *dentry, int parent) -{ - if (parent) { - Printk ((KERN_DEBUG "* parent dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name)); - } else { - Printk ((KERN_DEBUG "\n*** checking dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name)); - } - check_inode (dentry->d_inode); - Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count)); - check_sb (dentry->d_sb, 'd'); - if (dentry->d_op == NULL) { - Printk ((" (d_op is NULL)\n")); - } else { - Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op)); - } -} - -/* - * checks dentry and prints info - * - */ - -void check_dentry (struct dentry *dentry) -{ - if (!dentry) { - Printk ((KERN_DEBUG "\n*** checking dentry... it is NULL !\n")); - return; - } - check_dent_int (dentry, 0); - - if (dentry->d_parent) { - check_dent_int (dentry->d_parent, 1); - } else { - Printk ((KERN_DEBUG "* has no parent.\n")); - } - - Printk ((KERN_DEBUG "*** end checking dentry\n")); -} - - -/* - * makes dentry. for name name with length len. /mn/ + * makes dentry. for name name with length len. * if inode is not NULL, puts it also. - * + * Note: Deprecated; use umsdos_lookup_dentry */ -struct dentry *creat_dentry (const char *name, const int len, struct inode *inode, struct dentry *parent) +struct dentry *creat_dentry (const char *name, const int len, + struct inode *inode, struct dentry *parent) { /* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */ @@ -206,73 +84,58 @@ struct dentry *creat_dentry (const char *name, const int len, struct inode *inod struct qstr qname; if (inode) - Printk ((KERN_DEBUG "/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name)); + Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name)); else - Printk ((KERN_DEBUG "/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name)); + Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name)); qname.name = name; qname.len = len; - qname.hash = 0; + qname.hash = full_name_hash (name, len); ret = d_alloc (parent, &qname); /* create new dentry */ - ret->d_inode = NULL; - if (!parent) { + if (parent) { +#if 0 + Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n")); + ret->d_op = parent->d_op; +#else + ret->d_op = NULL; +#endif + } else { ret->d_parent = ret; - Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking! beware !\n")); + Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n")); } if (inode) { - ret->d_sb = inode->i_sb; /* try to fill it in if avalaible. If avalaible in parent->d_sb, d_alloc will add it automatically */ + /* try to fill it in if available. If available in + * parent->d_sb, d_alloc will add it automatically + */ + if (!ret->d_sb) ret->d_sb = inode->i_sb; d_add (ret, inode); } - return ret; -} - - -/* - * removes temporary dentry created by creat_dentry - * - */ - -void kill_dentry (struct dentry *dentry) -{ - if (dentry) { - Printk (("/mn/ kill_dentry: kill_dentry %.*s :", (int) dentry->d_name.len, dentry->d_name.name)); - if (dentry->d_inode) - Printk (("inode=%lu (i_count=%d)\n", dentry->d_inode->i_ino, dentry->d_inode->i_count)); - else - Printk (("inode is NULL\n")); - - /* FIXME: is this ok ?! /mn/ */ - /* d_drop (dentry); */ - /* d_invalidate (dentry); */ - /*dput (dentry); */ - /*d_delete (dentry) */ - } else { - Printk (("/mn/ kill_dentry: dentry is NULL ?!\n")); + if (!ret->d_sb) { + printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n"); + } else if (!ret->d_sb->s_dev) { + printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n"); } - - - Printk ((KERN_DEBUG "/mn/ kill_dentry: exiting...\n")); - return; + + return ret; } - - - - void UMSDOS_put_inode (struct inode *inode) { - PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n", inode, inode->i_ino + PRINTK ((KERN_DEBUG + "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n" + ,inode, inode->i_ino ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos ,inode->u.umsdos_i.i_emd_dir, inode->i_count)); if (inode && pseudo_root && inode == pseudo_root) { - printk (KERN_ERR "Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n"); + printk (KERN_ERR "Umsdos: Oops releasing pseudo_root." + " Notify jacques@solucorp.qc.ca\n"); } fat_put_inode (inode); @@ -287,20 +150,17 @@ void UMSDOS_put_super (struct super_block *sb) } - /* * Call msdos_lookup, but set back the original msdos function table. - * Return 0 if ok, or a negative error code if not. + * Return 0 if OK, or a negative error code if not. + * Dentry will hold inode of the file, if successful */ -int umsdos_real_lookup ( - struct inode *dir, - struct dentry *dentry -) -{ /* Will hold inode of the file, if successful */ +int umsdos_real_lookup (struct inode *dir, struct dentry *dentry) +{ int ret; - PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s /", dentry->d_name.name)); - inc_count (dir); /* should be here ? Causes all kind of missing iput()'s all around, but panics w/o it /mn/ */ + PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s/%s /", + dentry->d_parent->d_name.name, dentry->d_name.name)); ret = msdos_lookup (dir, dentry); PRINTK (("/ returned %d\n", ret)); @@ -308,29 +168,61 @@ int umsdos_real_lookup ( } /* - * Complete the setup of an directory inode. + * Complete the setup of an directory dentry. * First, it completes the function pointers, then * it locates the EMD file. If the EMD is there, then plug the * umsdos function table. If not, use the msdos one. + * + * {i,d}_counts are untouched by this function. */ -void umsdos_setup_dir_inode (struct inode *inode) +void umsdos_setup_dir(struct dentry *dir) { + struct inode *inode = dir->d_inode; + + if (!S_ISDIR(inode->i_mode)) + printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n", + dir->d_parent->d_name.name, dir->d_name.name); + inode->u.umsdos_i.i_emd_dir = 0; - { - struct inode *emd_dir; + inode->i_op = &umsdos_rdir_inode_operations; + if (umsdos_have_emd(dir)) { +Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n", +dir->d_parent->d_name.name, dir->d_name.name)); + inode->i_op = &umsdos_dir_inode_operations; + } +} - emd_dir = umsdos_emd_dir_lookup (inode, 0); - Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%p returned %p\n", inode, emd_dir)); +/* + * Complete the setup of an directory inode. + * First, it completes the function pointers, then + * it locates the EMD file. If the EMD is there, then plug the + * umsdos function table. If not, use the msdos one. + * + * {i,d}_counts are untouched by this function. + * Note: Deprecated; use above function if possible. + */ +void umsdos_setup_dir_inode (struct inode *inode) +{ + struct inode *emd_dir; - if (emd_dir == NULL) { - Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. NOT using EMD.\n")); - inode->i_op = &umsdos_rdir_inode_operations; - } else { - Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. using EMD.\n")); - inode->i_op = &umsdos_dir_inode_operations; - } + inode->u.umsdos_i.i_emd_dir = 0; - iput (emd_dir); /* FIXME? OK? */ + Printk ((KERN_DEBUG + "umsdos_setup_dir_inode: Entering for inode=%lu\n", + inode->i_ino)); + check_inode (inode); + emd_dir = umsdos_emd_dir_lookup (inode, 0); + Printk ((KERN_DEBUG "umsdos_setup_dir_inode: " + "umsdos_emd_dir_lookup for inode=%lu returned %p\n", + inode->i_ino, emd_dir)); + check_inode (inode); + check_inode (emd_dir); + + inode->i_op = &umsdos_rdir_inode_operations; + if (emd_dir) { + Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n")); + inode->i_op = &umsdos_dir_inode_operations; + iput (emd_dir); } } @@ -338,21 +230,20 @@ void umsdos_setup_dir_inode (struct inode *inode) /* * Add some info into an inode so it can find its owner quickly */ -void umsdos_set_dirinfo ( - struct inode *inode, - struct inode *dir, - off_t f_pos) +void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos) { - struct inode *emd_owner; + struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1); - /* FIXME, I don't have a clue on this one - /mn/ hmmm ? ok ? */ -/* Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue. inode=%lu dir=%lu\n", inode->i_ino, dir->i_ino)); */ - emd_owner = umsdos_emd_dir_lookup (dir, 1); - Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino)); + if (!emd_owner) + goto out; + Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", + emd_owner->i_ino, dir->i_ino)); inode->u.umsdos_i.i_dir_owner = dir->i_ino; inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino; - iput (emd_owner); /* FIXME? */ inode->u.umsdos_i.pos = f_pos; + iput (emd_owner); +out: + return; } @@ -362,150 +253,143 @@ void umsdos_set_dirinfo ( */ int umsdos_isinit (struct inode *inode) { -#if 1 return inode->u.umsdos_i.i_emd_owner != 0; -#elif 0 - return inode->i_atime != 0; -#else - return atomic_read (&inode->i_count) > 1; -#endif } /* * Connect the proper tables in the inode and add some info. + * i_counts is not changed. + * + * This function is called very early to setup the inode, somewhat + * too early (called by UMSDOS_read_inode). At this point, we can't + * do too much, such as lookup up EMD files and so on. This causes + * confusion in the kernel. This is why some initialisation + * will be done when dir != NULL only. + * + * UMSDOS do run piggy back on top of msdos fs. It looks like something + * is missing in the VFS to accommodate stacked fs. Still unclear what + * (quite honestly). + * + * Well, maybe one! A new entry "may_unmount" which would allow + * the stacked fs to allocate some inode permanently and release + * them at the end. Doing that now introduce a problem. unmount + * always fail because some inodes are in use. + */ +/* #Specification: inode / umsdos info + * The first time an inode is seen (inode->i_count == 1), + * the inode number of the EMD file which controls this inode + * is tagged to this inode. It allows operations such as + * notify_change to be handled. */ -void umsdos_patch_inode ( - struct inode *inode, - struct inode *dir, /* May be NULL */ - off_t f_pos) +void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos) { - /* - * This function is called very early to setup the inode, somewhat - * too early (called by UMSDOS_read_inode). At this point, we can't - * do to much, such as lookup up EMD files and so on. This causes - * confusion in the kernel. This is why some initialisation - * will be done when dir != NULL only. - * - * UMSDOS do run piggy back on top of msdos fs. It looks like something - * is missing in the VFS to accommodate stacked fs. Still unclear what - * (quite honestly). - * - * Well, maybe one! A new entry "may_unmount" which would allow - * the stacked fs to allocate some inode permanently and release - * them at the end. Doing that now introduce a problem. unmount - * always fail because some inodes are in use. - */ + Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", + inode->i_ino)); + + if (umsdos_isinit (inode)) + goto already_init; - Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", inode->i_ino)); - - if (!umsdos_isinit (inode)) { - inode->u.umsdos_i.i_emd_dir = 0; - 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) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_readpage\n")); - inode->i_op = &umsdos_file_inode_operations_readpage; - } else { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n")); - inode->i_op = &umsdos_file_inode_operations_no_bmap; - } + inode->u.umsdos_i.i_emd_dir = 0; + 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) { +Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n")); + inode->i_op = &umsdos_file_inode_operations_readpage; } else { - if (inode->i_op->bmap != NULL) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n")); - inode->i_op = &umsdos_file_inode_operations; - } else { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n")); - inode->i_op = &umsdos_file_inode_operations_no_bmap; - } +Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n")); + inode->i_op = &umsdos_file_inode_operations_no_bmap; } - } else if (S_ISDIR (inode->i_mode)) { - if (dir != NULL) { - umsdos_setup_dir_inode (inode); + } else { + if (inode->i_op->bmap != NULL) { +Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops\n")); + inode->i_op = &umsdos_file_inode_operations; + } else { +Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n")); + inode->i_op = &umsdos_file_inode_operations_no_bmap; } - } else if (S_ISLNK (inode->i_mode)) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_symlink_inode_operations\n")); - inode->i_op = &umsdos_symlink_inode_operations; - } else if (S_ISCHR (inode->i_mode)) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = chrdev_inode_operations\n")); - inode->i_op = &chrdev_inode_operations; - } else if (S_ISBLK (inode->i_mode)) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = blkdev_inode_operations\n")); - inode->i_op = &blkdev_inode_operations; - } else if (S_ISFIFO (inode->i_mode)) { - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n")); - init_fifo (inode); } + } else if (S_ISDIR (inode->i_mode)) { if (dir != NULL) { - /* #Specification: inode / umsdos info - * The first time an inode is seen (inode->i_count == 1), - * the inode number of the EMD file which control this inode - * is tagged to this inode. It allows operation such - * as notify_change to be handled. - */ - /* - * This is done last because it also control the - * status of umsdos_isinit() - */ - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: here we go: calling umsdos_set_dirinfo (%p,%p,%lu)\n", inode, dir, f_pos)); - umsdos_set_dirinfo (inode, dir, f_pos); + umsdos_setup_dir_inode (inode); } - } else if (dir != NULL) { + } else if (S_ISLNK (inode->i_mode)) { + Printk ((KERN_DEBUG + "umsdos_patch_inode: umsdos_symlink_inode_ops\n")); + inode->i_op = &umsdos_symlink_inode_operations; + } else if (S_ISCHR (inode->i_mode)) { + Printk ((KERN_DEBUG "umsdos_patch_inode: chrdev_inode_ops\n")); + inode->i_op = &chrdev_inode_operations; + } else if (S_ISBLK (inode->i_mode)) { + Printk ((KERN_DEBUG "umsdos_patch_inode: blkdev_inode_ops\n")); + inode->i_op = &blkdev_inode_operations; + } else if (S_ISFIFO (inode->i_mode)) { + Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n")); + init_fifo (inode); + } + if (dir != NULL) { + /* + * This is done last because it also control the + * status of umsdos_isinit() + */ + Printk ((KERN_DEBUG + "umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n", + inode, dir, f_pos)); + umsdos_set_dirinfo (inode, dir, f_pos); + } + return; + +already_init: + if (dir != NULL) { /* * Test to see if the info is maintained. * This should be removed when the file system will be proven. */ - /* FIXME, again, not a clue */ - struct inode *emd_owner; - - Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n")); - emd_owner = umsdos_emd_dir_lookup (dir, 1); - iput (emd_owner); /* FIXME? */ + struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1); + if (!emd_owner) + goto out; if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner) { - printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld " - ,inode->i_ino, emd_owner->i_ino, inode->u.umsdos_i.i_emd_owner); +printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld ", +inode->i_ino, emd_owner->i_ino, inode->u.umsdos_i.i_emd_owner); } + iput (emd_owner); + out: + return; } } - /* - * Get the inode of the directory which owns this inode. - * Return 0 if ok, -EIO if error. + * Patch the inode in a dentry. */ -int umsdos_get_dirowner ( - struct inode *inode, - struct inode **result) -{ /* Hold NULL if any error */ - /* else, the inode of the directory */ - int ret = -EIO; - unsigned long ino = inode->u.umsdos_i.i_dir_owner; - - *result = NULL; - if (ino == 0) { - printk ("UMSDOS: umsdos_get_dirowner ino == 0\n"); - } else { - struct inode *dir = *result = iget (inode->i_sb, ino); - - if (dir != NULL) { - umsdos_patch_inode (dir, NULL, 0); - /* iput (dir); / * FIXME: /mn/ added this. Is it ok ? */ - ret = 0; - } - } - return ret; +void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos) +{ + umsdos_patch_inode(dentry->d_inode, dentry->d_parent->d_inode, f_pos); } - /* * Load an inode from disk. */ +/* #Specification: Inode / post initialisation + * To completely initialise an inode, we need access to the owner + * directory, so we can locate more info in the EMD file. This is + * not available the first time the inode is access, we use + * a value in the inode to tell if it has been finally initialised. + * + * At first, we have tried testing i_count but it was causing + * problem. It is possible that two or more process use the + * newly accessed inode. While the first one block during + * the initialisation (probably while reading the EMD file), the + * others believe all is well because i_count > 1. They go banana + * with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode. + */ void UMSDOS_read_inode (struct inode *inode) { - Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", inode, inode->i_ino)); + PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", + inode, inode->i_ino)); msdos_read_inode (inode); - Printk (("ino after msdos_read_inode= %lu i_count=%d\n", inode->i_ino, inode->i_count)); + PRINTK (("ino after msdos_read_inode= %lu i_count=%d\n", + inode->i_ino, inode->i_count)); if (S_ISDIR (inode->i_mode) && (inode->u.umsdos_i.u.dir_info.creating != 0 || inode->u.umsdos_i.u.dir_info.looking != 0 @@ -515,121 +399,92 @@ void UMSDOS_read_inode (struct inode *inode) ,inode->u.umsdos_i.u.dir_info.looking ,inode->u.umsdos_i.u.dir_info.p)); } - /* #Specification: Inode / post initialisation - * To completely initialise an inode, we need access to the owner - * directory, so we can locate more info in the EMD file. This is - * not available the first time the inode is access, we use - * a value in the inode to tell if it has been finally initialised. - * - * At first, we have tried testing i_count but it was causing - * problem. It is possible that two or more process use the - * newly accessed inode. While the first one block during - * the initialisation (probably while reading the EMD file), the - * others believe all is well because i_count > 1. They go banana - * with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode. - */ + + /* N.B. Defer this until we have a dentry ... */ umsdos_patch_inode (inode, NULL, 0); } -int internal_notify_change (struct inode *inode, struct iattr *attr) +/* #Specification: notify_change / i_nlink > 0 + * notify change is only done for inode with nlink > 0. An inode + * with nlink == 0 is no longer associated with any entry in + * the EMD file, so there is nothing to update. + */ +static int internal_notify_change (struct inode *inode, struct iattr *attr) { - int ret = 0; - struct inode *root; + unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner; + int ret; - PRINTK ((KERN_DEBUG "UMSDOS_notify_change: entering\n")); + Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n")); if ((ret = inode_change_ok (inode, attr)) != 0) - return ret; + goto out; + + if (inode->i_nlink == 0) + goto out_nolink; - if (inode->i_nlink > 0) { - /* #Specification: notify_change / i_nlink > 0 - * notify change is only done for inode with nlink > 0. An inode - * with nlink == 0 is no longer associated with any entry in - * the EMD file, so there is nothing to update. + if (inode->i_ino == UMSDOS_ROOT_INO) + goto out_nolink; + + if (i_emd_owner != 0xffffffff && i_emd_owner != 0) { + /* This inode is not a EMD file nor an inode used internally + * by MSDOS, so we can update its status. + * See emd.c */ - unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner; - - root = iget (inode->i_sb, UMSDOS_ROOT_INO); - if (inode == root) { - /* #Specification: root inode / attributes - * I don't know yet how this should work. Normally - * the attributes (permissions bits, owner, times) of - * a directory are stored in the EMD file of its parent. - * - * One thing we could do is store the attributes of the root - * inode in its own EMD file. A simple entry named "." could - * be used for this special case. It would be read once - * when the file system is mounted and update in - * UMSDOS_notify_change() (right here). - * - * I am not sure of the behavior of the root inode for - * a real UNIX file system. For now, this is a nop. - */ - } else if (i_emd_owner != 0xffffffff && i_emd_owner != 0) { - /* This inode is not a EMD file nor an inode used internally - * by MSDOS, so we can update its status. - * See emd.c + struct inode *emd_owner; + struct file filp; + struct umsdos_dirent entry; + struct dentry *emd_dentry; + + Printk (("notify change %p ", inode)); + ret = -EPERM; + emd_owner = iget (inode->i_sb, i_emd_owner); + if (!emd_owner) { + printk ("UMSDOS: emd_owner = NULL ???"); + goto out_nolink; + } + emd_dentry = geti_dentry (emd_owner); /* FIXME? */ + fill_new_filp (&filp, emd_dentry); + + filp.f_pos = inode->u.umsdos_i.pos; + filp.f_reada = 0; + Printk (("pos = %Lu ", filp.f_pos)); + /* Read only the start of the entry since we don't touch */ + /* the name */ + ret = umsdos_emd_dir_read (&filp, (char *) &entry, + UMSDOS_REC_SIZE); + if (!ret) { + if (attr->ia_valid & ATTR_UID) + entry.uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + entry.gid = attr->ia_gid; + if (attr->ia_valid & ATTR_MODE) + entry.mode = attr->ia_mode; + if (attr->ia_valid & ATTR_ATIME) + entry.atime = attr->ia_atime; + if (attr->ia_valid & ATTR_MTIME) + entry.mtime = attr->ia_mtime; + if (attr->ia_valid & ATTR_CTIME) + entry.ctime = attr->ia_ctime; + + entry.nlink = inode->i_nlink; + filp.f_pos = inode->u.umsdos_i.pos; + ret = umsdos_emd_dir_write (&filp, (char *) &entry, + UMSDOS_REC_SIZE); + + Printk (("notify pos %lu ret %d nlink %d ", + inode->u.umsdos_i.pos, ret, entry.nlink)); + /* #Specification: notify_change / msdos fs + * notify_change operation are done only on the + * EMD file. The msdos fs is not even called. */ - struct inode *emd_owner; - - emd_owner = iget (inode->i_sb, i_emd_owner); - Printk (("notify change %p ", inode)); - if (emd_owner == NULL) { - printk ("UMSDOS: emd_owner = NULL ???"); - ret = -EPERM; - } else { - struct file filp; - struct umsdos_dirent entry; - struct dentry *emd_dentry; - loff_t offs; - - emd_dentry = creat_dentry ("notify_emd", 10, emd_owner, NULL); - fill_new_filp (&filp, emd_dentry); - - filp.f_pos = inode->u.umsdos_i.pos; - filp.f_reada = 0; - offs = filp.f_pos; /* FIXME: /mn/ is this ok ? */ - Printk (("pos = %Lu ", filp.f_pos)); - /* Read only the start of the entry since we don't touch */ - /* the name */ - ret = umsdos_emd_dir_read (emd_owner, &filp, (char *) &entry, UMSDOS_REC_SIZE, &offs); - if (ret == 0) { - if (attr->ia_valid & ATTR_UID) - entry.uid = attr->ia_uid; - if (attr->ia_valid & ATTR_GID) - entry.gid = attr->ia_gid; - if (attr->ia_valid & ATTR_MODE) - entry.mode = attr->ia_mode; - if (attr->ia_valid & ATTR_ATIME) - entry.atime = attr->ia_atime; - if (attr->ia_valid & ATTR_MTIME) - entry.mtime = attr->ia_mtime; - if (attr->ia_valid & ATTR_CTIME) - entry.ctime = attr->ia_ctime; - - entry.nlink = inode->i_nlink; - filp.f_pos = inode->u.umsdos_i.pos; - offs = filp.f_pos; /* FIXME: /mn/ is this ok ? */ - ret = umsdos_emd_dir_write (emd_owner, &filp, (char *) &entry, UMSDOS_REC_SIZE, &offs); - - Printk (("notify pos %lu ret %d nlink %d " - ,inode->u.umsdos_i.pos - ,ret, entry.nlink)); - /* #Specification: notify_change / msdos fs - * notify_change operation are done only on the - * EMD file. The msdos fs is not even called. - */ - } - iput (emd_owner); /* FIXME? /mn/ */ - } - Printk (("\n")); } - iput (root); /* FIXME - /mn/ this is hopefully ok */ + iput(emd_owner); } +out_nolink: if (ret == 0) inode_setattr (inode, attr); - +out: return ret; } @@ -646,7 +501,8 @@ void UMSDOS_write_inode (struct inode *inode) { struct iattr newattrs; - PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n", inode->u.umsdos_i.i_emd_owner)); + PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n", + inode->u.umsdos_i.i_emd_owner)); fat_write_inode (inode); newattrs.ia_mtime = inode->i_mtime; newattrs.ia_atime = inode->i_atime; @@ -657,26 +513,12 @@ void UMSDOS_write_inode (struct inode *inode) * to update the EMD entry associated with this inode. * But it has the side effect to re"dirt" the inode. */ - /* * internal_notify_change (inode, &newattrs); - * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work... we need to remove ourselfs from list on dirty inodes /mn/ */ + * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */ } - - - -/* #Specification: function name / convention - * A simple convention for function name has been used in - * the UMSDOS file system. First all function use the prefix - * umsdos_ to avoid name clash with other part of the kernel. - * - * And standard VFS entry point use the prefix UMSDOS (upper case) - * so it's easier to tell them apart. - * N.B. (FIXME) PTW, the order and contents of this struct changed - */ - static struct super_operations umsdos_sops = { UMSDOS_read_inode, /* read_inode */ @@ -693,149 +535,127 @@ static struct super_operations umsdos_sops = /* * Read the super block of an Extended MS-DOS FS. */ -struct super_block *UMSDOS_read_super ( - struct super_block *sb, - void *data, - int silent) +struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, + int silent) { - /* #Specification: mount / options - * Umsdos run on top of msdos. Currently, it supports no - * mount option, but happily pass all option received to - * the msdos driver. I am not sure if all msdos mount option - * make sense with Umsdos. Here are at least those who - * are useful. - * uid= - * gid= - * - * These options affect the operation of umsdos in directories - * which do not have an EMD file. They behave like normal - * msdos directory, with all limitation of msdos. - */ struct super_block *res; struct inode *pseudo = NULL; - Printk ((KERN_DEBUG "UMSDOS /mn/: starting UMSDOS_read_super\n")); MOD_INC_USE_COUNT; - PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n", sb)); + MSDOS_SB(sb)->options.isvfat = 0; + /* + * Call msdos-fs to mount the disk. + * Note: this returns res == sb or NULL + */ res = msdos_read_super (sb, data, silent); - PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n", res)); - printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-4 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); + if (!res) + goto out_fail; - if (res == NULL) { - MOD_DEC_USE_COUNT; - return NULL; - } - MSDOS_SB (res)->options.dotsOK = 0; /* disable hidden==dotfile */ - res->s_op = &umsdos_sops; - Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n")); + printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 " + "(compatibility level %d.%d, fast msdos)\n", + UMSDOS_VERSION, UMSDOS_RELEASE); - pseudo = iget (res, UMSDOS_ROOT_INO); - Printk ((KERN_DEBUG "umsdos_read_super %p\n", pseudo)); + sb->s_op = &umsdos_sops; + MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */ - umsdos_setup_dir_inode (pseudo); - Printk ((KERN_DEBUG "umsdos_setup_dir_inode passed. pseudo i_count=%d\n", pseudo->i_count)); + /* FIXME:?? clear d_op on root so it will not be inherited */ + sb->s_root->d_op = NULL; - /* if (s == super_blocks){ FIXME, super_blocks no longer exported */ + pseudo = sb->s_root->d_inode; + umsdos_setup_dir(sb->s_root); + +#if 0 if (pseudo) { - /* #Specification: pseudo root / mount - * When a umsdos fs is mounted, a special handling is done - * if it is the root partition. We check for the presence - * of the file /linux/etc/init or /linux/etc/rc or - * /linux/sbin/init. If one is there, we do a chroot("/linux"). - * - * We check both because (see init/main.c) the kernel - * try to exec init at different place and if it fails - * it tries /bin/sh /etc/rc. To be consistent with - * init/main.c, many more test would have to be done - * to locate init. Any complain ? - * - * The chroot is done manually in init/main.c but the - * info (the inode) is located at mount time and store - * in a global variable (pseudo_root) which is used at - * different place in the umsdos driver. There is no - * need to store this variable elsewhere because it - * will always be one, not one per mount. - * - * This feature allows the installation - * of a linux system within a DOS system in a subdirectory. - * - * A user may install its linux stuff in c:\linux - * avoiding any clash with existing DOS file and subdirectory. - * When linux boots, it hides this fact, showing a normal - * root directory with /etc /bin /tmp ... - * - * The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h - * in the macro UMSDOS_PSDROOT_NAME. - */ - struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL; + pseudo_root_stuff(); + } +#endif - root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen (UMSDOS_PSDROOT_NAME), NULL, NULL); - sbin = creat_dentry ("sbin", 4, NULL, NULL); + /* if d_count is not 1, mount will fail with -EBUSY! */ + if (sb->s_root->d_count > 1) { + shrink_dcache_sb(sb); + } + return sb; - Printk ((KERN_DEBUG "Mounting root\n")); - if (umsdos_real_lookup (pseudo, root) == 0 - && (root->d_inode != NULL) - && S_ISDIR (root->d_inode->i_mode)) { +out_fail: + printk(KERN_INFO "UMSDOS: msdos_read_super failed, mount aborted.\n"); + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; +} - int pseudo_ok = 0; +/* + * FIXME URGENT: + * disable pseudo root-for the moment of testing. + * re-enable this before release ! + */ +#if 0 +void pseudo_root_stuff(void) +{ + struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL; - Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME)); - etc = creat_dentry ("etc", 3, NULL, root); + root = creat_dentry (UMSDOS_PSDROOT_NAME, + strlen (UMSDOS_PSDROOT_NAME), + NULL, res->s_root); + sbin = creat_dentry ("sbin", 4, NULL, root); + Printk ((KERN_DEBUG "Mounting root\n")); + if (umsdos_real_lookup (pseudo, root) == 0 + && (root->d_inode != NULL) + && S_ISDIR (root->d_inode->i_mode)) { - if (umsdos_real_lookup (pseudo, etc) == 0 - && S_ISDIR (etc->d_inode->i_mode)) { + int pseudo_ok = 0; - Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME)); +Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME)); + etc = creat_dentry ("etc", 3, NULL, root); - init = creat_dentry ("init", 4, NULL, etc); - etc_rc = creat_dentry ("rc", 2, NULL, etc); - /* if ((umsdos_real_lookup (etc,"init",4,init)==0 */ - if ((umsdos_real_lookup (pseudo, init) == 0 - && S_ISREG (init->d_inode->i_mode)) - /* || (umsdos_real_lookup (etc,"rc",2,&rc)==0 */ - || (umsdos_real_lookup (pseudo, etc_rc) == 0 - && S_ISREG (etc_rc->d_inode->i_mode))) { - pseudo_ok = 1; - } - /* FIXME !!!!!! */ - /* iput(init); */ - /* iput(rc); */ - } - if (!pseudo_ok - /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */ - && umsdos_real_lookup (pseudo, sbin) == 0 - && S_ISDIR (sbin->d_inode->i_mode)) { - - Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME)); - /* if (umsdos_real_lookup (sbin,"init",4,init)==0 */ - if (umsdos_real_lookup (pseudo, init) == 0 - && S_ISREG (init->d_inode->i_mode)) { - pseudo_ok = 1; - } - /* FIXME !!! - * iput (init); */ + if (umsdos_real_lookup (pseudo, etc) == 0 + && S_ISDIR (etc->d_inode->i_mode)) { + +Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME)); + + init = creat_dentry ("init", 4, NULL, etc); + etc_rc = creat_dentry ("rc", 2, NULL, etc); + + if ((umsdos_real_lookup (pseudo, init) == 0 + && S_ISREG (init->d_inode->i_mode)) + || (umsdos_real_lookup (pseudo, etc_rc) == 0 + && S_ISREG (etc_rc->d_inode->i_mode))) { + pseudo_ok = 1; } - if (pseudo_ok) { - umsdos_setup_dir_inode (pseudo); - Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME)); - pseudo_root = pseudo; - inc_count (pseudo); - pseudo = NULL; + + /* FIXME !!!!!! */ + /* iput(init); */ + /* iput(rc); */ + } + if (!pseudo_ok + /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */ + && umsdos_real_lookup (pseudo, sbin) == 0 + && S_ISDIR (sbin->d_inode->i_mode)) { + +Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME)); + if (umsdos_real_lookup (pseudo, init) == 0 + && S_ISREG (init->d_inode->i_mode)) { + pseudo_ok = 1; } - /* FIXME - * - * iput (sbin); - * iput (etc); - */ + /* FIXME !!! + * iput (init); */ + } + if (pseudo_ok) { + umsdos_setup_dir_inode (pseudo); +Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME)); + pseudo_root = pseudo; + inc_count (pseudo); + pseudo = NULL; } - iput (pseudo); + /* FIXME + * + * iput (sbin); + * iput (etc); + */ } - Printk ((KERN_DEBUG "umsdos_read_super /mn/: (pseudo=%lu, i_count=%d) returning %p\n", pseudo->i_ino, pseudo->i_count, res)); - return res; } - +#endif static struct file_system_type umsdos_fs_type = diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index a379ba368..84e1f5034 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -15,9 +15,6 @@ #include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> -#define PRINTK(x) -#define Printk(x) printk x - struct UMSDOS_DIR_ONCE { struct dirent *ent; int count; @@ -53,14 +50,33 @@ static int umsdos_ioctl_fill ( /* * Perform special function on a directory */ -int UMSDOS_ioctl_dir ( - struct inode *dir, - struct file *filp, - unsigned int cmd, - unsigned long data) +/* #Specification: ioctl / prototypes + * The official prototype for the umsdos ioctl on directory + * is: + * + * int ioctl ( + * int fd, // File handle of the directory + * int cmd, // command + * struct umsdos_ioctl *data) + * + * The struct and the commands are defined in linux/umsdos_fs.h. + * + * umsdos_progs/umsdosio.c provide an interface in C++ to all + * these ioctl. umsdos_progs/udosctl is a small utility showing + * all this. + * + * These ioctl generally allow one to work on the EMD or the + * DOS directory independently. These are essential to implement + * the synchronise. + */ +int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, + unsigned long data_ptr) { - int ret = -EPERM; - int err; + struct dentry *dentry = filp->f_dentry; + struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr; + int ret; + struct file new_filp; + struct umsdos_ioctl data; /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ if (cmd != UMSDOS_GETVERSION @@ -74,7 +90,7 @@ int UMSDOS_ioctl_dir ( && cmd != UMSDOS_RMDIR_DOS && cmd != UMSDOS_STAT_DOS && cmd != UMSDOS_DOS_SETUP) - return fat_dir_ioctl (dir, filp, cmd, data); + return fat_dir_ioctl (dir, filp, cmd, data_ptr); /* #Specification: ioctl / acces * Only root (effective id) is allowed to do IOCTL on directory @@ -85,275 +101,315 @@ int UMSDOS_ioctl_dir ( * the code, and let's face it, there is only one client (umssync) * for all this. */ - if ((err = verify_area (VERIFY_WRITE, (void *) data, sizeof (struct umsdos_ioctl))) < 0) { - ret = err; - } else if (current->euid == 0 - || cmd == UMSDOS_GETVERSION) { - struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data; + ret = verify_area (VERIFY_WRITE, (void *) data_ptr, + sizeof (struct umsdos_ioctl)); + if (ret < 0) + goto out; - ret = -EINVAL; - /* #Specification: ioctl / prototypes - * The official prototype for the umsdos ioctl on directory - * is: - * - * int ioctl ( - * int fd, // File handle of the directory - * int cmd, // command - * struct umsdos_ioctl *data) - * - * The struct and the commands are defined in linux/umsdos_fs.h. + ret = -EPERM; + if (current->euid != 0 && cmd != UMSDOS_GETVERSION) + goto out; + + ret = -EINVAL; + if (cmd == UMSDOS_GETVERSION) { + /* #Specification: ioctl / UMSDOS_GETVERSION + * The field version and release of the structure + * umsdos_ioctl are filled with the version and release + * number of the fs code in the kernel. This will allow + * some form of checking. Users won't be able to run + * incompatible utility such as the synchroniser (umssync). + * umsdos_progs/umsdosio.c enforce this checking. * - * umsdos_progs/umsdosio.c provide an interface in C++ to all - * these ioctl. umsdos_progs/udosctl is a small utility showing - * all this. + * Return always 0. + */ + put_user (UMSDOS_VERSION, &idata->version); + put_user (UMSDOS_RELEASE, &idata->release); + ret = 0; + goto out; + } + if (cmd == UMSDOS_READDIR_DOS) { + /* #Specification: ioctl / UMSDOS_READDIR_DOS + * One entry is read from the DOS directory at the current + * file position. The entry is put as is in the dos_dirent + * field of struct umsdos_ioctl. * - * These ioctl generally allow one to work on the EMD or the - * DOS directory independently. These are essential to implement - * the synchronise. + * Return > 0 if success. */ - Printk (("ioctl %d ", cmd)); - if (cmd == UMSDOS_GETVERSION) { - /* #Specification: ioctl / UMSDOS_GETVERSION - * The field version and release of the structure - * umsdos_ioctl are filled with the version and release - * number of the fs code in the kernel. This will allow - * some form of checking. Users won't be able to run - * incompatible utility such as the synchroniser (umssync). - * umsdos_progs/umsdosio.c enforce this checking. - * - * Return always 0. - */ - put_user (UMSDOS_VERSION, &idata->version); - put_user (UMSDOS_RELEASE, &idata->release); - ret = 0; - } else if (cmd == UMSDOS_READDIR_DOS) { - /* #Specification: ioctl / UMSDOS_READDIR_DOS - * One entry is read from the DOS directory at the current - * file position. The entry is put as is in the dos_dirent - * field of struct umsdos_ioctl. - * - * Return > 0 if success. - */ - struct UMSDOS_DIR_ONCE bufk; + struct UMSDOS_DIR_ONCE bufk; - bufk.count = 0; - bufk.ent = &idata->dos_dirent; + bufk.count = 0; + bufk.ent = &idata->dos_dirent; - fat_readdir (filp, &bufk, umsdos_ioctl_fill); + fat_readdir (filp, &bufk, umsdos_ioctl_fill); - ret = bufk.count == 1 ? 1 : 0; - } else if (cmd == UMSDOS_READDIR_EMD) { - /* #Specification: ioctl / UMSDOS_READDIR_EMD - * One entry is read from the EMD at the current - * file position. The entry is put as is in the umsdos_dirent - * field of struct umsdos_ioctl. The corresponding mangled - * DOS entry name is put in the dos_dirent field. - * - * All entries are read including hidden links. Blank - * entries are skipped. - * - * Return > 0 if success. - */ - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0); + ret = bufk.count == 1 ? 1 : 0; + goto out; + } + if (cmd == UMSDOS_READDIR_EMD) { + /* #Specification: ioctl / UMSDOS_READDIR_EMD + * One entry is read from the EMD at the current + * file position. The entry is put as is in the umsdos_dirent + * field of struct umsdos_ioctl. The corresponding mangled + * DOS entry name is put in the dos_dirent field. + * + * All entries are read including hidden links. Blank + * entries are skipped. + * + * Return > 0 if success. + */ + struct dentry *demd; - if (emd_dir != NULL) { - while (1) { - if (filp->f_pos >= emd_dir->i_size) { - ret = 0; - break; - } else { - struct umsdos_dirent entry; - off_t f_pos = filp->f_pos; + /* The absence of the EMD is simply seen as an EOF */ + demd = umsdos_get_emd_dentry(dentry); + ret = PTR_ERR(demd); + if (IS_ERR(demd)) + goto out; + fill_new_filp(&new_filp, demd); + new_filp.f_pos = filp->f_pos; - ret = umsdos_emd_dir_readentry (emd_dir, filp, &entry); - if (ret < 0) { - break; - } else if (entry.name_len > 0) { - struct umsdos_info info; + while (1) { + off_t f_pos = new_filp.f_pos; + struct umsdos_dirent entry; + struct umsdos_info info; - ret = entry.name_len; - umsdos_parse (entry.name, entry.name_len, &info); - info.f_pos = f_pos; - umsdos_manglename (&info); - copy_to_user (&idata->umsdos_dirent, &entry - ,sizeof (entry)); - copy_to_user (&idata->dos_dirent.d_name - ,info.fake.fname, info.fake.len + 1); - break; - } - } - } - iput (emd_dir); /* FIXME? */ - } else { - /* The absence of the EMD is simply seen as an EOF */ - ret = 0; - } - } else if (cmd == UMSDOS_INIT_EMD) { - /* #Specification: ioctl / UMSDOS_INIT_EMD - * The UMSDOS_INIT_EMD command make sure the EMD - * exist for a directory. If it does not, it is - * created. Also, it makes sure the directory functions - * table (struct inode_operations) is set to the UMSDOS - * semantic. This mean that umssync may be applied to - * an "opened" msdos directory, and it will change behavior - * on the fly. - * - * Return 0 if success. - */ - extern struct inode_operations umsdos_rdir_inode_operations; - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1); + ret = 0; + if (new_filp.f_pos >= demd->d_inode->i_size) + break; - ret = emd_dir != NULL; - iput (emd_dir); /* FIXME?? */ + ret = umsdos_emd_dir_readentry (&new_filp, &entry); + if (ret < 0) + break; + if (entry.name_len <= 0) + continue; - dir->i_op = ret - ? &umsdos_dir_inode_operations - : &umsdos_rdir_inode_operations; - } else { - struct umsdos_ioctl data; + umsdos_parse (entry.name, entry.name_len, &info); + info.f_pos = f_pos; + umsdos_manglename (&info); + ret = -EFAULT; + if (copy_to_user (&idata->umsdos_dirent, &entry, + sizeof (entry))) + break; + if (copy_to_user (&idata->dos_dirent.d_name, + info.fake.fname, + info.fake.len + 1)) + break; + ret = entry.name_len; + break; + } + /* update the original f_pos */ + filp->f_pos = new_filp.f_pos; + d_drop(demd); + dput(demd); + goto out; + } + if (cmd == UMSDOS_INIT_EMD) { + /* #Specification: ioctl / UMSDOS_INIT_EMD + * The UMSDOS_INIT_EMD command makes sure the EMD + * exists for a directory. If it does not, it is + * created. Also, it makes sure the directory function + * table (struct inode_operations) is set to the UMSDOS + * semantic. This mean that umssync may be applied to + * an "opened" msdos directory, and it will change behavior + * on the fly. + * + * Return 0 if success. + */ + extern struct inode_operations umsdos_rdir_inode_operations; + + ret = umsdos_make_emd(dentry); + dir->i_op = (ret == 0) + ? &umsdos_dir_inode_operations + : &umsdos_rdir_inode_operations; + goto out; + } - copy_from_user (&data, idata, sizeof (data)); - if (cmd == UMSDOS_CREAT_EMD) { - /* #Specification: ioctl / UMSDOS_CREAT_EMD - * The umsdos_dirent field of the struct umsdos_ioctl is used - * as is to create a new entry in the EMD of the directory. - * The DOS directory is not modified. - * No validation is done (yet). - * - * Return 0 if success. - */ - struct umsdos_info info; + ret = -EFAULT; + if (copy_from_user (&data, idata, sizeof (data))) + goto out; - /* This makes sure info.entry and info in general is correctly */ - /* initialised */ - memcpy (&info.entry, &data.umsdos_dirent - ,sizeof (data.umsdos_dirent)); - umsdos_parse (data.umsdos_dirent.name - ,data.umsdos_dirent.name_len, &info); - ret = umsdos_newentry (dir, &info); - } else if (cmd == UMSDOS_RENAME_DOS) { - struct dentry *old_dentry, *new_dentry; /* FIXME */ + if (cmd == UMSDOS_CREAT_EMD) { + /* #Specification: ioctl / UMSDOS_CREAT_EMD + * The umsdos_dirent field of the struct umsdos_ioctl is used + * as is to create a new entry in the EMD of the directory. + * The DOS directory is not modified. + * No validation is done (yet). + * + * Return 0 if success. + */ + struct umsdos_info info; - /* #Specification: ioctl / UMSDOS_RENAME_DOS - * A file or directory is rename in a DOS directory - * (not moved across directory). The source name - * is in the dos_dirent.name field and the destination - * is in umsdos_dirent.name field. - * - * This ioctl allows umssync to rename a mangle file - * name before syncing it back in the EMD. - */ - inc_count (dir); - inc_count (dir); - /* - * ret = msdos_rename (dir - * ,data.dos_dirent.d_name,data.dos_dirent.d_reclen - * ,dir - * ,data.umsdos_dirent.name,data.umsdos_dirent.name_len); - */ - old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, geti_dentry (dir)); /* FIXME: prolly should fill inode part */ - new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL, geti_dentry (dir)); - ret = msdos_rename (dir, old_dentry, dir, new_dentry); - } else if (cmd == UMSDOS_UNLINK_EMD) { - /* #Specification: ioctl / UMSDOS_UNLINK_EMD - * The umsdos_dirent field of the struct umsdos_ioctl is used - * as is to remove an entry from the EMD of the directory. - * No validation is done (yet). The mode field is used - * to validate S_ISDIR or S_ISREG. - * - * Return 0 if success. - */ - struct umsdos_info info; + /* This makes sure info.entry and info in general + * is correctly initialised + */ + memcpy (&info.entry, &data.umsdos_dirent, + sizeof (data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name + ,data.umsdos_dirent.name_len, &info); + ret = umsdos_newentry (dentry, &info); + goto out; + } + else if (cmd == UMSDOS_RENAME_DOS) { + struct dentry *old_dentry, *new_dentry; /* FIXME */ - /* This makes sure info.entry and info in general is correctly */ - /* initialised */ - memcpy (&info.entry, &data.umsdos_dirent - ,sizeof (data.umsdos_dirent)); - umsdos_parse (data.umsdos_dirent.name - ,data.umsdos_dirent.name_len, &info); - ret = umsdos_delentry (dir, &info - ,S_ISDIR (data.umsdos_dirent.mode)); - } else if (cmd == UMSDOS_UNLINK_DOS) { - struct dentry *dentry, *dp; + /* #Specification: ioctl / UMSDOS_RENAME_DOS + * A file or directory is renamed in a DOS directory + * (not moved across directory). The source name + * is in the dos_dirent.name field and the destination + * is in umsdos_dirent.name field. + * + * This ioctl allows umssync to rename a mangle file + * name before syncing it back in the EMD. + */ + old_dentry = umsdos_lookup_dentry (dentry, + data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto out; + new_dentry = umsdos_lookup_dentry (dentry, + data.umsdos_dirent.name, + data.umsdos_dirent.name_len); + ret = PTR_ERR(new_dentry); + if (!IS_ERR(new_dentry)) { +printk("umsdos_ioctl: renaming %s/%s to %s/%s\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, +new_dentry->d_parent->d_name.name, new_dentry->d_name.name); + ret = msdos_rename (dir, old_dentry, dir, new_dentry); + dput(new_dentry); + } + d_drop(old_dentry); + dput(old_dentry); + goto out; + } + else if (cmd == UMSDOS_UNLINK_EMD) { + /* #Specification: ioctl / UMSDOS_UNLINK_EMD + * The umsdos_dirent field of the struct umsdos_ioctl is used + * as is to remove an entry from the EMD of the directory. + * No validation is done (yet). The mode field is used + * to validate S_ISDIR or S_ISREG. + * + * Return 0 if success. + */ + struct umsdos_info info; - /* #Specification: ioctl / UMSDOS_UNLINK_DOS - * The dos_dirent field of the struct umsdos_ioctl is used to - * execute a msdos_unlink operation. The d_name and d_reclen - * fields are used. - * - * Return 0 if success. - */ - inc_count (dir); - dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL); - dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp); - ret = msdos_unlink (dir, dentry); + /* This makes sure info.entry and info in general + * is correctly initialised + */ + memcpy (&info.entry, &data.umsdos_dirent, + sizeof (data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name, + data.umsdos_dirent.name_len, &info); + ret = umsdos_delentry (dentry, &info, + S_ISDIR (data.umsdos_dirent.mode)); + goto out; + } + else if (cmd == UMSDOS_UNLINK_DOS) { + struct dentry *temp; - } else if (cmd == UMSDOS_RMDIR_DOS) { - struct dentry *dentry, *dp; + /* #Specification: ioctl / UMSDOS_UNLINK_DOS + * The dos_dirent field of the struct umsdos_ioctl is used to + * execute a msdos_unlink operation. The d_name and d_reclen + * fields are used. + * + * Return 0 if success. + */ + temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out; + ret = -ENOENT; + if (temp->d_inode) + ret = msdos_unlink (dir, temp); + d_drop(temp); + dput (temp); + goto out; + } + else if (cmd == UMSDOS_RMDIR_DOS) { + struct dentry *temp; - /* #Specification: ioctl / UMSDOS_RMDIR_DOS - * The dos_dirent field of the struct umsdos_ioctl is used to - * execute a msdos_unlink operation. The d_name and d_reclen - * fields are used. - * - * Return 0 if success. - */ - inc_count (dir); - dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL); - dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp); - ret = msdos_rmdir (dir, dentry); + /* #Specification: ioctl / UMSDOS_RMDIR_DOS + * The dos_dirent field of the struct umsdos_ioctl is used to + * execute a msdos_rmdir operation. The d_name and d_reclen + * fields are used. + * + * Return 0 if success. + */ + temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out; + ret = -ENOENT; + if (temp->d_inode) + ret = msdos_rmdir (dir, temp); + d_drop(temp); + dput (temp); + goto out; - } else if (cmd == UMSDOS_STAT_DOS) { - /* #Specification: ioctl / UMSDOS_STAT_DOS - * The dos_dirent field of the struct umsdos_ioctl is - * used to execute a stat operation in the DOS directory. - * The d_name and d_reclen fields are used. - * - * The following field of umsdos_ioctl.stat are filled. - * - * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, - * Return 0 if success. - */ - struct inode *inode; + } else if (cmd == UMSDOS_STAT_DOS) { + /* #Specification: ioctl / UMSDOS_STAT_DOS + * The dos_dirent field of the struct umsdos_ioctl is + * used to execute a stat operation in the DOS directory. + * The d_name and d_reclen fields are used. + * + * The following field of umsdos_ioctl.stat are filled. + * + * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, + * Return 0 if success. + */ + struct dentry *dret; + struct inode *inode; - ret = compat_umsdos_real_lookup (dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen, &inode); - if (ret == 0) { - data.stat.st_ino = inode->i_ino; - data.stat.st_mode = inode->i_mode; - data.stat.st_size = inode->i_size; - data.stat.st_atime = inode->i_atime; - data.stat.st_ctime = inode->i_ctime; - data.stat.st_mtime = inode->i_mtime; - copy_to_user (&idata->stat, &data.stat, sizeof (data.stat)); - /* iput (inode); FIXME */ - } - } else if (cmd == UMSDOS_DOS_SETUP) { - /* #Specification: ioctl / UMSDOS_DOS_SETUP - * The UMSDOS_DOS_SETUP ioctl allow changing the - * default permission of the MsDOS file system driver - * on the fly. The MsDOS driver apply global permission - * to every file and directory. Normally these permissions - * are controlled by a mount option. This is not - * available for root partition, so a special utility - * (umssetup) is provided to do this, normally in - * /etc/rc.local. - * - * Be aware that this apply ONLY to MsDOS directory - * (those without EMD --linux-.---). Umsdos directory - * have independent (standard) permission for each - * and every file. - * - * The field umsdos_dirent provide the information needed. - * umsdos_dirent.uid and gid sets the owner and group. - * umsdos_dirent.mode set the permissions flags. - */ - dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; - dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; - dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; + dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + goto out; + ret = -ENOENT; + inode = dret->d_inode; + if (inode) { + data.stat.st_ino = inode->i_ino; + data.stat.st_mode = inode->i_mode; + data.stat.st_size = inode->i_size; + data.stat.st_atime = inode->i_atime; + data.stat.st_ctime = inode->i_ctime; + data.stat.st_mtime = inode->i_mtime; + ret = -EFAULT; + if (!copy_to_user (&idata->stat, &data.stat, + sizeof (data.stat))) ret = 0; - } } + d_drop(dret); + dput(dret); + goto out; + } + else if (cmd == UMSDOS_DOS_SETUP) { + /* #Specification: ioctl / UMSDOS_DOS_SETUP + * The UMSDOS_DOS_SETUP ioctl allow changing the + * default permission of the MS-DOS filesystem driver + * on the fly. The MS-DOS driver applies global permissions + * to every file and directory. Normally these permissions + * are controlled by a mount option. This is not + * available for root partition, so a special utility + * (umssetup) is provided to do this, normally in + * /etc/rc.local. + * + * Be aware that this applies ONLY to MS-DOS directories + * (those without EMD --linux-.---). Umsdos directory + * have independent (standard) permission for each + * and every file. + * + * The field umsdos_dirent provide the information needed. + * umsdos_dirent.uid and gid sets the owner and group. + * umsdos_dirent.mode set the permissions flags. + */ + dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; + dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; + dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; + ret = 0; } - Printk (("ioctl return %d\n", ret)); +out: + Printk (("ioctl %d, returning %d\n", cmd, ret)); return ret; } diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c index ecd62f4c3..c7f12722f 100644 --- a/fs/umsdos/mangle.c +++ b/fs/umsdos/mangle.c @@ -4,7 +4,7 @@ * Written 1993 by Jacques Gelinas * * Control the mangling of file name to fit msdos name space. - * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID) + * Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David) */ #include <linux/errno.h> @@ -12,6 +12,11 @@ #include <linux/kernel.h> #include <linux/umsdos_fs.h> +/* (This file is used outside of the kernel) */ +#ifndef __KERNEL__ +#define KERN_WARNING +#endif + /* * Complete the mangling of the MSDOS fake name * based on the position of the entry in the EMD file. @@ -47,7 +52,7 @@ void umsdos_manglename (struct umsdos_info *info) * different extensions which should not clash with any useful * extension already popular or meaningful. Since most directory * have much less than 32 * 32 files in it, the first character - * of the extension of any mangle name will be {. + * of the extension of any mangled name will be {. * * Here are the reason to do this (this kind of mangling). * @@ -72,10 +77,11 @@ void umsdos_manglename (struct umsdos_info *info) } u; char *pt = info->fake.fname + info->fake.len; - /* lookup for encoding the last character of the extension */ - /* It contain valid character after the ugly one to make sure */ - /* even if someone overflow the 32 * 32 * 9 limit, it still do */ - /* something */ + /* lookup for encoding the last character of the extension + * It contains valid character after the ugly one to make sure + * even if someone overflows the 32 * 32 * 9 limit, it still + * does something + */ #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@' static char lookup3[] = { @@ -89,7 +95,7 @@ void umsdos_manglename (struct umsdos_info *info) #define lookup12 (lookup3+9) u.entry_num = info->f_pos / UMSDOS_REC_SIZE; if (u.entry_num > (9 * 32 * 32)) { - printk ("UMSDOS: More than 9216 file in a directory.\n" + printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.\n" "This may break the mangling strategy.\n" "Not a killer problem. See doc.\n"); } @@ -136,7 +142,7 @@ int umsdos_evalrecsize_old (int len) /* * Fill the struct info with the full and msdos name of a file - * Return 0 if all is ok, a negative error code otherwise. + * Return 0 if all is OK, a negative error code otherwise. */ int umsdos_parse ( const char *fname, @@ -153,10 +159,10 @@ int umsdos_parse ( if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME; { - const char *firstpt = NULL; /* First place we saw a . in fname */ + const char *firstpt = NULL; /* First place we saw a "." in fname */ /* #Specification: file name / non MSDOS conforming / base length 0 - * file name beginning with a period '.' are invalid for MsDOS. + * file names beginning with a period '.' are invalid for MS-DOS. * It needs absolutely a base name. So the file name is mangled */ int ivldchar = fname[0] == '.'; /* At least one invalid character */ @@ -164,8 +170,8 @@ int umsdos_parse ( int base_len; /* - * cardinal_per_size tells if there exist at least one - * DOS pseudo devices on length n. See the test below. + * cardinal_per_size tells if there exists at least one + * DOS pseudo device on length n. See the test below. */ static const char cardinal_per_size[9] = { @@ -175,15 +181,15 @@ int umsdos_parse ( /* * lkp translate all character to acceptable character (for DOS). * When lkp[n] == n, it means also it is an acceptable one. - * So it serve both as a flag and as a translator. + * So it serves both as a flag and as a translator. */ static char lkp[256]; static char is_init = 0; if (!is_init) { /* - * Initialisation of the array is easier and less error prone - * like this. + * Initialisation of the array is easier and less error + * prone like this. */ int i; static const char *spc = "\"*+,/:;<=>?[\\]|~"; @@ -205,9 +211,9 @@ int umsdos_parse ( lkp[(unsigned char) (*spc++)] = '#'; } /* GLU - * file name which are longer than 8+'.'+3 are invalid for MsDOS. - * So the file name is to be mangled no more test needed. - * This Speed Up for long and very long name. + * File names longer than 8+'.'+3 are invalid for MS-DOS, + * so the file name is to be mangled--no further test is needed. + * This speeds up handling of long names. * The position of the last point is no more necessary anyway. */ if (len <= (8 + 1 + 3)) { @@ -235,8 +241,8 @@ int umsdos_parse ( } else if (extlen == 1) { /* #Specification: file name / non MSDOS conforming / last char == . * If the last character of a file name is - * a period, mangling is applied. MsDOS do - * not support those file name. + * a period, mangling is applied. MS-DOS does + * not support those file names. */ ivldchar = 1; break; @@ -244,7 +250,7 @@ int umsdos_parse ( /* #Specification: file name / non MSDOS conforming / mangling clash * To avoid clash with the umsdos mangling, any file * with a special character as the first character - * of the extension will be mangled. This solve the + * of the extension will be mangled. This solves the * following problem: * * # @@ -285,7 +291,7 @@ int umsdos_parse ( * name. So UMSDOS does not restrict its use. */ /* #Specification: file name / non MSDOS conforming / mangling - * Non MSDOS conforming file name must use some alias to fit + * Non MSDOS conforming file names must use some alias to fit * in the MSDOS name space. * * The strategy is simple. The name is simply truncated to @@ -294,15 +300,15 @@ int umsdos_parse ( * to the entry number in the EMD file. The EMD file * only need to carry the real name. * - * Upper case is also convert to lower case. + * Upper case is also converted to lower case. * Control character are converted to #. - * Space are converted to #. - * The following character are also converted to #. + * Spaces are converted to #. + * The following characters are also converted to #. * # * " * + , / : ; < = > ? [ \ ] | ~ * # * - * Sometime, the problem is not in MsDOS itself but in + * Sometimes the problem is not in MS-DOS itself but in * command.com. */ int i; @@ -316,7 +322,7 @@ int umsdos_parse ( memcpy (info->fake.fname, fname, msdos_len); for (i = 0; i < msdos_len; i++, pt++) *pt = lkp[(unsigned char) (*pt)]; - *pt = '\0'; /* GLU C'est sur on a un 0 a la fin */ + *pt = '\0'; /* GLU We force null termination. */ info->msdos_reject = 1; /* * The numeric extension is added only when we know @@ -332,15 +338,15 @@ int umsdos_parse ( } if (cardinal_per_size[base_len]) { /* #Specification: file name / MSDOS devices / mangling - * To avoid unreachable file from MsDOS, any MsDOS conforming - * file with a basename equal to one of the MsDOS pseudo + * To avoid unreachable file from MS-DOS, any MS-DOS conforming + * file with a basename equal to one of the MS-DOS pseudo * devices will be mangled. * * If a file such as "prn" was created, it would be unreachable - * under MsDOS because prn is assumed to be the printer, even + * under MS-DOS because "prn" is assumed to be the printer, even * if the file does have an extension. * - * Since the extension is unimportant to MsDOS, we must patch + * Since the extension is unimportant to MS-DOS, we must patch * the basename also. We simply insert a minus '-'. To avoid * conflict with valid file with a minus in front (such as * "-prn"), we add an mangled extension like any other @@ -359,10 +365,10 @@ int umsdos_parse ( * * "emmxxxx0","xmsxxxx0","setverxx" * - * (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK> - * for pointing these to me). + * (Thanks to Chris Hall <cah17@phoenix.cambridge.ac.uk> + * for pointing these out to me). * - * Is there one missing ? + * Is there one missing? */ /* This table must be ordered by length */ static const char *tbdev[] = @@ -386,13 +392,13 @@ int umsdos_parse ( for (i = start_ind_dev[base_len - 1]; i < start_ind_dev[base_len]; i++) { if (memcmp (info->fake.fname, tbdev[i], base_len) == 0) { memcpy (basen, info->fake.fname, base_len); - basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */ + basen[base_len] = '\0'; /* GLU We force null termination. */ /* - * GLU On ne fait cela que si necessaire, on essaye d'etre le - * GLU simple dans le cas general (le plus frequent). + * GLU We do that only if necessary; we try to do the + * GLU simple thing in the usual circumstance. */ info->fake.fname[0] = '-'; - strcpy (info->fake.fname + 1, basen); /* GLU C'est sur on a un 0 a la fin */ + strcpy (info->fake.fname + 1, basen); /* GLU We already guaranteed a null would be at the end. */ msdos_len = (base_len == 8) ? 8 : base_len + 1; info->msdos_reject = 1; break; @@ -400,15 +406,16 @@ int umsdos_parse ( } } info->fake.fname[msdos_len] = '\0'; /* Help doing printk */ - /* GLU Ce zero devrais deja y etre ! (invariant ?) */ + /* GLU This zero should (always?) be there already. */ info->fake.len = msdos_len; - /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ? */ + /* Why not use info->fake.len everywhere? Is it longer? + */ memcpy (info->entry.name, fname, len); info->entry.name_len = len; ret = 0; } /* - * Evaluate how many record are needed to store this entry. + * Evaluate how many records are needed to store this entry. */ info->recsize = umsdos_evalrecsize (len); return ret; @@ -435,15 +442,15 @@ struct MANG_TEST tb[] = "Hello.c", 1, "hello.c", #elseif /* - * Je trouve les trois exemples ci-dessous tres "malheureux". - * Je propose de mettre en minuscule dans un passe preliminaire, - * et de tester apres si il y a d'autres caracters "mechants". - * Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement - * modifiable que ca. Mais c'est pour le principe. - * Evidemment cela augmente les chances de "Collision", - * par exemple: entre "HELLO" et "Hello", mais ces problemes - * peuvent etre traiter ailleur avec les autres collisions. + * I find the three examples below very unfortunate. I propose to + * convert them to lower case in a quick preliminary pass, then test + * whether there are other troublesome characters. I have not made + * this change, because it is not easy, but I wanted to mention the + * principle. Obviously something like that would increase the chance + * of collisions, for example between "HELLO" and "Hello", but these + * can be treated elsewhere along with the other collisions. */ + "HELLO", 1, "hello", "Hello.1", 1, "hello_1", "Hello.c", 1, "hello_c", @@ -465,11 +472,12 @@ struct MANG_TEST tb[] = "prn.abc", 1, "-prn", "PRN", 1, "-prn", /* - * GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version - * GLU du mangle par rapport au mangle originale. - * GLU CAUSE: La maniere de calculer la variable baselen. - * GLU Pour toi c'est toujours 3 - * GLU Pour moi c'est respectivement 7, 8 et 8 + * GLU WARNING: the results of these are different with my version + * GLU of mangling compared to the original one. + * GLU CAUSE: the manner of calculating the baselen variable. + * GLU For you they are always 3. + * GLU For me they are respectively 7, 8, and 8. + */ "PRN.abc", 1, "prn_abc", "Prn.abcd", 1, "prn_abcd", diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 387ed4fe3..b365fbf04 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -18,9 +18,6 @@ #include <linux/umsdos_fs.h> #include <linux/malloc.h> -#define PRINTK(x) -#define Printk(x) printk x - #if 1 /* * Wait for creation exclusivity. @@ -54,36 +51,36 @@ static void umsdos_waitlookup (struct inode *dir) /* * Lock all other process out of this directory. */ +/* #Specification: file creation / not atomic + * File creation is a two step process. First we create (allocate) + * an entry in the EMD file and then (using the entry offset) we + * build a unique name for MSDOS. We create this name in the msdos + * space. + * + * We have to use semaphore (sleep_on/wake_up) to prevent lookup + * into a directory when we create a file or directory and to + * prevent creation while a lookup is going on. Since many lookup + * may happen at the same time, the semaphore is a counter. + * + * Only one creation is allowed at the same time. This protection + * may not be necessary. The problem arise mainly when a lookup + * or a readdir is done while a file is partially created. The + * lookup process see that as a "normal" problem and silently + * erase the file from the EMD file. Normal because a file + * may be erased during a MSDOS session, but not removed from + * the EMD file. + * + * The locking is done on a directory per directory basis. Each + * directory inode has its wait_queue. + * + * For some operation like hard link, things even get worse. Many + * creation must occur at once (atomic). To simplify the design + * a process is allowed to recursively lock the directory for + * creation. The pid of the locking process is kept along with + * a counter so a second level of locking is granted or not. + */ void umsdos_lockcreate (struct inode *dir) { - /* #Specification: file creation / not atomic - * File creation is a two step process. First we create (allocate) - * an entry in the EMD file and then (using the entry offset) we - * build a unique name for MSDOS. We create this name in the msdos - * space. - * - * We have to use semaphore (sleep_on/wake_up) to prevent lookup - * into a directory when we create a file or directory and to - * prevent creation while a lookup is going on. Since many lookup - * may happen at the same time, the semaphore is a counter. - * - * Only one creation is allowed at the same time. This protection - * may not be necessary. The problem arise mainly when a lookup - * or a readdir is done while a file is partially created. The - * lookup process see that as a "normal" problem and silently - * erase the file from the EMD file. Normal because a file - * may be erased during a MSDOS session, but not removed from - * the EMD file. - * - * The locking is done on a directory per directory basis. Each - * directory inode has its wait_queue. - * - * For some operation like hard link, things even get worse. Many - * creation must occur at once (atomic). To simplify the design - * a process is allowed to recursively lock the directory for - * creation. The pid of the locking process is kept along with - * a counter so a second level of locking is granted or not. - */ /* * Wait for any creation process to finish except * if we (the process) own the lock @@ -174,11 +171,21 @@ void umsdos_endlookup (struct inode *dir) #endif -static int umsdos_nevercreat ( - struct inode *dir, - struct dentry *dentry, - int errcod) -{ /* Length of the name */ +/* + * Check whether we can delete from the directory. + */ +static int is_sticky(struct inode *dir, int uid) +{ + return !((dir->i_mode & S_ISVTX) == 0 || + capable (CAP_FOWNER) || + current->fsuid == uid || + current->fsuid == dir->i_uid); +} + + +static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, + int errcod) +{ const char *name = dentry->d_name.name; int len = dentry->d_name.len; int ret = 0; @@ -213,94 +220,127 @@ static int umsdos_nevercreat ( /* * Add a new file (ordinary or special) into the alternate directory. * The file is added to the real MSDOS directory. If successful, it - * is then added to the EDM file. + * is then added to the EMD file. * * Return the status of the operation. 0 mean success. + * + * #Specification: create / file exist in DOS + * Here is a situation. Trying to create a file with + * UMSDOS. The file is unknown to UMSDOS but already + * exists in the DOS directory. + * + * Here is what we are NOT doing: + * + * We could silently assume that everything is fine + * and allows the creation to succeed. + * + * It is possible not all files in the partition + * are meant to be visible from linux. By trying to create + * those file in some directory, one user may get access + * to those file without proper permissions. Looks like + * a security hole to me. Off course sharing a file system + * with DOS is some kind of security hole :-) + * + * So ? + * + * We return EEXIST in this case. + * The same is true for directory creation. */ -static int umsdos_create_any ( - struct inode *dir, - struct dentry *dentry, /* name/length etc */ - int mode, /* Permission bit + file type ??? */ - int rdev, /* major, minor or 0 for ordinary file and symlinks */ - char flags -) -{ /* Will hold the inode of the newly created file */ - - int ret; +static int umsdos_create_any (struct inode *dir, struct dentry *dentry, + int mode, int rdev, char flags) +{ struct dentry *fake; + struct inode *inode; + int ret; + struct umsdos_info info; + +if (dentry->d_inode) +printk("umsdos_create_any: %s/%s not negative!\n", +dentry->d_parent->d_name.name, dentry->d_name.name); - Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino)); - check_dentry (dentry); +Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", +(int) dentry->d_name.len, dentry->d_name.name, dir->i_ino)); + + check_dentry_path (dentry, "umsdos_create_any"); ret = umsdos_nevercreat (dir, dentry, -EEXIST); - Printk (("%d/\n", ret)); - if (ret == 0) { - struct umsdos_info info; + if (ret) { +Printk (("%d/\n", ret)); + goto out; + } - ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + if (ret) + goto out; + + info.entry.mode = mode; + info.entry.rdev = rdev; + info.entry.flags = flags; + info.entry.uid = current->fsuid; + info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME; + info.entry.nlink = 1; + umsdos_lockcreate (dir); + ret = umsdos_newentry (dentry->d_parent, &info); + if (ret) + goto out_unlock; + + /* create short name dentry */ + fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, + info.fake.len); + ret = PTR_ERR(fake); + if (IS_ERR(fake)) + goto out_unlock; + + /* should not exist yet ... */ + ret = -EEXIST; + if (fake->d_inode) + goto out_remove; + + ret = msdos_create (dir, fake, S_IFREG | 0777); + if (ret) + goto out_remove; + + inode = fake->d_inode; + umsdos_lookup_patch_new(fake, &info.entry, info.f_pos); + +Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count)); +Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", +dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos)); + + check_dentry_path (dentry, "umsdos_create_any: BEG dentry"); + check_dentry_path (fake, "umsdos_create_any: BEG fake"); - if (ret == 0) { - info.entry.mode = mode; - info.entry.rdev = rdev; - info.entry.flags = flags; - info.entry.uid = current->fsuid; - info.entry.gid = (dir->i_mode & S_ISGID) - ? dir->i_gid : current->fsgid; - info.entry.ctime = info.entry.atime = info.entry.mtime - = CURRENT_TIME; - info.entry.nlink = 1; - umsdos_lockcreate (dir); - ret = umsdos_newentry (dir, &info); - if (ret == 0) { - inc_count (dir); - fake = creat_dentry (info.fake.fname, info.fake.len, NULL, dentry->d_parent); /* create short name dentry */ - ret = msdos_create (dir, fake, S_IFREG | 0777); - if (ret == 0) { - struct inode *inode = fake->d_inode; - - umsdos_lookup_patch (dir, inode, &info.entry, info.f_pos); - Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count)); - Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino, - info.fake.len, info.fake.fname, current->pid, info.f_pos)); - - d_instantiate (dentry, inode); /* long name also gets inode info */ - dput (fake); /* FIXME: is this ok ? we try to kill short name dentry that we don't need */ - } else { - /* #Specification: create / file exist in DOS - * Here is a situation. Trying to create a file with - * UMSDOS. The file is unknown to UMSDOS but already - * exist in the DOS directory. - * - * Here is what we are NOT doing: - * - * We could silently assume that everything is fine - * and allows the creation to succeed. - * - * It is possible not all files in the partition - * are mean to be visible from linux. By trying to create - * those file in some directory, one user may get access - * to those file without proper permissions. Looks like - * a security hole to me. Off course sharing a file system - * with DOS is some kind of security hole :-) - * - * So ? - * - * We return EEXIST in this case. - * The same is true for directory creation. - */ - if (ret == -EEXIST) { - printk ("UMSDOS: out of sync, Creation error [%ld], " - "deleting %.*s %d %d pos %ld\n", dir->i_ino - ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos); - } - umsdos_delentry (dir, &info, 0); - } - Printk (("umsdos_create %.*s ret = %d pos %ld\n", - info.fake.len, info.fake.fname, ret, info.f_pos)); - } - umsdos_unlockcreate (dir); - } + /* + * Note! The long and short name might be the same, + * so check first before doing the instantiate ... + */ + if (dentry != fake) { + /* long name also gets inode info */ + inode->i_count++; + d_instantiate (dentry, inode); } - /* d_add(dentry,dir); /mn/ FIXME: msdos_create already did this for short name ! */ + + check_dentry_path (dentry, "umsdos_create_any: END dentry"); + check_dentry_path (fake, "umsdos_create_any: END fake"); + goto out_dput; + +out_remove: +if (ret == -EEXIST) +printk("UMSDOS: out of sync, error [%ld], deleting %.*s %d %d pos %ld\n", +dir->i_ino ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos); + umsdos_delentry (dentry->d_parent, &info, 0); + +out_dput: +Printk (("umsdos_create %.*s ret = %d pos %ld\n", +info.fake.len, info.fake.fname, ret, info.f_pos)); + /* N.B. any value in keeping short name dentries? */ + if (dentry != fake) + d_drop(fake); + dput(fake); + +out_unlock: + umsdos_unlockcreate (dir); +out: return ret; } @@ -308,11 +348,9 @@ static int umsdos_create_any ( * Initialise the new_entry from the old for a rename operation. * (Only useful for umsdos_rename_f() below). */ -static void umsdos_ren_init ( - struct umsdos_info *new_info, - struct umsdos_info *old_info, - int flags) -{ /* 0 == copy flags from old_name */ +static void umsdos_ren_init (struct umsdos_info *new_info, + struct umsdos_info *old_info, int flags) +{ /* != 0, this is the value of flags */ new_info->entry.mode = old_info->entry.mode; new_info->entry.rdev = old_info->entry.rdev; @@ -340,201 +378,193 @@ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\ * Rename a file (move) in the file system. */ -static int umsdos_rename_f ( - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, - int flags) -{ /* 0 == copy flags from old_name */ - /* != 0, this is the value of flags */ +static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + int flags) +{ + int old_ret, new_ret; + struct dentry *old, *new, *dret; + struct inode *oldid = NULL; int ret = -EPERM; struct umsdos_info old_info; - int old_ret = umsdos_parse (old_dentry->d_name.name, - old_dentry->d_name.len, &old_info); struct umsdos_info new_info; - int new_ret = umsdos_parse (new_dentry->d_name.name, - new_dentry->d_name.len, &new_info); - check_dentry (old_dentry); - check_dentry (new_dentry); + old_ret = umsdos_parse (old_dentry->d_name.name, + old_dentry->d_name.len, &old_info); + if (old_ret) + goto out; + new_ret = umsdos_parse (new_dentry->d_name.name, + new_dentry->d_name.len, &new_info); + if (new_ret) + goto out; + + check_dentry_path (old_dentry, "umsdos_rename_f OLD"); + check_dentry_path (new_dentry, "umsdos_rename_f OLD"); chkstk (); - Printk (("umsdos_rename %d %d ", old_ret, new_ret)); - if (old_ret == 0 && new_ret == 0) { - umsdos_lockcreate2 (old_dir, new_dir); - chkstk (); - Printk (("old findentry ")); - ret = umsdos_findentry (old_dir, &old_info, 0); +Printk (("umsdos_rename %d %d ", old_ret, new_ret)); + umsdos_lockcreate2 (old_dir, new_dir); + chkstk (); + + ret = umsdos_findentry(old_dentry->d_parent, &old_info, 0); + chkstk (); + if (ret) { +Printk (("ret %d ", ret)); + goto out_unlock; + } + + /* check sticky bit on old_dir */ + ret = -EPERM; + if (is_sticky(old_dir, old_info.entry.uid)) { + Printk (("sticky set on old ")); + goto out_unlock; + } + + /* Does new_name already exist? */ + new_ret = umsdos_findentry(new_dentry->d_parent, &new_info, 0); + /* if destination file exists, are we allowed to replace it ? */ + if (new_ret == 0 && is_sticky(new_dir, new_info.entry.uid)) { + Printk (("sticky set on new ")); + goto out_unlock; + } + + Printk (("new newentry ")); + umsdos_ren_init (&new_info, &old_info, flags); + ret = umsdos_newentry (new_dentry->d_parent, &new_info); + chkstk (); + if (ret) { +Printk (("ret %d %d ", ret, new_info.fake.len)); + goto out_unlock; + } + + dret = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, + old_info.fake.len); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + goto out_unlock; +#if 0 + /* This is the same as dret */ + oldid = dret->d_inode; + old = creat_dentry (old_info.fake.fname, old_info.fake.len, + oldid, old_dentry->d_parent); +#endif + old = dret; + new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, + new_info.fake.len); + ret = PTR_ERR(new); + if (IS_ERR(new)) + goto out_dput; + + Printk (("msdos_rename ")); + check_dentry_path (old, "umsdos_rename_f OLD2"); + check_dentry_path (new, "umsdos_rename_f NEW2"); + ret = msdos_rename (old_dir, old, new_dir, new); + chkstk (); +printk("after m_rename ret %d ", ret); + /* dput(old); */ + dput(new); + + if (ret != 0) { + umsdos_delentry (new_dentry->d_parent, &new_info, + S_ISDIR (new_info.entry.mode)); chkstk (); - Printk (("ret %d ", ret)); - if (ret == 0) { - /* check sticky bit on old_dir */ - if (!(old_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) || - current->fsuid == old_info.entry.uid || - current->fsuid == old_dir->i_uid) { - /* Does new_name already exist? */ - Printk (("new findentry ")); - ret = umsdos_findentry (new_dir, &new_info, 0); - if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */ - !(new_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) || - current->fsuid == new_info.entry.uid || - current->fsuid == new_dir->i_uid) { - Printk (("new newentry ")); - umsdos_ren_init (&new_info, &old_info, flags); - ret = umsdos_newentry (new_dir, &new_info); - chkstk (); - Printk (("ret %d %d ", ret, new_info.fake.len)); - if (ret == 0) { - struct dentry *old, *new; - struct inode *oldid = NULL; - - ret = compat_umsdos_real_lookup (old_dir, old_info.fake.fname, old_info.fake.len, &oldid); - old = creat_dentry (old_info.fake.fname, old_info.fake.len, oldid, old_dentry->d_parent); - - new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL, new_dentry->d_parent); - - Printk (("msdos_rename ")); - inc_count (old_dir); - inc_count (new_dir); /* Both inode are needed later */ - - check_dentry (old); check_dentry (new); /* FIXME: debug only */ - ret = msdos_rename (old_dir, old, new_dir, new); - chkstk (); - Printk (("after m_rename ret %d ", ret)); - kill_dentry (old); - kill_dentry (new); - - if (ret != 0) { - umsdos_delentry (new_dir, &new_info, S_ISDIR (new_info.entry.mode)); - chkstk (); - } else { - ret = umsdos_delentry (old_dir, &old_info, S_ISDIR (old_info.entry.mode)); - chkstk (); - if (ret == 0) { - /* - * This umsdos_lookup_x does not look very useful. - * It makes sure that the inode of the file will - * be correctly setup (umsdos_patch_inode()) in - * case it is already in use. - * - * Not very efficient ... - */ - struct inode *inode; - - inc_count (new_dir); - PRINTK ((KERN_DEBUG "rename lookup len %d %d -- ", new_len, new_info.entry.flags)); - ret = umsdos_lookup_x (new_dir, new_dentry, 0); - inode = new_dentry->d_inode; - chkstk (); - if (ret != 0) { - printk ("UMSDOS: partial rename for file %.*s\n" - ,new_info.entry.name_len, new_info.entry.name); - } else { - /* - * Update f_pos so notify_change will succeed - * if the file was already in use. - */ - umsdos_set_dirinfo (inode, new_dir, new_info.f_pos); - d_move (old_dentry, new_dentry); - chkstk (); - /* iput (inode); FIXME */ - } - } - } - } - } else { - /* sticky bit set on new_dir */ - Printk (("sticky set on new ")); - ret = -EPERM; - } - } else { - /* sticky bit set on old_dir */ - Printk (("sticky set on old ")); - ret = -EPERM; - } - } - Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n")); - umsdos_unlockcreate (old_dir); - umsdos_unlockcreate (new_dir); + goto out_dput; } - check_dentry (old_dentry); - check_dentry (new_dentry); + ret = umsdos_delentry (old_dentry->d_parent, &old_info, + S_ISDIR (old_info.entry.mode)); + chkstk (); + if (ret) + goto out_dput; +#if 0 + /* + * Update f_pos so notify_change will succeed + * if the file was already in use. + */ + umsdos_set_dirinfo (new_dentry->d_inode, new_dir, new_info.f_pos); +#endif + if (old_dentry == dret) { +printk("umsdos_rename_f: old dentries match -- skipping d_move\n"); + goto out_dput; + } + d_move (old_dentry, new_dentry); + +out_dput: + dput(dret); + +out_unlock: + Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n")); + umsdos_unlockcreate (old_dir); + umsdos_unlockcreate (new_dir); + +out: + check_dentry_path (old_dentry, "umsdos_rename_f OLD3"); + check_dentry_path (new_dentry, "umsdos_rename_f NEW3"); Printk ((" _ret=%d\n", ret)); return ret; } /* - * Setup un Symbolic link or a (pseudo) hard link - * Return a negative error code or 0 if ok. + * Setup a Symbolic link or a (pseudo) hard link + * Return a negative error code or 0 if OK. + */ +/* #Specification: symbolic links / strategy + * A symbolic link is simply a file which hold a path. It is + * implemented as a normal MSDOS file (not very space efficient :-() + * + * I see 2 different way to do it. One is to place the link data + * in unused entry of the EMD file. The other is to have a separate + * file dedicated to hold all symbolic links data. + * + * Let's go for simplicity... */ -static int umsdos_symlink_x ( - struct inode *dir, - struct dentry *dentry, - const char *symname, /* name will point to this path */ - int mode, - char flags) + +static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry, + const char *symname, int mode, char flags) { - /* #Specification: symbolic links / strategy - * A symbolic link is simply a file which hold a path. It is - * implemented as a normal MSDOS file (not very space efficient :-() - * - * I see 2 different way to do it. One is to place the link data - * in unused entry of the EMD file. The other is to have a separate - * file dedicated to hold all symbolic links data. - * - * Let's go for simplicity... - */ + int ret, len; + struct file filp; + ret = umsdos_create_any (dir, dentry, mode, 0, flags); + if (ret) { +Printk (("umsdos_symlink ret %d ", ret)); + goto out; + } - int ret; + len = strlen (symname); - inc_count (dir); /* We keep the inode in case we need it */ - /* later */ - ret = umsdos_create_any (dir, dentry, mode, 0, flags); - Printk (("umsdos_symlink ret %d ", ret)); - if (ret == 0) { - int len = strlen (symname); - struct file filp; - loff_t myofs = 0; - - fill_new_filp (&filp, dentry); - - /* Make the inode acceptable to MSDOS FIXME */ - Printk ((KERN_WARNING "umsdos_symlink_x: /mn/ Is this ok?\n")); - Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino)); - ret = umsdos_file_write_kmem_real (&filp, symname, len, &myofs); - /* dput(dentry); ?? where did this come from FIXME */ - - if (ret >= 0) { - if (ret != len) { - ret = -EIO; - printk ("UMSDOS: " - "Can't write symbolic link data\n"); - } else { - ret = 0; - } - } - if (ret != 0) { - UMSDOS_unlink (dir, dentry); - dir = NULL; + fill_new_filp (&filp, dentry); + filp.f_pos = 0; + + /* Make the inode acceptable to MSDOS FIXME */ +Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n", +symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino)); + ret = umsdos_file_write_kmem_real (&filp, symname, len); + + if (ret >= 0) { + if (ret != len) { + ret = -EIO; + printk ("UMSDOS: " + "Can't write symbolic link data\n"); + } else { + ret = 0; } } - /* d_instantiate(dentry,dir); //already done in umsdos_create_any. */ + if (ret != 0) { + UMSDOS_unlink (dir, dentry); + } + +out: Printk (("\n")); return ret; } /* - * Setup un Symbolic link. - * Return a negative error code or 0 if ok. + * Setup a Symbolic link. + * Return a negative error code or 0 if OK. */ -int UMSDOS_symlink ( - struct inode *dir, - struct dentry *dentry, - const char *symname -) +int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry, + const char *symname) { return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0); } @@ -542,168 +572,78 @@ int UMSDOS_symlink ( /* * Add a link to an inode in a directory */ -int UMSDOS_link ( - struct dentry *olddentry, - struct inode *dir, - struct dentry *dentry) +int UMSDOS_link (struct dentry *olddentry, struct inode *dir, + struct dentry *dentry) { struct inode *oldinode = olddentry->d_inode; + struct inode *olddir; + char *path; + struct dentry *temp; + unsigned long buffer; + int ret; + struct umsdos_dirent entry; - /* #Specification: hard link / strategy - * Well ... hard link are difficult to implement on top of an - * MsDOS fat file system. Unlike UNIX file systems, there are no - * inode. A directory entry hold the functionality of the inode - * and the entry. - * - * We will used the same strategy as a normal Unix file system - * (with inode) except we will do it symbolically (using paths). - * - * Because anything can happen during a DOS session (defragment, - * directory sorting, etc...), we can't rely on MsDOS pseudo - * inode number to record the link. For this reason, the link - * will be done using hidden symbolic links. The following - * scenario illustrate how it work. - * - * Given a file /foo/file - * - * # - * ln /foo/file /tmp/file2 - * - * become internally - * - * mv /foo/file /foo/-LINK1 - * ln -s /foo/-LINK1 /foo/file - * ln -s /foo/-LINK1 /tmp/file2 - * # - * - * Using this strategy, we can operate on /foo/file or /foo/file2. - * We can remove one and keep the other, like a normal Unix hard link. - * We can rename /foo/file or /tmp/file2 independently. - * - * The entry -LINK1 will be hidden. It will hold a link count. - * When all link are erased, the hidden file is erased too. - */ - /* #Specification: weakness / hard link - * The strategy for hard link introduces a side effect that - * may or may not be acceptable. Here is the sequence - * - * # - * mkdir subdir1 - * touch subdir1/file - * mkdir subdir2 - * ln subdir1/file subdir2/file - * rm subdir1/file - * rmdir subdir1 - * rmdir: subdir1: Directory not empty - * # - * - * This happen because there is an invisible file (--link) in - * subdir1 which is referenced by subdir2/file. - * - * Any idea ? - */ - /* #Specification: weakness / hard link / rename directory - * Another weakness of hard link come from the fact that - * it is based on hidden symbolic links. Here is an example. - * - * # - * mkdir /subdir1 - * touch /subdir1/file - * mkdir /subdir2 - * ln /subdir1/file subdir2/file - * mv /subdir1 subdir3 - * ls -l /subdir2/file - * # - * - * Since /subdir2/file is a hidden symbolic link - * to /subdir1/..hlinkNNN, accessing it will fail since - * /subdir1 does not exist anymore (has been renamed). - */ - int ret = 0; + ret = -EPERM; + if (S_ISDIR (oldinode->i_mode)) + goto out; - if (S_ISDIR (oldinode->i_mode)) { - /* #Specification: hard link / directory - * A hard link can't be made on a directory. EPERM is returned - * in this case. - */ - ret = -EPERM; - } else if ((ret = umsdos_nevercreat (dir, dentry, -EPERM)) == 0) { - struct inode *olddir; + ret = umsdos_nevercreat (dir, dentry, -EPERM); + if (ret) + goto out; - ret = umsdos_get_dirowner (oldinode, &olddir); - Printk (("umsdos_link dir_owner = %lu -> %p [%d] ", - oldinode->u.umsdos_i.i_dir_owner, olddir, olddir->i_count)); - if (ret == 0) { - struct umsdos_dirent entry; + ret = -ENOMEM; + buffer = get_free_page(GFP_KERNEL); + if (!buffer) + goto out; - umsdos_lockcreate2 (dir, olddir); - ret = umsdos_inode2entry (olddir, oldinode, &entry); - if (ret == 0) { - Printk (("umsdos_link :%.*s: ino %lu flags %d " - ,entry.name_len, entry.name - ,oldinode->i_ino, entry.flags)); - if (!(entry.flags & UMSDOS_HIDDEN)) { - /* #Specification: hard link / first hard link - * The first time a hard link is done on a file, this - * file must be renamed and hidden. Then an internal - * symbolic link must be done on the hidden file. - * - * The second link is done after on this hidden file. - * - * It is expected that the Linux MSDOS file system - * keeps the same pseudo inode when a rename operation - * is done on a file in the same directory. - */ - struct umsdos_info info; - - ret = umsdos_newhidden (olddir, &info); - if (ret == 0) { - Printk (("olddir[%d] ", olddir->i_count)); - ret = umsdos_rename_f (olddentry->d_inode, olddentry, dir, dentry, UMSDOS_HIDDEN); - if (ret == 0) { - char *path = (char *) kmalloc (PATH_MAX, GFP_KERNEL); - - if (path == NULL) { - ret = -ENOMEM; - } else { - struct dentry *temp; - - temp = creat_dentry (entry.name, entry.name_len, NULL, NULL); - Printk (("olddir[%d] ", olddir->i_count)); - ret = umsdos_locate_path (oldinode, path); - Printk (("olddir[%d] ", olddir->i_count)); - if (ret == 0) { - inc_count (olddir); - ret = umsdos_symlink_x (olddir, temp, path, S_IFREG | 0777, UMSDOS_HLINK); - if (ret == 0) { - inc_count (dir); - ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777, UMSDOS_HLINK); - } - } - kfree (path); - } - } - } - } else { - char *path = (char *) kmalloc (PATH_MAX, GFP_KERNEL); - - if (path == NULL) { - ret = -ENOMEM; - } else { - ret = umsdos_locate_path (oldinode, path); - if (ret == 0) { - inc_count (dir); - ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777, UMSDOS_HLINK); - } - kfree (path); - } - } - } - umsdos_unlockcreate (olddir); - umsdos_unlockcreate (dir); + olddir = olddentry->d_parent->d_inode; + umsdos_lockcreate2 (dir, olddir); + + /* get the entry for the old name */ + ret = umsdos_dentry_to_entry(olddentry, &entry); + if (ret) + goto out_unlock; +Printk (("umsdos_link :%.*s: ino %lu flags %d ", +entry.name_len, entry.name ,oldinode->i_ino, entry.flags)); + + if (!(entry.flags & UMSDOS_HIDDEN)) { + struct umsdos_info info; + + ret = umsdos_newhidden (olddentry->d_parent, &info); + if (ret) + goto out_unlock; + + ret = umsdos_rename_f (olddentry->d_inode, olddentry, + dir, dentry, UMSDOS_HIDDEN); + if (ret) + goto out_unlock; + path = d_path(olddentry, (char *) buffer, PAGE_SIZE); + if (!path) + goto out_unlock; + temp = umsdos_lookup_dentry(olddentry->d_parent, entry.name, + entry.name_len); + if (IS_ERR(temp)) + goto out_unlock; + ret = umsdos_symlink_x (olddir, temp, path, + S_IFREG | 0777, UMSDOS_HLINK); + if (ret == 0) { + ret = umsdos_symlink_x (dir, dentry, path, + S_IFREG | 0777, UMSDOS_HLINK); } - /* iput (olddir); FIXME */ + dput(temp); + goto out_unlock; + } + path = d_path(olddentry, (char *) buffer, PAGE_SIZE); + if (path) { + ret = umsdos_symlink_x (dir, dentry, path, + S_IFREG | 0777, UMSDOS_HLINK); } + +out_unlock: + umsdos_unlockcreate (olddir); + umsdos_unlockcreate (dir); + free_page(buffer); +out: if (ret == 0) { struct iattr newattrs; @@ -711,532 +651,428 @@ int UMSDOS_link ( newattrs.ia_valid = 0; ret = UMSDOS_notify_change (olddentry, &newattrs); } -/* dput (olddentry); - * dput (dentry); FIXME.... */ - Printk (("umsdos_link %d\n", ret)); return ret; } - /* * Add a new file into the alternate directory. * The file is added to the real MSDOS directory. If successful, it - * is then added to the EDM file. + * is then added to the EMD file. * * Return the status of the operation. 0 mean success. */ -int UMSDOS_create ( - struct inode *dir, - struct dentry *dentry, - int mode /* Permission bit + file type ??? */ -) -{ /* Will hold the inode of the newly created file */ +int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode) +{ + int ret; Printk ((KERN_ERR "UMSDOS_create: entering\n")); - check_dentry (dentry); - return umsdos_create_any (dir, dentry, mode, 0, 0); + check_dentry_path (dentry, "UMSDOS_create START"); + ret = umsdos_create_any (dir, dentry, mode, 0, 0); + check_dentry_path (dentry, "UMSDOS_create END"); + return ret; } - /* * Add a sub-directory in a directory */ -int UMSDOS_mkdir ( - struct inode *dir, - struct dentry *dentry, - int mode) +/* #Specification: mkdir / Directory already exist in DOS + * We do the same thing as for file creation. + * For all user it is an error. + */ +/* #Specification: mkdir / umsdos directory / create EMD + * When we created a new sub-directory in a UMSDOS + * directory (one with full UMSDOS semantics), we + * create immediately an EMD file in the new + * sub-directory so it inherits UMSDOS semantics. + */ +int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode) { - int ret = umsdos_nevercreat (dir, dentry, -EEXIST); + struct dentry *temp; + struct inode *inode; + int ret, err; + struct umsdos_info info; - if (ret == 0) { - struct umsdos_info info; + ret = umsdos_nevercreat (dir, dentry, -EEXIST); + if (ret) + goto out; - ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - Printk (("umsdos_mkdir %d\n", ret)); - if (ret == 0) { - info.entry.mode = mode | S_IFDIR; - info.entry.rdev = 0; - info.entry.uid = current->fsuid; - info.entry.gid = (dir->i_mode & S_ISGID) - ? dir->i_gid : current->fsgid; - info.entry.ctime = info.entry.atime = info.entry.mtime - = CURRENT_TIME; - info.entry.flags = 0; - umsdos_lockcreate (dir); - info.entry.nlink = 1; - ret = umsdos_newentry (dir, &info); - Printk (("newentry %d ", ret)); - if (ret == 0) { - struct dentry *temp, *tdir; - - tdir = creat_dentry ("mkd-dir", 7, dir, NULL); - temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); - inc_count (dir); - ret = msdos_mkdir (dir, temp, mode); - - if (ret != 0) { - umsdos_delentry (dir, &info, 1); - /* #Specification: mkdir / Directory already exist in DOS - * We do the same thing as for file creation. - * For all user it is an error. - */ - } else { - /* #Specification: mkdir / umsdos directory / create EMD - * When we created a new sub-directory in a UMSDOS - * directory (one with full UMSDOS semantic), we - * create immediately an EMD file in the new - * sub-directory so it inherit UMSDOS semantic. - */ - struct inode *subdir; - - ret = compat_umsdos_real_lookup (dir, info.fake.fname, - info.fake.len, &subdir); - if (ret == 0) { - struct dentry *tdentry, - *tdsub; - - tdsub = creat_dentry ("mkd-emd", 7, subdir, NULL); - tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tdsub); - ret = msdos_create (subdir, tdentry, S_IFREG | 0777); - kill_dentry (tdentry); /* we don't want empty EMD file to be visible ! too bad kill_dentry does nothing at the moment :-) FIXME */ - kill_dentry (tdsub); - umsdos_setup_dir_inode (subdir); /* this should setup dir so it is promoted to EMD, and EMD file is not visible inside it */ - subdir = NULL; - d_instantiate (dentry, temp->d_inode); - /* iput (result); FIXME */ - } - if (ret < 0) { - printk ("UMSDOS: Can't create empty --linux-.---\n"); - } - /* iput (subdir); FIXME */ - } - } - umsdos_unlockcreate (dir); + ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + if (ret) { +Printk (("umsdos_mkdir %d\n", ret)); + goto out; + } + + umsdos_lockcreate (dir); + info.entry.mode = mode | S_IFDIR; + info.entry.rdev = 0; + info.entry.uid = current->fsuid; + info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME; + info.entry.flags = 0; + info.entry.nlink = 1; + ret = umsdos_newentry (dentry->d_parent, &info); + if (ret) { +Printk (("newentry %d ", ret)); + goto out_unlock; + } + + /* lookup the short name dentry */ + temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, + info.fake.len); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out_unlock; + + /* Make sure the short name doesn't exist */ + ret = -EEXIST; + if (temp->d_inode) { +printk("umsdos_mkdir: short name %s/%s exists\n", +dentry->d_parent->d_name.name, info.fake.fname); + goto out_remove; + } + + ret = msdos_mkdir (dir, temp, mode); + if (ret) + goto out_remove; + + inode = temp->d_inode; + umsdos_lookup_patch_new(temp, &info.entry, info.f_pos); + + /* + * Note! The long and short name might be the same, + * so check first before doing the instantiate ... + */ + if (dentry != temp) { + if (!dentry->d_inode) { + inode->i_count++; + d_instantiate(dentry, inode); + } else { + printk("umsdos_mkdir: not negative??\n"); } + } else { + printk("umsdos_mkdir: dentries match, skipping inst\n"); } + + /* create the EMD file */ + err = umsdos_make_emd(dentry); + + /* + * set up the dir so it is promoted to EMD, + * with the EMD file invisible inside it. + */ + umsdos_setup_dir(temp); + goto out_dput; + +out_remove: + umsdos_delentry (dentry->d_parent, &info, 1); + +out_dput: + /* kill off the short name dentry */ + if (temp != dentry) + d_drop(temp); + dput(temp); + +out_unlock: + umsdos_unlockcreate (dir); Printk (("umsdos_mkdir %d\n", ret)); - /* dput (dentry); / * FIXME /mn/ */ +out: return ret; } /* * Add a new device special file into a directory. + * + * #Specification: Special files / strategy + * Device special file, pipes, etc ... are created like normal + * file in the msdos file system. Of course they remain empty. + * + * One strategy was to create those files only in the EMD file + * since they were not important for MSDOS. The problem with + * that, is that there were not getting inode number allocated. + * The MSDOS filesystems is playing a nice game to fake inode + * number, so why not use it. + * + * The absence of inode number compatible with those allocated + * for ordinary files was causing major trouble with hard link + * in particular and other parts of the kernel I guess. */ -int UMSDOS_mknod ( - struct inode *dir, - struct dentry *dentry, - int mode, - int rdev) +int UMSDOS_mknod (struct inode *dir, struct dentry *dentry, + int mode, int rdev) { - /* #Specification: Special files / strategy - * Device special file, pipes, etc ... are created like normal - * file in the msdos file system. Of course they remain empty. - * - * One strategy was to create those files only in the EMD file - * since they were not important for MSDOS. The problem with - * that, is that there were not getting inode number allocated. - * The MSDOS filesystems is playing a nice game to fake inode - * number, so why not use it. - * - * The absence of inode number compatible with those allocated - * for ordinary files was causing major trouble with hard link - * in particular and other parts of the kernel I guess. - */ - int ret; - - check_dentry (dentry); + check_dentry_path (dentry, "UMSDOS_mknod START"); ret = umsdos_create_any (dir, dentry, mode, rdev, 0); - check_dentry (dentry); - - /* dput(dentry); / * /mn/ FIXME! */ + check_dentry_path (dentry, "UMSDOS_mknod END"); return ret; } /* * Remove a sub-directory. */ -int UMSDOS_rmdir ( - struct inode *dir, - struct dentry *dentry) +int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry) { - /* #Specification: style / iput strategy - * In the UMSDOS project, I am trying to apply a single - * programming style regarding inode management. Many - * entry point are receiving an inode to act on, and must - * do an iput() as soon as they are finished with - * the inode. - * - * For simple case, there is no problem. When you introduce - * error checking, you end up with many iput placed around the - * code. - * - * The coding style I use all around is one where I am trying - * to provide independent flow logic (I don't know how to - * name this). With this style, code is easier to understand - * but you rapidly get iput() all around. Here is an exemple - * of what I am trying to avoid. - * - * # - * if (a){ - * ... - * if(b){ - * ... - * } - * ... - * if (c){ - * // Complex state. Was b true ? - * ... - * } - * ... - * } - * // Weird state - * if (d){ - * // ... - * } - * // Was iput finally done ? - * return status; - * # - * - * Here is the style I am using. Still sometime I do the - * first when things are very simple (or very complicated :-( ) - * - * # - * if (a){ - * if (b){ - * ... - * }else if (c){ - * // A single state gets here - * } - * }else if (d){ - * ... - * } - * return status; - * # - * - * Again, while this help clarifying the code, I often get a lot - * of iput(), unlike the first style, where I can place few - * "strategic" iput(). "strategic" also mean, more difficult - * to place. - * - * So here is the style I will be using from now on in this project. - * There is always an iput() at the end of a function (which has - * to do an iput()). One iput by inode. There is also one iput() - * at the places where a successful operation is achieved. This - * iput() is often done by a sub-function (often from the msdos - * file system). So I get one too many iput() ? At the place - * where an iput() is done, the inode is simply nulled, disabling - * the last one. - * - * # - * if (a){ - * if (b){ - * ... - * }else if (c){ - * msdos_rmdir(dir,...); - * dir = NULL; - * } - * }else if (d){ - * ... - * } - * iput (dir); - * return status; - * # - * - * Note that the umsdos_lockcreate() and umsdos_unlockcreate() function - * pair goes against this practice of "forgetting" the inode as soon - * as possible. - */ - - int ret; + struct dentry *temp; + int ret, err, empty; + struct umsdos_info info; ret = umsdos_nevercreat (dir, dentry, -EPERM); - if (ret == 0) { - volatile struct inode *sdir; + if (ret) + goto out; + +#if 0 /* no need for lookup ... we have a dentry ... */ + ret = umsdos_lookup_x (dir, dentry, 0); + Printk (("rmdir lookup %d ", ret)); + if (ret != 0) + goto out; +#endif - inc_count (dir); - ret = umsdos_lookup_x (dir, dentry, 0); - sdir = dentry->d_inode; - Printk (("rmdir lookup %d ", ret)); - if (ret == 0) { - int empty; - - umsdos_lockcreate (dir); - - Printk ((" /mn/ rmdir: FIXME EBUSY TEST: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count)); - sdir->i_count = 1; /* /mn/ FIXME! DELME! FOR TEST ONLY ! */ - - if (sdir->i_count > 1) { - Printk ((" /mn/ rmdir: FIXME EBUSY: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count)); - ret = -EBUSY; - } else if ((empty = umsdos_isempty (sdir)) != 0) { - struct dentry *tdentry, *tedir; - - tedir = creat_dentry ("emd-rmd", 7, dir, NULL); - tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tedir); - umsdos_real_lookup (dir, tdentry); /* fill inode part */ - Printk (("isempty %d i_count %d ", empty, sdir->i_count)); - /* check sticky bit */ - if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) || - current->fsuid == sdir->i_uid || - current->fsuid == dir->i_uid) { - if (empty == 1) { - /* We have to remove the EMD file */ - ret = msdos_unlink (sdir, tdentry); - Printk (("UMSDOS_rmdir: unlinking empty EMD ret=%d", ret)); - sdir = NULL; - } - /* sdir must be free before msdos_rmdir() */ - /* iput (sdir); FIXME */ - sdir = NULL; - Printk (("isempty ret %d nlink %d ", ret, dir->i_nlink)); - if (ret == 0) { - struct umsdos_info info; - struct dentry *temp, *tdir; - - inc_count (dir); - umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - /* The findentry is there only to complete */ - /* the mangling */ - umsdos_findentry (dir, &info, 2); - - tdir = creat_dentry ("dir-rmd", 7, dir, NULL); - temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); - umsdos_real_lookup (dir, temp); /* fill inode part */ - - Printk ((KERN_ERR " rmdir start dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb)); /* FIXME: /mn/ debug only */ - Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%lu\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode->i_ino)); - Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino)); - - ret = msdos_rmdir (dir, temp); - - Printk ((KERN_ERR " rmdir passed %d\n", ret)); /* FIXME: /mn/ debug only */ - Printk ((KERN_ERR " rmdir end dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb)); - Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%p\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode)); - Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino)); - - kill_dentry (tdir); - kill_dentry (temp); - - if (ret == 0) { - ret = umsdos_delentry (dir, &info, 1); - d_delete (dentry); - } - } - } else { - /* sticky bit set and we don't have permission */ - Printk (("sticky set ")); - ret = -EPERM; - } - } else { - /* - * The subdirectory is not empty, so leave it there - */ - ret = -ENOTEMPTY; - } - /* iput(sdir); FIXME */ - umsdos_unlockcreate (dir); + umsdos_lockcreate (dir); + ret = -EBUSY; + if (dentry->d_count > 1) { + shrink_dcache_parent(dentry); + if (dentry->d_count > 1) { +printk("umsdos_rmdir: %s/%s busy\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + goto out_unlock; } } - /* dput(dentry); FIXME /mn/ */ + + /* check whether the EMD is empty */ + empty = umsdos_isempty (dentry); + ret = -ENOTEMPTY; + if (empty == 0) + goto out_unlock; + + /* Have to remove the EMD file? */ + if (empty == 1) { + struct dentry *demd; + /* check sticky bit */ + ret = -EPERM; + if (is_sticky(dir, dentry->d_inode->i_uid)) { +printk("umsdos_rmdir: %s/%s is sticky\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + goto out_unlock; + } + + ret = -ENOTEMPTY; + /* see if there's an EMD file ... */ + demd = umsdos_get_emd_dentry(dentry); + if (IS_ERR(demd)) + goto out_unlock; +printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n", +demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode); + + err = msdos_unlink (dentry->d_inode, demd); +Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err)); + dput(demd); + if (err) + goto out_unlock; + } + + umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + /* Call findentry to complete the mangling */ + umsdos_findentry (dentry->d_parent, &info, 2); + temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, + info.fake.len); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out_unlock; + /* + * If the short name matches the dentry, dput() it now. + */ + if (temp == dentry) { + dput(temp); +printk("umsdos_rmdir: %s/%s, short matches long\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + } + + /* + * Attempt to remove the msdos name. + */ + ret = msdos_rmdir (dir, temp); + if (ret && ret != -ENOENT) + goto out_dput; + + /* OK so far ... remove the name from the EMD */ + ret = umsdos_delentry (dentry->d_parent, &info, 1); + +out_dput: + /* dput() temp if we didn't do it above */ + if (temp != dentry) { + d_drop(temp); + dput(temp); + if (!ret) + d_delete (dentry); +printk("umsdos_rmdir: %s/%s, short=%s dput\n", +dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname); + } + +out_unlock: + umsdos_unlockcreate (dir); + +out: Printk (("umsdos_rmdir %d\n", ret)); return ret; } - /* * Remove a file from the directory. + * + * #Specification: hard link / deleting a link + * When we delete a file, and this file is a link + * we must subtract 1 to the nlink field of the + * hidden link. + * + * If the count goes to 0, we delete this hidden + * link too. */ -int UMSDOS_unlink ( - struct inode *dir, - struct dentry *dentry) +int UMSDOS_unlink (struct inode *dir, struct dentry *dentry) { + struct dentry *temp; + struct inode *inode; int ret; + struct umsdos_info info; - Printk ((" *** UMSDOS_unlink entering /mn/ *** \n")); +Printk (("UMSDOS_unlink: entering %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name)); ret = umsdos_nevercreat (dir, dentry, -EPERM); + if (ret) + goto out; + + ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); + if (ret) + goto out; + + umsdos_lockcreate (dir); + ret = umsdos_findentry (dentry->d_parent, &info, 1); + if (ret) { +printk("UMSDOS_unlink: findentry returned %d\n", ret); + goto out_unlock; + } - Printk (("UMSDOS_unlink /mn/: nevercreat=%d\n", ret)); +Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); + ret = -EPERM; + /* check sticky bit */ + if (is_sticky(dir, info.entry.uid)) { +printk("umsdos_unlink: %s/%s is sticky\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + goto out_unlock; + } - if (ret == 0) { - struct umsdos_info info; + ret = 0; + if (info.entry.flags & UMSDOS_HLINK) { +printk("UMSDOS_unlink: hard link %s/%s, fake=%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname); + /* + * First, get the inode of the hidden link + * using the standard lookup function. + */ - ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - if (ret == 0) { - umsdos_lockcreate (dir); - ret = umsdos_findentry (dir, &info, 1); - Printk (("UMSDOS_unlink: findentry returned %d\n", ret)); + ret = umsdos_lookup_x (dir, dentry, 0); + inode = dentry->d_inode; + if (ret) + goto out_unlock; + + Printk (("unlink nlink = %d ", inode->i_nlink)); + inode->i_nlink--; + if (inode->i_nlink == 0) { + struct umsdos_dirent entry; + + ret = umsdos_dentry_to_entry (dentry, &entry); if (ret == 0) { - Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); - /* check sticky bit */ - if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) || - current->fsuid == info.entry.uid || - current->fsuid == dir->i_uid) { - if (info.entry.flags & UMSDOS_HLINK) { - /* #Specification: hard link / deleting a link - * When we deletes a file, and this file is a link - * we must subtract 1 to the nlink field of the - * hidden link. - * - * If the count goes to 0, we delete this hidden - * link too. - */ - /* - * First, get the inode of the hidden link - * using the standard lookup function. - */ - struct inode *inode; - - inc_count (dir); - ret = umsdos_lookup_x (dir, dentry, 0); - inode = dentry->d_inode; - if (ret == 0) { - Printk (("unlink nlink = %d ", inode->i_nlink)); - inode->i_nlink--; - if (inode->i_nlink == 0) { - struct inode *hdir = iget (inode->i_sb - ,inode->u.umsdos_i.i_dir_owner); - struct umsdos_dirent entry; - - ret = umsdos_inode2entry (hdir, inode, &entry); - if (ret == 0) { - ret = UMSDOS_unlink (hdir, dentry); - } else { - /* iput (hdir); FIXME */ - } - } else { - struct iattr newattrs; - - newattrs.ia_valid = 0; - ret = UMSDOS_notify_change (dentry, &newattrs); - } - /* iput (inode); FIXME */ - } - } - if (ret == 0) { - ret = umsdos_delentry (dir, &info, 0); - if (ret == 0) { - struct dentry *temp, - *tdir; - - Printk (("Avant msdos_unlink %.*s ", info.fake.len, info.fake.fname)); - inc_count (dir); /* FIXME /mn/ is this needed anymore now that msdos_unlink locks dir using d_parent ? */ - tdir = creat_dentry ("dir-del", 7, dir, NULL); /* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */ - temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); - umsdos_real_lookup (dir, temp); /* fill inode part */ - - ret = msdos_unlink_umsdos (dir, temp); - Printk (("msdos_unlink %.*s %o ret %d ", info.fake.len, info.fake.fname - ,info.entry.mode, ret)); - - d_delete (dentry); - - kill_dentry (tdir); - kill_dentry (temp); - } - } - } else { - /* sticky bit set and we've not got permission */ - Printk (("sticky set ")); - ret = -EPERM; - } + ret = UMSDOS_unlink (dentry->d_parent->d_inode, + dentry); } - umsdos_unlockcreate (dir); + } else { + struct iattr newattrs; + newattrs.ia_valid = 0; + ret = UMSDOS_notify_change (dentry, &newattrs); } } - /* dput(dentry); FIXME: shouldn't this be done in msdos_unlink ? */ + if (ret) + goto out_unlock; + + /* get the short name dentry */ + temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, + info.fake.len); + if (IS_ERR(temp)) + goto out_unlock; + + /* + * If the short name matches the long, + * dput() it now so it's not busy. + */ + if (temp == dentry) { +printk("UMSDOS_unlink: %s/%s, short matches long\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + dput(temp); + } + + ret = umsdos_delentry (dentry->d_parent, &info, 0); + if (ret && ret != -ENOENT) + goto out_dput; + +printk("UMSDOS: Before msdos_unlink %.*s ", +info.fake.len, info.fake.fname); + ret = msdos_unlink_umsdos (dir, temp); + +Printk (("msdos_unlink %.*s %o ret %d ", +info.fake.len, info.fake.fname ,info.entry.mode, ret)); + + /* dput() temp if we didn't do it above */ +out_dput: + if (temp != dentry) { + d_drop(temp); + dput(temp); + if (!ret) + d_delete (dentry); +printk("umsdos_unlink: %s/%s, short=%s dput\n", +dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname); + } + +out_unlock: + umsdos_unlockcreate (dir); +out: Printk (("umsdos_unlink %d\n", ret)); return ret; } - /* - * Rename a file (move) in the file system. + * Rename (move) a file. */ -int UMSDOS_rename ( - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry) +int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { - /* #Specification: weakness / rename - * There is a case where UMSDOS rename has a different behavior - * than normal UNIX file system. Renaming an open file across - * directory boundary does not work. Renaming an open file within - * a directory does work however. - * - * The problem (not sure) is in the linux VFS msdos driver. - * I believe this is not a bug but a design feature, because - * an inode number represent some sort of directory address - * in the MSDOS directory structure. So moving the file into - * another directory does not preserve the inode number. - */ - int ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST); + int ret; - if (ret == 0) { - /* umsdos_rename_f eat the inode and we may need those later */ - inc_count (old_dir); - inc_count (new_dir); - ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0); - if (ret == -EEXIST) { - /* #Specification: rename / new name exist - * If the destination name already exist, it will - * silently be removed. EXT2 does it this way - * and this is the spec of SUNOS. So does UMSDOS. - * - * If the destination is an empty directory it will - * also be removed. - */ - /* #Specification: rename / new name exist / possible flaw - * The code to handle the deletion of the target (file - * and directory) use to be in umsdos_rename_f, surrounded - * by proper directory locking. This was insuring that only - * one process could achieve a rename (modification) operation - * in the source and destination directory. This was also - * insuring the operation was "atomic". - * - * This has been changed because this was creating a kernel - * stack overflow (stack is only 4k in the kernel). To avoid - * the code doing the deletion of the target (if exist) has - * been moved to a upper layer. umsdos_rename_f is tried - * once and if it fails with EEXIST, the target is removed - * and umsdos_rename_f is done again. - * - * This makes the code cleaner and (not sure) solve a - * deadlock problem one tester was experiencing. - * - * The point is to mention that possibly, the semantic of - * "rename" may be wrong. Anyone dare to check that :-) - * Be aware that IF it is wrong, to produce the problem you - * will need two process trying to rename a file to the - * same target at the same time. Again, I am not sure it - * is a problem at all. - */ - /* This is not super efficient but should work */ - inc_count (new_dir); - ret = UMSDOS_unlink (new_dir, new_dentry); - chkstk (); - Printk (("rename unlink ret %d -- ", ret)); - if (ret == -EISDIR) { - inc_count (new_dir); - ret = UMSDOS_rmdir (new_dir, new_dentry); - chkstk (); - Printk (("rename rmdir ret %d -- ", ret)); - } - if (ret == 0) { - ret = umsdos_rename_f (old_dir, old_dentry, - new_dir, new_dentry, 0); - new_dir = old_dir = NULL; - } - } + ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST); + if (ret) + goto out; + + ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0); + if (ret != -EEXIST) + goto out; + + /* This is not terribly efficient but should work. */ + ret = UMSDOS_unlink (new_dir, new_dentry); + chkstk (); + Printk (("rename unlink ret %d -- ", ret)); + if (ret == -EISDIR) { + ret = UMSDOS_rmdir (new_dir, new_dentry); + chkstk (); + Printk (("rename rmdir ret %d -- ", ret)); } - /* - * dput (new_dentry); - * dput (old_dentry); FIXME /mn/ */ + if (ret) + goto out; + + /* this time the rename should work ... */ + ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0); + +out: return ret; } diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index f0252254a..39605218d 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -18,9 +18,6 @@ #include <asm/uaccess.h> -#define PRINTK(x) -#define Printk(x) printk x - extern struct inode *pseudo_root; @@ -30,8 +27,7 @@ struct RDIR_FILLDIR { int real_root; }; -static int rdir_filldir ( - void *buf, +static int rdir_filldir ( void *buf, const char *name, int name_len, off_t offset, @@ -40,16 +36,13 @@ static int rdir_filldir ( int ret = 0; struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf; - PRINTK ((KERN_DEBUG "rdir_filldir /mn/: entering\n")); if (d->real_root) { PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n")); /* real root of a pseudo_rooted partition */ if (name_len != UMSDOS_PSDROOT_LEN || memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) { /* So it is not the /linux directory */ - if (name_len == 2 - && name[0] == '.' - && name[1] == '.') { + if (name_len == 2 && name[0] == '.' && name[1] == '.') { /* Make sure the .. entry points back to the pseudo_root */ ino = pseudo_root->i_ino; } @@ -57,30 +50,22 @@ static int rdir_filldir ( } } else { /* Any DOS directory */ - PRINTK ((KERN_DEBUG "rdir_filldir /mn/: calling d->filldir (%p) for %.*s (%lu)\n", d->filldir, name_len, name, ino)); ret = d->filldir (d->dirbuf, name, name_len, offset, ino); } return ret; } -static int UMSDOS_rreaddir ( - struct file *filp, - void *dirbuf, - filldir_t filldir) +static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir) { - struct RDIR_FILLDIR bufk; struct inode *dir = filp->f_dentry->d_inode; - - PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: entering %p %p\n", filldir, dirbuf)); - + struct RDIR_FILLDIR bufk; bufk.filldir = filldir; bufk.dirbuf = dirbuf; - bufk.real_root = pseudo_root - && dir == iget (dir->i_sb, UMSDOS_ROOT_INO) - && dir == iget (pseudo_root->i_sb, UMSDOS_ROOT_INO); - PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: calling fat_readdir with filldir=%p and exiting\n", filldir)); + bufk.real_root = pseudo_root && + dir->i_ino == UMSDOS_ROOT_INO && + dir->i_sb == pseudo_root->i_sb; return fat_readdir (filp, &bufk, rdir_filldir); } @@ -90,167 +75,150 @@ static int UMSDOS_rreaddir ( * If the result is a directory, make sure we find out if it is * a promoted one or not (calling umsdos_setup_dir_inode(inode)). */ -int umsdos_rlookup_x ( - struct inode *dir, - struct dentry *dentry, - int nopseudo) -{ /* Don't care about pseudo root mode */ +/* #Specification: pseudo root / DOS/.. + * In the real root directory (c:\), the directory .. + * is the pseudo root (c:\linux). + */ +int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo) +{ /* so locating "linux" will work */ - int len = dentry->d_name.len; const char *name = dentry->d_name.name; + int len = dentry->d_name.len; struct inode *inode; int ret; - if (pseudo_root - && len == 2 - && name[0] == '.' - && name[1] == '.' - && dir == iget (dir->i_sb, UMSDOS_ROOT_INO) - && dir == iget (pseudo_root->i_sb, UMSDOS_ROOT_INO)) { - /* *result = pseudo_root; */ - Printk ((KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n")); - inc_count (pseudo_root); + if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' && + dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) { +printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n"); + pseudo_root->i_count++; + d_add(dentry, pseudo_root); ret = 0; - /* #Specification: pseudo root / DOS/.. - * In the real root directory (c:\), the directory .. - * is the pseudo root (c:\linux). - */ - } else { - ret = umsdos_real_lookup (dir, dentry); - inode = dentry->d_inode; + goto out; + } -#if 0 - Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup for %.*s in %lu returned %d\n", len, name, dir->i_ino, ret)); - Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup: inode is %p resolving to ", inode)); - if (inode) { /* /mn/ FIXME: DEL_ME */ - Printk ((KERN_DEBUG "i_ino=%lu\n", inode->i_ino)); - } else { - Printk ((KERN_DEBUG "NONE!\n")); + ret = umsdos_real_lookup (dir, dentry); + inode = dentry->d_inode; + if ((ret == 0) && inode) { + if (inode == pseudo_root && !nopseudo) { + /* #Specification: pseudo root / DOS/linux + * Even in the real root directory (c:\), the directory + * /linux won't show + */ +printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n"); + /* make the dentry negative */ + d_delete(dentry); } -#endif - - if ((ret == 0) && inode) { - - if (pseudo_root && inode == pseudo_root && !nopseudo) { - /* #Specification: pseudo root / DOS/linux - * Even in the real root directory (c:\), the directory - * /linux won't show - */ - Printk ((KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n")); - ret = -ENOENT; - iput (pseudo_root); /* FIXME? */ - - } else if (S_ISDIR (inode->i_mode)) { - /* We must place the proper function table */ - /* depending if this is a MsDOS directory or an UMSDOS directory */ - Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", inode->i_ino)); - umsdos_setup_dir_inode (inode); - } + else if (S_ISDIR (inode->i_mode)) { + /* We must place the proper function table + * depending on whether this is an MS-DOS or + * a UMSDOS directory + */ +Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", +inode->i_ino)); + umsdos_setup_dir(dentry); } } - iput (dir); /* FIXME? */ +out: PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret)); return ret; } -int UMSDOS_rlookup ( - struct inode *dir, - struct dentry *dentry -) +int UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry) { - PRINTK ((KERN_DEBUG "UMSDOS_rlookup /mn/: executing umsdos_rlookup_x for ino=%lu in %.*s\n", dir->i_ino, (int) dentry->d_name.len, dentry->d_name.name)); return umsdos_rlookup_x (dir, dentry, 0); } -static int UMSDOS_rrmdir ( - struct inode *dir, - struct dentry *dentry) +/* #Specification: dual mode / rmdir in a DOS directory + * In a DOS (not EMD in it) directory, we use a reverse strategy + * compared with a UMSDOS directory. We assume that a subdirectory + * of a DOS directory is also a DOS directory. This is not always + * true (umssync may be used anywhere), but makes sense. + * + * So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY + * then we check if it is a Umsdos directory. We check if it is + * really empty (only . .. and --linux-.--- in it). If it is true + * we remove the EMD and do a msdos_rmdir() again. + * + * In a Umsdos directory, we assume all subdirectories are also + * Umsdos directories, so we check the EMD file first. + */ +/* #Specification: pseudo root / rmdir /DOS + * The pseudo sub-directory /DOS can't be removed! + * This is done even if the pseudo root is not a Umsdos + * directory anymore (very unlikely), but an accident (under + * MS-DOS) is always possible. + * + * EPERM is returned. + */ +static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry) { - /* #Specification: dual mode / rmdir in a DOS directory - * In a DOS (not EMD in it) directory, we use a reverse strategy - * compared with an Umsdos directory. We assume that a subdirectory - * of a DOS directory is also a DOS directory. This is not always - * true (umssync may be used anywhere), but make sense. - * - * So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY - * then we check if it is a Umsdos directory. We check if it is - * really empty (only . .. and --linux-.--- in it). If it is true - * we remove the EMD and do a msdos_rmdir() again. - * - * In a Umsdos directory, we assume all subdirectory are also - * Umsdos directory, so we check the EMD file first. - */ - int ret; + int ret, empty; - if (umsdos_is_pseudodos (dir, dentry)) { - /* #Specification: pseudo root / rmdir /DOS - * The pseudo sub-directory /DOS can't be removed! - * This is done even if the pseudo root is not a Umsdos - * directory anymore (very unlikely), but an accident (under - * MsDOS) is always possible. - * - * EPERM is returned. - */ - ret = -EPERM; - } else { - umsdos_lockcreate (dir); - inc_count (dir); - ret = msdos_rmdir (dir, dentry); - if (ret == -ENOTEMPTY) { - struct inode *sdir; + ret = -EPERM; + if (umsdos_is_pseudodos (dir, dentry)) + goto out; - inc_count (dir); + umsdos_lockcreate (dir); + ret = -EBUSY; + if (dentry->d_count > 1) { + shrink_dcache_parent(dentry); + if (dentry->d_count > 1) + goto out_unlock; + } - ret = UMSDOS_rlookup (dir, dentry); - sdir = dentry->d_inode; - PRINTK (("rrmdir lookup %d ", ret)); - if (ret == 0) { - int empty; + ret = msdos_rmdir (dir, dentry); + if (ret != -ENOTEMPTY) + goto out_check; - if ((empty = umsdos_isempty (sdir)) != 0) { - PRINTK (("isempty %d i_count %d ", empty, - atomic_read (&sdir->i_count))); - if (empty == 2) { - /* - * Not a Umsdos directory, so the previous msdos_rmdir - * was not lying :-) - */ - ret = -ENOTEMPTY; - } else if (empty == 1) { - /* We have to removed the EMD file */ - struct dentry *temp; +#if 0 /* why do this? we have the dentry ... */ + ret = UMSDOS_rlookup (dir, dentry); + PRINTK (("rrmdir lookup %d ", ret)); + if (ret) + goto out_unlock; + ret = -ENOTEMPTY; +#endif - Printk ((KERN_WARNING "UMSDOS_rmdir: hmmm... whatabout inode ? FIXME\n")); - temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, NULL); /* FIXME: prolly should fill inode part ? */ - ret = msdos_unlink (sdir, temp); - sdir = NULL; - if (ret == 0) { - inc_count (dir); - ret = msdos_rmdir (dir, dentry); - } - } - } else { - ret = -ENOTEMPTY; - } - /* iput (sdir); FIXME */ - } + empty = umsdos_isempty (dentry); + if (empty == 1) { + struct dentry *temp; + /* We have to remove the EMD file. */ + temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE, + UMSDOS_EMD_NAMELEN); + ret = PTR_ERR(temp); + if (!IS_ERR(temp)) { + ret = 0; + if (temp->d_inode) + ret = msdos_unlink (dentry->d_inode, temp); + dput(temp); } - umsdos_unlockcreate (dir); + if (ret) + goto out_unlock; } - /* iput (dir); FIXME */ + /* now retry the original ... */ + ret = msdos_rmdir (dir, dentry); + +out_check: + /* Check whether we succeeded ... */ + if (!ret) + d_delete(dentry); + +out_unlock: + umsdos_unlockcreate (dir); +out: + check_inode (dir); return ret; } /* #Specification: dual mode / introduction * One goal of UMSDOS is to allow a practical and simple coexistence - * between MsDOS and Linux in a single partition. Using the EMD file - * in each directory, UMSDOS add Unix semantics and capabilities to - * normal DOS file system. To help and simplify coexistence, here is + * between MS-DOS and Linux in a single partition. Using the EMD file + * in each directory, UMSDOS adds Unix semantics and capabilities to + * a normal DOS filesystem. To help and simplify coexistence, here is * the logic related to the EMD file. * - * If it is missing, then the directory is managed by the MsDOS driver. + * If it is missing, then the directory is managed by the MS-DOS driver. * The names are limited to DOS limits (8.3). No links, no device special * and pipe and so on. * @@ -259,12 +227,12 @@ static int UMSDOS_rrmdir ( * of the real DOS directory and the EMD. * * Whenever umssync is applied to a directory without EMD, one is - * created on the fly. The directory is promoted to full unix semantic. + * created on the fly. The directory is promoted to full Unix semantics. * Of course, the ls command will show exactly the same content as before * the umssync session. * - * It is believed that the user/admin will promote directories to unix - * semantic as needed. + * It is believed that the user/admin will promote directories to Unix + * semantics as needed. * * The strategy to implement this is to use two function table (struct * inode_operations). One for true UMSDOS directory and one for directory @@ -272,18 +240,19 @@ static int UMSDOS_rrmdir ( * * Functions related to the DOS semantic (but aware of UMSDOS) generally * have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate - * from the one with full UMSDOS semantic. + * from the one with full UMSDOS semantics. */ static struct file_operations umsdos_rdir_operations = { NULL, /* lseek - default */ - UMSDOS_dir_read, /* read */ + dummy_dir_read, /* read */ NULL, /* write - bad */ UMSDOS_rreaddir, /* readdir */ NULL, /* poll - default */ UMSDOS_ioctl_dir, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ + NULL, /* flush */ NULL, /* no special release code */ NULL /* fsync */ }; diff --git a/fs/umsdos/specs b/fs/umsdos/specs new file mode 100644 index 000000000..7b88a78e4 --- /dev/null +++ b/fs/umsdos/specs @@ -0,0 +1,287 @@ +/* #Specification: umsdos / readdir + * umsdos_readdir() should fill a struct dirent with + * an inode number. The cheap way to get it is to + * do a lookup in the MSDOS directory for each + * entry processed by the readdir() function. + * This is not very efficient, but very simple. The + * other way around is to maintain a copy of the inode + * number in the EMD file. This is a problem because + * this has to be maintained in sync using tricks. + * Remember that MSDOS (the OS) does not update the + * modification time (mtime) of a directory. There is + * no easy way to tell that a directory was modified + * during a DOS session and synchronise the EMD file. + */ + /* #Specification: readdir / . and .. + * The msdos filesystem manages the . and .. entry properly + * so the EMD file won't hold any info about it. + * + * In readdir, we assume that for the root directory + * the read position will be 0 for ".", 1 for "..". For + * a non root directory, the read position will be 0 for "." + * and 32 for "..". + */ + /* + * This is a trick used by the msdos file system (fs/msdos/dir.c) + * to manage . and .. for the root directory of a file system. + * Since there is no such entry in the root, fs/msdos/dir.c + * use the following: + * + * if f_pos == 0, return ".". + * if f_pos == 1, return "..". + * + * So let msdos handle it + * + * Since umsdos entries are much larger, we share the same f_pos. + * if f_pos is 0 or 1 or 32, we are clearly looking at . and + * .. + * + * As soon as we get f_pos == 2 or f_pos == 64, then back to + * 0, but this time we are reading the EMD file. + * + * Well, not so true. The problem, is that UMSDOS_REC_SIZE is + * also 64, so as soon as we read the first record in the + * EMD, we are back at offset 64. So we set the offset + * to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the + * .. entry from msdos. + * + * Now (linux 1.3), umsdos_readdir can read more than one + * entry even if we limit (umsdos_dir_once) to only one: + * It skips over hidden file. So we switch to + * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully + * the .. entry. + */ + /* #Specification: umsdos / lookup / inode info + * After successfully reading an inode from the MSDOS + * filesystem, we use the EMD file to complete it. + * We update the following field. + * + * uid, gid, atime, ctime, mtime, mode. + * + * We rely on MSDOS for mtime. If the file + * was modified during an MSDOS session, at least + * mtime will be meaningful. We do this only for regular + * file. + * + * We don't rely on MS-DOS for mtime for directories + * because the MS-DOS date on a directory is its + * creation time (strange MSDOS behavior) which + * corresponds to none of the three Unix time stamps. + */ + /* #Specification: umsdos / conversion mode + * The msdos filesystem can do some inline conversion + * of the data of a file. It can translate silently + * from the MS-DOS text file format to the Unix one + * (CRLF -> LF) while reading, and the reverse + * while writing. This is activated using the mount + * option conv=.... + * + * This is not useful for Linux files in a promoted + * directory. It can even be harmful. For this + * reason, the binary (no conversion) mode is + * always activated. + */ + /* #Specification: umsdos / conversion mode / todo + * A flag could be added to file and directories + * forcing an automatic conversion mode (as + * done with the msdos filesystem). + * + * This flag could be setup on a directory basis + * (instead of file) and all files in it would + * logically inherit it. If the conversion mode + * is active (conv=) then the i_binary flag would + * be left untouched in those directories. + * + * It was proposed that the sticky bit be used to set + * this. A problem with that is that new files would + * be written incorrectly. The other problem is that + * the sticky bit has a meaning for directories. So + * another bit should be used (there is some space + * in the EMD file for it) and a special utility + * would be used to assign the flag to a directory). + * I don't think it is useful to assign this flag + * on a single file. + */ + * #Specification: weakness / rename + * There is a case where UMSDOS rename has a different behavior + * than a normal Unix file system. Renaming an open file across + * directory boundary does not work. Renaming an open file within + * a directory does work, however. + * + * The problem may is in Linux VFS driver for msdos. + * I believe this is not a bug but a design feature, because + * an inode number represents some sort of directory address + * in the MSDOS directory structure, so moving the file into + * another directory does not preserve the inode number. + */ +/* #Specification: rename / new name exist + * If the destination name already exists, it will + * silently be removed. EXT2 does it this way + * and this is the spec of SunOS. So does UMSDOS. + * + * If the destination is an empty directory it will + * also be removed. + */ +/* #Specification: rename / new name exist / possible flaw + * The code to handle the deletion of the target (file + * and directory) use to be in umsdos_rename_f, surrounded + * by proper directory locking. This was ensuring that only + * one process could achieve a rename (modification) operation + * in the source and destination directory. This was also + * ensuring the operation was "atomic". + * + * This has been changed because this was creating a + * stack overflow (the stack is only 4 kB) in the kernel. To avoid + * the code doing the deletion of the target (if exist) has + * been moved to a upper layer. umsdos_rename_f is tried + * once and if it fails with EEXIST, the target is removed + * and umsdos_rename_f is done again. + * + * This makes the code cleaner and may solve a + * deadlock problem one tester was experiencing. + * + * The point is to mention that possibly, the semantic of + * "rename" may be wrong. Anyone dare to check that :-) + * Be aware that IF it is wrong, to produce the problem you + * will need two process trying to rename a file to the + * same target at the same time. Again, I am not sure it + * is a problem at all. + */ +/* #Specification: hard link / strategy + * Hard links are difficult to implement on top of an MS-DOS FAT file + * system. Unlike Unix file systems, there are no inodes. A directory + * entry holds the functionality of the inode and the entry. + * + * We will used the same strategy as a normal Unix file system + * (with inodes) except we will do it symbolically (using paths). + * + * Because anything can happen during a DOS session (defragment, + * directory sorting, etc.), we can't rely on an MS-DOS pseudo + * inode number to record the link. For this reason, the link + * will be done using hidden symbolic links. The following + * scenario illustrates how it works. + * + * Given a file /foo/file + * + * # + * ln /foo/file /tmp/file2 + * + * become internally + * + * mv /foo/file /foo/-LINK1 + * ln -s /foo/-LINK1 /foo/file + * ln -s /foo/-LINK1 /tmp/file2 + * # + * + * Using this strategy, we can operate on /foo/file or /foo/file2. + * We can remove one and keep the other, like a normal Unix hard link. + * We can rename /foo/file or /tmp/file2 independently. + * + * The entry -LINK1 will be hidden. It will hold a link count. + * When all link are erased, the hidden file is erased too. + */ +/* #Specification: weakness / hard link + * The strategy for hard link introduces a side effect that + * may or may not be acceptable. Here is the sequence + * + * # + * mkdir subdir1 + * touch subdir1/file + * mkdir subdir2 + * ln subdir1/file subdir2/file + * rm subdir1/file + * rmdir subdir1 + * rmdir: subdir1: Directory not empty + * # + * + * This happen because there is an invisible file (--link) in + * subdir1 which is referenced by subdir2/file. + * + * Any idea ? + */ +/* #Specification: weakness / hard link / rename directory + * Another weakness of hard link come from the fact that + * it is based on hidden symbolic links. Here is an example. + * + * # + * mkdir /subdir1 + * touch /subdir1/file + * mkdir /subdir2 + * ln /subdir1/file subdir2/file + * mv /subdir1 subdir3 + * ls -l /subdir2/file + * # + * + * Since /subdir2/file is a hidden symbolic link + * to /subdir1/..hlinkNNN, accessing it will fail since + * /subdir1 does not exist anymore (has been renamed). + */ +/* #Specification: hard link / directory + * A hard link can't be made on a directory. EPERM is returned + * in this case. + */ +/* #Specification: hard link / first hard link + * The first time a hard link is done on a file, this + * file must be renamed and hidden. Then an internal + * symbolic link must be done on the hidden file. + * + * The second link is done after on this hidden file. + * + * It is expected that the Linux MSDOS file system + * keeps the same pseudo inode when a rename operation + * is done on a file in the same directory. + */ +/* #Specification: function name / convention + * A simple convention for function names has been used in + * the UMSDOS filesystem. First, all functions use the prefix + * umsdos_ to avoid name clashes with other parts of the kernel. + * + * Standard VFS entry points use the prefix UMSDOS (upper case) + * so it's easier to tell them apart. + * N.B. (FIXME) PTW, the order and contents of this struct changed. + */ + +/* #Specification: mount / options + * Umsdos run on top of msdos. Currently, it supports no + * mount option, but happily pass all option received to + * the msdos driver. I am not sure if all msdos mount option + * make sense with Umsdos. Here are at least those who + * are useful. + * uid= + * gid= + * + * These options affect the operation of umsdos in directories + * which do not have an EMD file. They behave like normal + * msdos directory, with all limitation of msdos. + */ + +/* #Specification: pseudo root / mount + * When a umsdos fs is mounted, a special handling is done + * if it is the root partition. We check for the presence + * of the file /linux/etc/init or /linux/etc/rc or + * /linux/sbin/init. If one is there, we do a chroot("/linux"). + * + * We check both because (see init/main.c) the kernel + * try to exec init at different place and if it fails + * it tries /bin/sh /etc/rc. To be consistent with + * init/main.c, many more test would have to be done + * to locate init. Any complain ? + * + * The chroot is done manually in init/main.c but the + * info (the inode) is located at mount time and store + * in a global variable (pseudo_root) which is used at + * different place in the umsdos driver. There is no + * need to store this variable elsewhere because it + * will always be one, not one per mount. + * + * This feature allows the installation + * of a linux system within a DOS system in a subdirectory. + * + * A user may install its linux stuff in c:\linux + * avoiding any clash with existing DOS file and subdirectory. + * When linux boots, it hides this fact, showing a normal + * root directory with /etc /bin /tmp ... + * + * The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h + * in the macro UMSDOS_PSDROOT_NAME. + */ diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 32510a46b..dc639a7a4 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -19,9 +19,6 @@ #include <asm/uaccess.h> #include <asm/system.h> -#define PRINTK(x) -#define Printk(x) printk x - static struct file_operations umsdos_symlink_operations; @@ -31,10 +28,9 @@ static struct file_operations umsdos_symlink_operations; * */ -static int umsdos_readlink_x ( - struct dentry *dentry, - char *buffer, - int bufsiz) +int umsdos_readlink_x ( struct dentry *dentry, + char *buffer, + int bufsiz) { int ret; loff_t loffs = 0; @@ -42,7 +38,10 @@ static int umsdos_readlink_x ( ret = dentry->d_inode->i_size; - check_dentry (dentry); + if (!(dentry->d_inode)) { + return -EBADF; + } + fill_new_filp (&filp, dentry); filp.f_reada = 0; @@ -68,7 +67,7 @@ static int umsdos_readlink_x ( if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) { ret = -EIO; } -#if 0 /* DEBUG */ +#if 0 { struct umsdos_dirent *mydirent = buffer; @@ -89,9 +88,11 @@ static int UMSDOS_readlink (struct dentry *dentry, char *buffer, int buflen) int ret; PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name)); + ret = umsdos_readlink_x (dentry, buffer, buflen); PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen)); - /* dput(dentry); / * FIXME /mn/ */ + /* dput(dentry); / * FIXME /mn/? It seems it is unneeded. d_count is not changed by umsdos_readlink_x */ + Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret)); return ret; @@ -149,6 +150,7 @@ static struct file_operations umsdos_symlink_operations = NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open is needed */ + NULL, /* no flush code */ NULL, /* release */ NULL /* fsync */ }; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index be37113d5..fe1565106 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -100,22 +100,6 @@ static struct dentry_operations vfat_dentry_ops[4] = { } }; -static int strnicmp(const char *s1, const char *s2, int len) -{ - int n = 0; - while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) { - s1++; s2++; n++; - if (n == len) return 0; - } - if (*s1 == 0 && *s2 == 0) return 0; - if (*s1 && *s2) { - if (*s1 > *s2) return 1; - return -1; - } - if (*s1) return 1; - return -1; -} - void vfat_put_super(struct super_block *sb) { fat_put_super(sb); @@ -1771,8 +1755,6 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, MSDOS_I(new_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; MSDOS_I(new_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; - old_inode->i_nlink = 0; - fat_cache_inval_inode(old_inode); mark_inode_dirty(new_inode); @@ -1816,6 +1798,16 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, fat_brelse(sb, dotdot_bh); } + /* + * This convinces the VFS layer to drop the old inode, + * but at the same time fools the VFAT layer to not + * actually delete any of the blocks in the old file + * (because they are very much used by the renamed file) + */ + MSDOS_I(old_inode)->i_start = 0; + MSDOS_I(old_inode)->i_logstart = 0; + old_inode->i_nlink = 0; + if (res > 0) res = 0; if (res == 0) { |