diff options
Diffstat (limited to 'fs')
154 files changed, 5248 insertions, 4616 deletions
diff --git a/fs/Config.in b/fs/Config.in index 57d46b32a..6db2cf755 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -27,6 +27,9 @@ if [ "$CONFIG_INET" = "y" ]; then bool ' Root file system on NFS' CONFIG_ROOT_NFS fi tristate 'NFS server support' CONFIG_NFSD + if [ "$CONFIG_NFSD" != "n" ]; then + bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN + fi if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_bool CONFIG_SUNRPC y define_bool CONFIG_LOCKD y @@ -45,7 +48,7 @@ if [ "$CONFIG_INET" = "y" ]; then bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95 fi fi -if [ "$CONFIG_IPX" != "n" ]; then +if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS if [ "$CONFIG_NCP_FS" != "n" ]; then source fs/ncpfs/Config.in @@ -67,11 +70,9 @@ if [ "$CONFIG_AFFS_FS" != "n" ]; then define_bool CONFIG_AMIGA_PARTITION y fi tristate 'UFS filesystem support' CONFIG_UFS_FS -if [ "$CONFIG_UFS_FS" != "n" ]; then - bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL - bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL - bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION -fi +bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL +bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL +bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'SGI EFS filesystem support' CONFIG_EFS_FS fi diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 74d62f54e..22540a57b 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -242,8 +242,6 @@ affs_unlink(struct inode *dir, struct dentry *dentry) inode = dentry->d_inode; retval = -EPERM; - if (S_ISDIR(inode->i_mode)) - goto unlink_done; if (current->fsuid != inode->i_uid && current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) goto unlink_done; @@ -376,13 +374,11 @@ affs_rmdir(struct inode *dir, struct dentry *dentry) /* * Make sure the directory is empty and the dentry isn't busy. */ - if (dentry->d_count > 1) - shrink_dcache_parent(dentry); retval = -ENOTEMPTY; if (!empty_dir(bh,AFFS_I2HSIZE(inode))) goto rmdir_done; retval = -EBUSY; - if (dentry->d_count > 1) + if (!list_empty(&dentry->d_hash)) goto rmdir_done; if ((retval = affs_remove_header(bh,inode)) < 0) diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index 2bf4cd00a..c3da66d40 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -20,7 +20,7 @@ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) static int affs_readlink(struct dentry *, char *, int); -static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base); +static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int); struct inode_operations affs_symlink_inode_operations = { NULL, /* no file-operations */ @@ -98,7 +98,7 @@ affs_readlink(struct dentry *dentry, char *buffer, int buflen) } static struct dentry * -affs_follow_link(struct dentry *dentry, struct dentry *base) +affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) { struct inode *inode = dentry->d_inode; struct buffer_head *bh; @@ -150,7 +150,7 @@ affs_follow_link(struct dentry *dentry, struct dentry *base) } buffer[i] = '\0'; affs_brelse(bh); - base = lookup_dentry(buffer,base,1); + base = lookup_dentry(buffer,base,follow); kfree(buffer); return base; } @@ -5,12 +5,9 @@ * changes by Thomas Schoebel-Theuer */ -#include <linux/stat.h> #include <linux/sched.h> -#include <linux/kernel.h> #include <linux/mm.h> #include <linux/string.h> -#include <asm/system.h> /* Taken over from the old code... */ diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 56af7f9df..821cf44e0 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -2,7 +2,7 @@ * * linux/fs/autofs/autofs_i.h * - * Copyright 1997 Transmeta Corporation - All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -47,11 +47,13 @@ struct autofs_dir_ent { int hash; - struct autofs_dir_ent *next; - struct autofs_dir_ent **back; char *name; int len; ino_t ino; + struct dentry *dentry; + /* Linked list of entries */ + struct autofs_dir_ent *next; + struct autofs_dir_ent **back; /* The following entries are for the expiry system */ unsigned long last_usage; struct autofs_dir_ent *exp_next; @@ -64,9 +66,9 @@ struct autofs_dirhash { }; struct autofs_wait_queue { - unsigned long wait_queue_token; struct wait_queue *queue; struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; /* We use the following to see what we are waiting for */ int hash; int len; @@ -77,8 +79,8 @@ struct autofs_wait_queue { }; struct autofs_symlink { - int len; char *data; + int len; time_t mtime; }; @@ -123,7 +125,7 @@ void autofs_initialize_hash(struct autofs_dirhash *); struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); void autofs_hash_delete(struct autofs_dir_ent *); -struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *); +struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *); void autofs_hash_nuke(struct autofs_dirhash *); /* Expiration-handling functions */ @@ -144,7 +146,7 @@ struct super_block *autofs_read_super(struct super_block *, void *,int); /* Queue management functions */ int autofs_wait(struct autofs_sb_info *,struct qstr *); -int autofs_wait_release(struct autofs_sb_info *,unsigned long,int); +int autofs_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); void autofs_catatonic_mode(struct autofs_sb_info *); #ifdef DEBUG diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 67b2c04bf..63efe9b87 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/dir.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index bd9f33aab..2fd659471 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -59,13 +59,11 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, return ent; /* Symlinks are always expirable */ /* Get the dentry for the autofs subdirectory */ - dentry = lookup_dentry(ent->name, dget(sb->s_root), 0); + dentry = ent->dentry; - if ( IS_ERR(dentry) ) { - printk("autofs: no such dentry on expiry queue: %s\n", - ent->name); + if ( !dentry ) { + printk("autofs: dentry == NULL but inode range is directory, entry %s\n", ent->name); autofs_delete_usage(ent); - continue; } if ( !dentry->d_inode ) { @@ -79,24 +77,12 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, /* Make sure entry is mounted and unused; note that dentry will point to the mounted-on-top root. */ if ( !S_ISDIR(dentry->d_inode->i_mode) - || dentry->d_covers == dentry ) { - dput(dentry); + || dentry->d_mounts == dentry ) { DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name)); continue; } - /* - * Now, this is known to be a mount point; therefore the dentry - * will be held by the superblock. is_root_busy() will break if - * we hold a use count here, so we have to dput() it before calling - * is_root_busy(). However, since it is a mount point (already - * verified), dput() will be a nonblocking operation and the use - * count will not go to zero; therefore the call to is_root_busy() - * here is legal. - */ - dput(dentry); - - if ( !is_root_busy(dentry) ) { + if ( !is_root_busy(dentry->d_mounts) ) { DPRINTK(("autofs: signaling expire on %s\n", ent->name)); return ent; /* Expirable! */ } @@ -136,6 +122,8 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_say(ent->name,ent->len); autofs_init_usage(dh,ent); + if ( ent->dentry ) + ent->dentry->d_count++; dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; @@ -153,6 +141,8 @@ void autofs_hash_delete(struct autofs_dir_ent *ent) autofs_delete_usage(ent); + if ( ent->dentry ) + dput(ent->dentry); kfree(ent->name); kfree(ent); } @@ -161,8 +151,12 @@ void autofs_hash_delete(struct autofs_dir_ent *ent) * Used by readdir(). We must validate "ptr", so we can't simply make it * a pointer. Values below 0xffff are reserved; calling with any value * <= 0x10000 will return the first entry found. + * + * "last" can be NULL or the value returned by the last search *if* we + * want the next sequential entry. */ -struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr) +struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, + off_t *ptr, struct autofs_dir_ent *last) { int bucket, ecount, i; struct autofs_dir_ent *ent; @@ -176,19 +170,23 @@ struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t * DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount)); - ent = NULL; - - while ( bucket < AUTOFS_HASH_SIZE ) { - ent = dh->h[bucket]; - for ( i = ecount ; ent && i ; i-- ) - ent = ent->next; - - if (ent) { - ecount++; /* Point to *next* entry */ - break; + ent = last ? last->next : NULL; + + if ( ent ) { + ecount++; + } else { + while ( bucket < AUTOFS_HASH_SIZE ) { + ent = dh->h[bucket]; + for ( i = ecount ; ent && i ; i-- ) + ent = ent->next; + + if (ent) { + ecount++; /* Point to *next* entry */ + break; + } + + bucket++; ecount = 0; } - - bucket++; ecount = 0; } #ifdef DEBUG @@ -214,6 +212,8 @@ void autofs_hash_nuke(struct autofs_dirhash *dh) for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { for ( ent = dh->h[i] ; ent ; ent = nent ) { nent = ent->next; + if ( ent->dentry ) + dput(ent->dentry); kfree(ent->name); kfree(ent); } diff --git a/fs/autofs/init.c b/fs/autofs/init.c index 7c0508267..2afca0547 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/init.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index d17115208..8461645fb 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/inode.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 41694e8eb..a3d41fb33 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/root.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -66,7 +66,7 @@ struct inode_operations autofs_root_inode_operations = { static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct autofs_dir_ent *ent; + struct autofs_dir_ent *ent = NULL; struct autofs_dirhash *dirhash; struct inode * inode = filp->f_dentry->d_inode; off_t onr, nr; @@ -90,10 +90,12 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi filp->f_pos = ++nr; /* fall through */ default: - while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) { - if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0) - return 0; - filp->f_pos = nr; + while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent) ) { + if ( !ent->dentry || ent->dentry->d_mounts != ent->dentry ) { + if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0) + return 0; + filp->f_pos = nr; + } } break; } @@ -110,8 +112,9 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) { do { if ( status && dentry->d_inode ) { - printk("autofs warning: lookup failure on existing dentry, status = %d, name = %s\n", status, dentry->d_name.name); - break; + if ( status != -ENOENT ) + printk("autofs warning: lookup failure on positive dentry, status = %d, name = %s\n", status, dentry->d_name.name); + return 0; /* Try to get the kernel to invalidate this dentry */ } /* Turn this into a real negative dentry? */ @@ -148,8 +151,9 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str /* We don't update the usages for the autofs daemon itself, this is necessary for recursive autofs mounts */ - if ( !autofs_oz_mode(sbi) ) + if ( !autofs_oz_mode(sbi) ) { autofs_update_usage(&sbi->dirhash,ent); + } dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; return 1; @@ -193,7 +197,8 @@ static int autofs_revalidate(struct dentry * dentry) /* Update the usage list */ if ( !autofs_oz_mode(sbi) ) { ent = (struct autofs_dir_ent *) dentry->d_time; - autofs_update_usage(&sbi->dirhash,ent); + if ( ent ) + autofs_update_usage(&sbi->dirhash,ent); } return 1; } @@ -207,7 +212,6 @@ static struct dentry_operations autofs_dentry_operations = { static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) { struct autofs_sb_info *sbi; - struct inode *res; int oz_mode; DPRINTK(("autofs_root_lookup: name = ")); @@ -216,7 +220,6 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) if (!S_ISDIR(dir->i_mode)) return -ENOTDIR; - res = NULL; sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; oz_mode = autofs_oz_mode(sbi); @@ -249,6 +252,15 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) return -ERESTARTNOINTR; } + /* + * If this dentry is unhashed, then we shouldn't honour this + * lookup even if the dentry is positive. Returning ENOENT here + * doesn't do the right thing for all system calls, but it should + * be OK for the operations we permit from an autofs. + */ + if ( dentry->d_inode && list_empty(&dentry->d_hash) ) + return -ENOENT; + return 0; } @@ -304,6 +316,7 @@ static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const c ent->ino = AUTOFS_FIRST_SYMLINK + n; ent->hash = dentry->d_name.hash; memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); + ent->dentry = NULL; /* We don't keep the dentry for symlinks */ autofs_hash_insert(dh,ent); d_instantiate(dentry, iget(dir->i_sb,ent->ino)); @@ -339,7 +352,8 @@ static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) n = ent->ino - AUTOFS_FIRST_SYMLINK; if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) ) return -EINVAL; /* Not a symlink inode, can't unlink */ - + + dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); kfree(sbi->symlink[n].data); @@ -364,6 +378,11 @@ static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) return -ENOTDIR; /* Not a directory */ + if ( ent->dentry != dentry ) { + printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name); + } + + dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL; autofs_hash_delete(ent); dir->i_nlink--; d_drop(dentry); @@ -399,12 +418,14 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) return -ENOSPC; } + dir->i_nlink++; + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); + ent->hash = dentry->d_name.hash; memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); ent->ino = sbi->next_dir_ino++; + ent->dentry = dentry; autofs_hash_insert(dh,ent); - dir->i_nlink++; - d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; } @@ -482,9 +503,9 @@ static int autofs_root_ioctl(struct inode *inode, struct file *filp, switch(cmd) { case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ - return autofs_wait_release(sbi,arg,0); + return autofs_wait_release(sbi,(autofs_wqt_t)arg,0); case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ - return autofs_wait_release(sbi,arg,-ENOENT); + return autofs_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT); case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ autofs_catatonic_mode(sbi); return 0; diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index 5c5550f91..a4cb5154f 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/symlink.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -27,12 +27,13 @@ static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen) } static struct dentry * autofs_follow_link(struct dentry *dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct autofs_symlink *sl; sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip; - return lookup_dentry(sl->data, base, 1); + return lookup_dentry(sl->data, base, follow); } struct inode_operations autofs_symlink_inode_operations = { diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index 607b4314e..0a36930d9 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -2,7 +2,7 @@ * * linux/fs/autofs/waitq.c * - * Copyright 1997 Transmeta Corporation -- All Rights Reserved + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your @@ -18,7 +18,10 @@ /* We make this a static variable rather than a part of the superblock; it is better if we don't reassign numbers easily even across filesystems */ -static int autofs_next_wait_queue = 1; +static autofs_wqt_t autofs_next_wait_queue = 1; + +/* These are the signals we allow interrupting a pending mount */ +#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT)) void autofs_catatonic_mode(struct autofs_sb_info *sbi) { @@ -133,9 +136,25 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) } else wq->wait_ctr++; + /* wq->name is NULL if and only if the lock is already released */ + if ( wq->name ) { - /* wq->name is NULL if and only if the lock is released */ + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; + + spin_lock_irqsave(¤t->sigmask_lock, irqflags); + oldset = current->blocked; + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); + interruptible_sleep_on(&wq->queue); + + spin_lock_irqsave(¤t->sigmask_lock, irqflags); + current->blocked = oldset; + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); } else { DPRINTK(("autofs_wait: skipped sleeping\n")); } @@ -149,7 +168,7 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) } -int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status) +int autofs_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status) { struct autofs_wait_queue *wq, **wql; diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 6eee191e8..33560caa4 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -15,7 +15,7 @@ * so that a bad root inode can at least be unmounted. To do this * we must dput() the base and return the dentry with a dget(). */ -static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base) +static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base, unsigned int follow) { dput(base); return dget(dent); diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index f4d37d3c5..c7925b235 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -116,7 +116,7 @@ do_aout_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600); + dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); if (IS_ERR(dentry)) { dentry = NULL; goto end_coredump; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ec1891598..8be2fc475 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -279,7 +279,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, #ifdef __sparc__ } else { load_addr = get_unmapped_area(0, eppnt->p_filesz + - ELF_PAGEOFFSET(vaddr), 0); + ELF_PAGEOFFSET(vaddr)); #endif } @@ -375,6 +375,8 @@ static unsigned long load_aout_interp(struct exec * interp_ex, retval = read_exec(interpreter_dentry, offset, addr, text_data, 0); if (retval < 0) goto out; + flush_icache_range((unsigned long)addr, + (unsigned long)addr + text_data); do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1), interp_ex->a_bss, @@ -589,7 +591,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) current->mm->end_data = 0; current->mm->end_code = 0; current->mm->mmap = NULL; - current->flags &= ~PF_FORKNOEXEC; /* accounting flags */ + current->flags &= ~PF_FORKNOEXEC; elf_entry = (unsigned long) elf_ex.e_entry; /* Do this immediately, since STACK_TOP as used in setup_arg_pages @@ -606,7 +608,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) base, as well as whatever program they might try to exec. This is because the brk will follow the loader, and is not movable. */ - load_bias = (elf_ex.e_type == ET_DYN ? ELF_ET_DYN_BASE : 0); + load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0); /* Now we do a little grungy work by mmaping the ELF image into the correct location in memory. At this point, we assume that @@ -616,51 +618,50 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) old_fs = get_fs(); set_fs(get_ds()); for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { - if (elf_ppnt->p_type == PT_LOAD) { - int elf_prot = 0, elf_flags; - unsigned long vaddr; + int elf_prot = 0, elf_flags; + unsigned long vaddr; - if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; - if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + if (elf_ppnt->p_type != PT_LOAD) + continue; - elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE; + if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; + if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - vaddr = elf_ppnt->p_vaddr; - if (elf_ex.e_type == ET_EXEC || load_addr_set) { - elf_flags |= MAP_FIXED; - } + elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE; - error = do_mmap(file, - ELF_PAGESTART(load_bias + vaddr), - (elf_ppnt->p_filesz + - ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), - elf_prot, elf_flags, - (elf_ppnt->p_offset - - ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); - - if (!load_addr_set) { - load_addr_set = 1; - load_addr = (elf_ppnt->p_vaddr - - elf_ppnt->p_offset); - if (elf_ex.e_type == ET_DYN) { - load_bias = error - ELF_PAGESTART(load_bias + vaddr); - load_addr += error; - } + vaddr = elf_ppnt->p_vaddr; + if (elf_ex.e_type == ET_EXEC || load_addr_set) { + elf_flags |= MAP_FIXED; + } + + error = do_mmap(file, ELF_PAGESTART(load_bias + vaddr), + (elf_ppnt->p_filesz + + ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), + elf_prot, elf_flags, (elf_ppnt->p_offset - + ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); + + if (!load_addr_set) { + load_addr_set = 1; + load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset); + if (elf_ex.e_type == ET_DYN) { + load_bias += error - + ELF_PAGESTART(load_bias + vaddr); + load_addr += error; } - k = elf_ppnt->p_vaddr; - if (k < start_code) start_code = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if (k > elf_bss) - elf_bss = k; - if ((elf_ppnt->p_flags & PF_X) && end_code < k) - end_code = k; - if (end_data < k) - end_data = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if (k > elf_brk) - elf_brk = k; } + k = elf_ppnt->p_vaddr; + if (k < start_code) start_code = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; + if (k > elf_bss) + elf_bss = k; + if ((elf_ppnt->p_flags & PF_X) && end_code < k) + end_code = k; + if (end_data < k) + end_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; + if (k > elf_brk) + elf_brk = k; } set_fs(old_fs); fput(file); /* all done with the file */ @@ -1123,12 +1124,16 @@ static int elf_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600); + dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600); if (IS_ERR(dentry)) { dentry = NULL; goto end_coredump; } inode = dentry->d_inode; + + if(inode->i_nlink > 1) + goto end_coredump; /* multiple links - don't dump */ + if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) diff --git a/fs/block_dev.c b/fs/block_dev.c index 1598e3bf9..11b5d02d2 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -4,15 +4,11 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> +#include <linux/mm.h> #include <linux/locks.h> #include <linux/fcntl.h> -#include <linux/mm.h> #include <asm/uaccess.h> -#include <asm/system.h> extern int *blk_size[]; extern int *blksize_size[]; @@ -204,8 +200,11 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) blocks = rblocks; } - if (block + blocks > size) + if (block + blocks > size) { blocks = size - block; + if (blocks == 0) + return 0; + } /* We do this in a two stage process. We first try to request as many blocks as we can, then we wait for the first one to diff --git a/fs/buffer.c b/fs/buffer.c index 103caefa3..3ca874e37 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -24,18 +24,11 @@ * - RMK */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/string.h> +#include <linux/malloc.h> #include <linux/locks.h> #include <linux/errno.h> -#include <linux/malloc.h> -#include <linux/slab.h> -#include <linux/pagemap.h> #include <linux/swap.h> #include <linux/swapctl.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/vmalloc.h> #include <linux/blkdev.h> @@ -44,7 +37,6 @@ #include <linux/init.h> #include <linux/quotaops.h> -#include <asm/system.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/bitops.h> @@ -734,7 +726,7 @@ static void refill_freelist(int size) needed = bdf_prm.b_un.nrefill * size; while ((nr_free_pages > freepages.min*2) && - (buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.max_percent * num_physpages) && + !buffer_over_max() && grow_buffers(GFP_BUFFER, size)) { obtained += PAGE_SIZE; if (obtained >= needed) diff --git a/fs/coda/dir.c b/fs/coda/dir.c index e074ca346..dc4734d0b 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -520,7 +520,7 @@ int coda_rmdir(struct inode *dir, struct dentry *de) struct coda_inode_info *dircnp; const char *name = de->d_name.name; int len = de->d_name.len; - int error, rehash = 0; + int error; ENTRY; coda_vfs_stat.rmdir++; @@ -535,24 +535,13 @@ int coda_rmdir(struct inode *dir, struct dentry *de) if (len > CFS_MAXNAMLEN) return -ENAMETOOLONG; - error = -EBUSY; - if (de->d_count > 1) { - /* Attempt to shrink child dentries ... */ - shrink_dcache_parent(de); - if (de->d_count > 1) - return error; - } - /* Drop the dentry to force a new lookup */ - if (!list_empty(&de->d_hash)) { - d_drop(de); - rehash = 1; - } + if (!list_empty(&de->d_hash)) + return -EBUSY; /* update i_nlink and free the inode before unlinking; if rmdir fails a new lookup set i_nlink right.*/ if (de->d_inode->i_nlink) de->d_inode->i_nlink --; - d_delete(de); error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len); @@ -561,10 +550,6 @@ int coda_rmdir(struct inode *dir, struct dentry *de) return error; } - if (rehash) - d_add(de, NULL); - /* XXX how can mtime be set? */ - return 0; } diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index eb2fe150d..f065c0270 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -26,7 +26,7 @@ #include <linux/coda_proc.h> static int coda_readlink(struct dentry *de, char *buffer, int length); -static struct dentry *coda_follow_link(struct dentry *, struct dentry *); +static struct dentry *coda_follow_link(struct dentry *, struct dentry *, unsigned int); struct inode_operations coda_symlink_inode_operations = { NULL, /* no file-operations */ @@ -86,7 +86,8 @@ static int coda_readlink(struct dentry *de, char *buffer, int length) } static struct dentry *coda_follow_link(struct dentry *de, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct inode *inode = de->d_inode; int error; @@ -116,7 +117,7 @@ static struct dentry *coda_follow_link(struct dentry *de, memcpy(path, mem, len); path[len] = 0; - base = lookup_dentry(path, base, 1); + base = lookup_dentry(path, base, follow); kfree(path); return base; } diff --git a/fs/devpts/root.c b/fs/devpts/root.c index 04215ad40..f517367b9 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -134,7 +134,8 @@ static int devpts_revalidate(struct dentry * dentry) static int devpts_root_lookup(struct inode * dir, struct dentry * dentry) { struct devpts_sb_info *sbi = SBI(dir->i_sb); - int entry, i; + unsigned int entry; + int i; const char *p; if (!S_ISDIR(dir->i_mode)) @@ -154,17 +155,23 @@ static int devpts_root_lookup(struct inode * dir, struct dentry * dentry) entry = *p++ - '0'; for ( i = dentry->d_name.len-1 ; i ; i-- ) { - if ( *p < '0' || *p > '9' ) + unsigned int nentry = *p++ - '0'; + if ( nentry > 9 ) return 0; - entry *= 10; - entry += (*p++ - '0'); + nentry += entry * 10; + if (nentry < entry) + return 0; + entry = nentry; } } - + + if ( entry >= sbi->max_ptys ) + return 0; + dentry->d_inode = sbi->inodes[entry]; if ( dentry->d_inode ) dentry->d_inode->i_count++; - + d_add(dentry, dentry->d_inode); return 0; diff --git a/fs/dquot.c b/fs/dquot.c index 3179a5d4d..8b7d07295 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -18,6 +18,9 @@ * * Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96 * + * Revised list management to avoid races + * -- Bill Hawes, <whawes@star.net>, 9/98 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ @@ -50,12 +53,29 @@ static char *quotatypes[] = INITQFNAMES; static kmem_cache_t *dquot_cachep; -static struct dquot *dquot_hash[NR_DQHASH]; -static struct free_dquot_queue { - struct dquot *head; - struct dquot **last; -} free_dquots = { NULL, &free_dquots.head }; +/* + * Dquot List Management: + * The quota code uses three lists for dquot management: the inuse_list, + * free_dquots, and dquot_hash[] array. A single dquot structure may be + * on all three lists, depending on its current state. + * + * All dquots are placed on the inuse_list when first created, and this + * list is used for the sync and invalidate operations, which must look + * at every dquot. + * + * Unused dquots (dq_count == 0) are added to the free_dquots list when + * freed, and this list is searched whenever we need an available dquot. + * Dquots are removed from the list as soon as they are used again, and + * nr_free_dquots gives the number of dquots on the list. + * + * Dquots with a specific identity (device, type and id) are placed on + * one of the dquot_hash[] hash chains. The provides an efficient search + * mechanism to lcoate a specific dquot. + */ + static struct dquot *inuse_list = NULL; +LIST_HEAD(free_dquots); +static struct dquot *dquot_hash[NR_DQHASH]; static int dquot_updating[NR_DQHASH]; static struct dqstats dqstats; @@ -128,37 +148,29 @@ static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigne return dquot; } +/* Add a dquot to the head of the free list */ static inline void put_dquot_head(struct dquot *dquot) { - if ((dquot->dq_next = free_dquots.head) != NULL) - free_dquots.head->dq_pprev = &dquot->dq_next; - else - free_dquots.last = &dquot->dq_next; - free_dquots.head = dquot; - dquot->dq_pprev = &free_dquots.head; + list_add(&dquot->dq_free, &free_dquots); nr_free_dquots++; } +/* Add a dquot to the tail of the free list */ static inline void put_dquot_last(struct dquot *dquot) { - dquot->dq_next = NULL; - dquot->dq_pprev = free_dquots.last; - *free_dquots.last = dquot; - free_dquots.last = &dquot->dq_next; + list_add(&dquot->dq_free, free_dquots.prev); nr_free_dquots++; } static inline void remove_free_dquot(struct dquot *dquot) { - if (dquot->dq_pprev) { - if (dquot->dq_next) - dquot->dq_next->dq_pprev = dquot->dq_pprev; - else - free_dquots.last = dquot->dq_pprev; - *dquot->dq_pprev = dquot->dq_next; - dquot->dq_pprev = NULL; - nr_free_dquots--; + /* sanity check */ + if (list_empty(&dquot->dq_free)) { + printk("remove_free_dquot: dquot not on free list??\n"); } + list_del(&dquot->dq_free); + INIT_LIST_HEAD(&dquot->dq_free); + nr_free_dquots--; } static inline void put_inuse(struct dquot *dquot) @@ -169,6 +181,7 @@ static inline void put_inuse(struct dquot *dquot) dquot->dq_pprev = &inuse_list; } +#if 0 /* currently not needed */ static inline void remove_inuse(struct dquot *dquot) { if (dquot->dq_pprev) { @@ -178,6 +191,7 @@ static inline void remove_inuse(struct dquot *dquot) dquot->dq_pprev = NULL; } } +#endif static void __wait_on_dquot(struct dquot *dquot) { @@ -187,7 +201,6 @@ static void __wait_on_dquot(struct dquot *dquot) repeat: current->state = TASK_UNINTERRUPTIBLE; if (dquot->dq_flags & DQ_LOCKED) { - dquot->dq_flags |= DQ_WANT; schedule(); goto repeat; } @@ -210,24 +223,16 @@ static inline void lock_dquot(struct dquot *dquot) static inline void unlock_dquot(struct dquot *dquot) { dquot->dq_flags &= ~DQ_LOCKED; - if (dquot->dq_flags & DQ_WANT) { - dquot->dq_flags &= ~DQ_WANT; - wake_up(&dquot->dq_wait); - } + wake_up(&dquot->dq_wait); } static void write_dquot(struct dquot *dquot) { - short type; - struct file *filp; + short type = dquot->dq_type; + struct file *filp = dquot->dq_mnt->mnt_dquot.files[type]; mm_segment_t fs; loff_t offset; - - type = dquot->dq_type; - filp = dquot->dq_mnt->mnt_dquot.files[type]; - - if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL)) - return; + ssize_t ret; lock_dquot(dquot); down(&dquot->dq_mnt->mnt_dquot.semaphore); @@ -235,8 +240,18 @@ static void write_dquot(struct dquot *dquot) fs = get_fs(); set_fs(KERNEL_DS); - if (filp->f_op->write(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset) == sizeof(struct dqblk)) - dquot->dq_flags &= ~DQ_MOD; + /* + * Note: clear the DQ_MOD flag unconditionally, + * so we don't loop forever on failure. + */ + dquot->dq_flags &= ~DQ_MOD; + ret = 0; + if (filp) + ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, + sizeof(struct dqblk), &offset); + if (ret != sizeof(struct dqblk)) + printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", + kdevname(dquot->dq_dev)); up(&dquot->dq_mnt->mnt_dquot.semaphore); set_fs(fs); @@ -274,73 +289,86 @@ static void read_dquot(struct dquot *dquot) dqstats.reads++; } +/* + * Unhash and selectively clear the dquot structure, + * but preserve the use count, list pointers, and + * wait queue. + */ void clear_dquot(struct dquot *dquot) { - struct wait_queue *wait; - - /* So we don't disappear. */ - dquot->dq_count++; - - wait_on_dquot(dquot); - - if (--dquot->dq_count > 0) - remove_inuse(dquot); - else - remove_free_dquot(dquot); + /* unhash it first */ unhash_dquot(dquot); - wait = dquot->dq_wait; - memset(dquot, 0, sizeof(*dquot)); barrier(); - dquot->dq_wait = wait; - put_dquot_head(dquot); + dquot->dq_mnt = NULL; + dquot->dq_flags = 0; + dquot->dq_referenced = 0; + memset(&dquot->dq_dqb, 0, sizeof(struct dqblk)); } void invalidate_dquots(kdev_t dev, short type) { - struct dquot *dquot, *next = NULL; - int pass = 0; + struct dquot *dquot, *next = inuse_list; + int need_restart; - dquot = free_dquots.head; -repeat: - while (dquot) { +restart: + need_restart = 0; + while ((dquot = next) != NULL) { next = dquot->dq_next; - if (dquot->dq_dev != dev || dquot->dq_type != type) - goto next; + if (dquot->dq_dev != dev) + continue; + if (dquot->dq_type != type) + continue; + if (dquot->dq_flags & DQ_LOCKED) { + __wait_on_dquot(dquot); + + /* Set the flag for another pass. */ + need_restart = 1; + /* + * Make sure it's still the same dquot. + */ + if (dquot->dq_dev != dev) + continue; + if (dquot->dq_type != type) + continue; + } clear_dquot(dquot); - next: - dquot = next; - } - - if (pass == 0) { - dquot = inuse_list; - pass = 1; - goto repeat; } + /* + * If anything blocked, restart the operation + * to ensure we don't miss any dquots. + */ + if (need_restart) + goto restart; } int sync_dquots(kdev_t dev, short type) { - struct dquot *dquot, *next; - int pass = 0; + struct dquot *dquot, *next = inuse_list; + int need_restart; - dquot = free_dquots.head; -repeat: - while (dquot) { +restart: + need_restart = 0; + while ((dquot = next) != NULL) { next = dquot->dq_next; - if ((dev && dquot->dq_dev != dev) || - (type != -1 && dquot->dq_type != type)) - goto next; + if (dev && dquot->dq_dev != dev) + continue; + if (type != -1 && dquot->dq_type != type) + continue; + if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD))) + continue; + wait_on_dquot(dquot); if (dquot->dq_flags & DQ_MOD) write_dquot(dquot); - next: - dquot = next; + /* Set the flag for another pass. */ + need_restart = 1; } + /* + * If anything blocked, restart the operation + * to ensure we don't miss any dquots. + */ + if (need_restart) + goto restart; - if (pass == 0) { - dquot = inuse_list; - pass = 1; - goto repeat; - } dqstats.syncs++; return(0); } @@ -349,40 +377,41 @@ void dqput(struct dquot *dquot) { if (!dquot) return; + if (!dquot->dq_count) { + printk("VFS: dqput: trying to free free dquot\n"); + printk("VFS: device %s, dquot of %s %d\n", + kdevname(dquot->dq_dev), quotatypes[dquot->dq_type], + dquot->dq_id); + return; + } /* * If the dq_mnt pointer isn't initialized this entry needs no - * checking and doesn't need to be written. It just an empty + * checking and doesn't need to be written. It's just an empty * dquot that is put back on to the freelist. */ if (dquot->dq_mnt != (struct vfsmount *)NULL) { dqstats.drops++; - wait_on_dquot(dquot); - - if (!dquot->dq_count) { - printk("VFS: dqput: trying to free free dquot\n"); - printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev), - quotatypes[dquot->dq_type], dquot->dq_id); - return; - } we_slept: + wait_on_dquot(dquot); if (dquot->dq_count > 1) { dquot->dq_count--; return; - } else { - wake_up(&dquot_wait); - - if (dquot->dq_flags & DQ_MOD) { - write_dquot(dquot); - wait_on_dquot(dquot); - goto we_slept; - } + } + if (dquot->dq_flags & DQ_MOD) { + write_dquot(dquot); + goto we_slept; } } + /* sanity check */ + if (!list_empty(&dquot->dq_free)) { + printk("dqput: dquot already on free list??\n"); + } if (--dquot->dq_count == 0) { - remove_inuse(dquot); - put_dquot_last(dquot); /* Place at end of LRU free queue */ + /* Place at end of LRU free queue */ + put_dquot_last(dquot); + wake_up(&dquot_wait); } return; @@ -400,45 +429,43 @@ static void grow_dquots(void) nr_dquots++; memset((caddr_t)dquot, 0, sizeof(struct dquot)); + /* all dquots go on the inuse_list */ + put_inuse(dquot); put_dquot_head(dquot); cnt--; } } -static struct dquot *find_best_candidate_weighted(struct dquot *dquot) +static struct dquot *find_best_candidate_weighted(void) { - int limit, myscore; - unsigned long bestscore; - struct dquot *best = NULL; - - if (dquot) { - bestscore = 2147483647; - limit = nr_free_dquots >> 2; - do { - if (!((dquot->dq_flags & DQ_LOCKED) || (dquot->dq_flags & DQ_MOD))) { - myscore = dquot->dq_referenced; - if (myscore < bestscore) { - bestscore = myscore; - best = dquot; - } - } - dquot = dquot->dq_next; - } while (dquot && --limit); + struct list_head *tmp = &free_dquots; + struct dquot *dquot, *best = NULL; + unsigned long myscore, bestscore = ~0U; + int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32; + + while ((tmp = tmp->next) != &free_dquots && --limit) { + dquot = list_entry(tmp, struct dquot, dq_free); + if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) + continue; + myscore = dquot->dq_referenced; + if (myscore < bestscore) { + bestscore = myscore; + best = dquot; + } } return best; } -static inline struct dquot *find_best_free(struct dquot *dquot) +static inline struct dquot *find_best_free(void) { - int limit; - - if (dquot) { - limit = nr_free_dquots >> 5; - do { - if (dquot->dq_referenced == 0) - return dquot; - dquot = dquot->dq_next; - } while (dquot && --limit); + struct list_head *tmp = &free_dquots; + struct dquot *dquot; + int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32; + + while ((tmp = tmp->next) != &free_dquots && --limit) { + dquot = list_entry(tmp, struct dquot, dq_free); + if (dquot->dq_referenced == 0) + return dquot; } return NULL; } @@ -446,42 +473,56 @@ static inline struct dquot *find_best_free(struct dquot *dquot) struct dquot *get_empty_dquot(void) { struct dquot *dquot; + int count; repeat: - dquot = find_best_free(free_dquots.head); + dquot = find_best_free(); if (!dquot) goto pressure; got_it: - dquot->dq_count++; - wait_on_dquot(dquot); - unhash_dquot(dquot); - remove_free_dquot(dquot); + if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) { + wait_on_dquot(dquot); + if (dquot->dq_flags & DQ_MOD) + write_dquot(dquot); + /* + * The dquot may be back in use now, so we + * must recheck the free list. + */ + goto repeat; + } + /* sanity check ... */ + if (dquot->dq_count != 0) + printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count); - memset(dquot, 0, sizeof(*dquot)); + remove_free_dquot(dquot); dquot->dq_count = 1; - - put_inuse(dquot); + /* unhash and selectively clear the structure */ + clear_dquot(dquot); return dquot; + pressure: if (nr_dquots < max_dquots) { grow_dquots(); goto repeat; } - dquot = find_best_candidate_weighted(free_dquots.head); - if (!dquot) { - printk("VFS: No free dquots, contact mvw@planets.elm.net\n"); - sleep_on(&dquot_wait); - goto repeat; - } - if (dquot->dq_flags & DQ_LOCKED) { - wait_on_dquot(dquot); - goto repeat; - } else if (dquot->dq_flags & DQ_MOD) { - write_dquot(dquot); + dquot = find_best_candidate_weighted(); + if (dquot) + goto got_it; + /* + * Try pruning the dcache to free up some dquots ... + */ + count = select_dcache(128, 0); + if (count) { + printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count); + prune_dcache(count); + free_inode_memory(count); goto repeat; } - goto got_it; + + printk("VFS: No free dquots, contact mvw@planets.elm.net\n"); + sleep_on(&dquot_wait); + goto repeat; } struct dquot *dqget(kdev_t dev, unsigned int id, short type) @@ -507,12 +548,12 @@ we_slept: dquot->dq_type = type; dquot->dq_dev = dev; dquot->dq_mnt = vfsmnt; - read_dquot(dquot); + /* hash it first so it can be found */ hash_dquot(dquot); + read_dquot(dquot); } else { if (!dquot->dq_count++) { remove_free_dquot(dquot); - put_inuse(dquot); } else dqstats.cache_hits++; wait_on_dquot(dquot); @@ -546,6 +587,7 @@ static void add_dquot_ref(kdev_t dev, short type) inode = filp->f_dentry->d_inode; if (!inode) continue; + /* N.B. race problem -- filp could become unused */ if (filp->f_mode & FMODE_WRITE) { sb->dq_op->initialize(inode, type); inode->i_flags |= S_QUOTA; @@ -558,10 +600,16 @@ static void reset_dquot_ptrs(kdev_t dev, short type) struct super_block *sb = get_super(dev); struct file *filp; struct inode *inode; + struct dquot *dquot; + int cnt; if (!sb || !sb->dq_op) return; /* nothing to do */ +restart: + /* free any quota for unused dentries */ + shrink_dcache_sb(sb); + for (filp = inuse_filps; filp; filp = filp->f_next) { if (!filp->f_dentry) continue; @@ -570,10 +618,25 @@ static void reset_dquot_ptrs(kdev_t dev, short type) inode = filp->f_dentry->d_inode; if (!inode) continue; + /* + * Note: we restart after each blocking operation, + * as the inuse_filps list may have changed. + */ if (IS_QUOTAINIT(inode)) { - sb->dq_op->drop(inode); + dquot = inode->i_dquot[type]; inode->i_dquot[type] = NODQUOT; + /* any other quota in use? */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (inode->i_dquot[cnt] != NODQUOT) + goto put_it; + } inode->i_flags &= ~S_QUOTA; + put_it: + if (dquot != NODQUOT) { + dqput(dquot); + /* we may have blocked ... */ + goto restart; + } } } } @@ -638,7 +701,8 @@ static inline char ignore_hardlimit(struct dquot *dquot, uid_t initiator) return(initiator == 0 && dquot->dq_mnt->mnt_dquot.rsquash[dquot->dq_type] == 0); } -static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, struct tty_struct *tty) +static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, + struct tty_struct *tty) { if (inodes <= 0 || dquot->dq_flags & DQ_FAKE) return(QUOTA_OK); @@ -682,7 +746,8 @@ static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t return(QUOTA_OK); } -static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, struct tty_struct *tty, char warn) +static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, + struct tty_struct *tty, char warn) { if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) return(QUOTA_OK); @@ -732,15 +797,15 @@ static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initi */ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk) { - int error; struct dquot *dquot; + int error = -EFAULT; struct dqblk dq_dqblk; if (dqblk == (struct dqblk *)NULL) - return(-EFAULT); + return error; if (flags & QUOTA_SYSCALL) { - if ((error = copy_from_user((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk))) != 0) + if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk))) return(error); } else memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk)); @@ -793,26 +858,35 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk) { struct dquot *dquot; - int error; + int error = -ESRCH; - if (dev_has_quota_enabled(dev, type)) { - if (dqblk == (struct dqblk *)NULL) - return(-EFAULT); + if (!dev_has_quota_enabled(dev, type)) + goto out; + dquot = dqget(dev, id, type); + if (dquot == NODQUOT) + goto out; - if ((dquot = dqget(dev, id, type)) != NODQUOT) { - error = copy_to_user((caddr_t)dqblk, (caddr_t)&dquot->dq_dqb, sizeof(struct dqblk)); - dqput(dquot); - return(error); - } - } - return(-ESRCH); + error = -EFAULT; + if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk))) + error = 0; + dqput(dquot); +out: + return error; } static int get_stats(caddr_t addr) { + int error = -EFAULT; + struct dqstats stats; + dqstats.allocated_dquots = nr_dquots; dqstats.free_dquots = nr_free_dquots; - return(copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats))); + + /* make a copy, in case we page-fault in user space */ + memcpy(&stats, &dqstats, sizeof(struct dqstats)); + if (!copy_to_user(addr, &stats, sizeof(struct dqstats))) + error = 0; + return error; } static int quota_root_squash(kdev_t dev, short type, int *addr) @@ -823,11 +897,12 @@ static int quota_root_squash(kdev_t dev, short type, int *addr) if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL) return(-ENODEV); - if ((error = copy_from_user((caddr_t)&new_value, (caddr_t)addr, sizeof(int))) != 0) - return(error); - - vfsmnt->mnt_dquot.rsquash[type] = new_value; - return(0); + error = -EFAULT; + if (!copy_from_user(&new_value, addr, sizeof(int))) { + vfsmnt->mnt_dquot.rsquash[type] = new_value; + error = 0; + } + return error; } /* @@ -856,6 +931,8 @@ static u_long isize_to_blocks(size_t isize, size_t blksize) /* * Externally referenced functions through dquot_operations in inode. + * + * Note: this is a blocking operation. */ void dquot_initialize(struct inode *inode, short type) { @@ -894,6 +971,11 @@ void dquot_initialize(struct inode *inode, short type) } } +/* + * Release all quota for the specified inode. + * + * Note: this is a blocking operation. + */ void dquot_drop(struct inode *inode) { struct dquot *dquot; @@ -909,7 +991,11 @@ void dquot_drop(struct inode *inode) } } -int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, char warn) +/* + * Note: this is a blocking operation. + */ +int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, + char warn) { unsigned short cnt; struct tty_struct *tty = current->tty; @@ -930,6 +1016,9 @@ int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t ini return(QUOTA_OK); } +/* + * Note: this is a blocking operation. + */ int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t initiator) { unsigned short cnt; @@ -951,6 +1040,9 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t ini return(QUOTA_OK); } +/* + * Note: this is a blocking operation. + */ void dquot_free_block(const struct inode *inode, unsigned long number) { unsigned short cnt; @@ -962,6 +1054,9 @@ void dquot_free_block(const struct inode *inode, unsigned long number) } } +/* + * Note: this is a blocking operation. + */ void dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned short cnt; @@ -975,6 +1070,8 @@ void dquot_free_inode(const struct inode *inode, unsigned long number) /* * Transfer the number of inode and blocks from one diskquota to an other. + * + * Note: this is a blocking operation. */ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid_t initiator) { @@ -1029,8 +1126,8 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid } /* - * Finally perform the needed transfer from transfer_from to transfer_to. - * And release any pointer to dquots not needed anymore. + * Finally perform the needed transfer from transfer_from to transfer_to, + * and release any pointers to dquots not needed anymore. */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* @@ -1050,9 +1147,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid } if (inode->i_dquot[cnt] != NODQUOT) { - dqput(transfer_from[cnt]); - dqput(inode->i_dquot[cnt]); + struct dquot *temp = inode->i_dquot[cnt]; inode->i_dquot[cnt] = transfer_to[cnt]; + dqput(temp); + dqput(transfer_from[cnt]); } else { dqput(transfer_from[cnt]); dqput(transfer_to[cnt]); @@ -1082,8 +1180,8 @@ void __init dquot_init_hash(void) * Definitions of diskquota operations. */ struct dquot_operations dquot_operations = { - dquot_initialize, - dquot_drop, + dquot_initialize, /* mandatory */ + dquot_drop, /* mandatory */ dquot_alloc_block, dquot_alloc_inode, dquot_free_block, @@ -1121,30 +1219,47 @@ static inline void reset_enable_flags(struct vfsmount *vfsmnt, short type) int quota_off(kdev_t dev, short type) { struct vfsmount *vfsmnt; + struct file *filp; short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; - if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL || - is_enabled(vfsmnt, cnt) == 0 || - vfsmnt->mnt_sb == (struct super_block *)NULL) + vfsmnt = lookup_vfsmnt(dev); + if (!vfsmnt) + goto out; + if (!vfsmnt->mnt_sb) + goto out; + if (!is_enabled(vfsmnt, cnt)) continue; + reset_enable_flags(vfsmnt, cnt); - vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL; - + /* Note: these are blocking operations */ reset_dquot_ptrs(dev, cnt); invalidate_dquots(dev, cnt); - fput(vfsmnt->mnt_dquot.files[cnt]); - - reset_enable_flags(vfsmnt, cnt); + filp = vfsmnt->mnt_dquot.files[cnt]; vfsmnt->mnt_dquot.files[cnt] = (struct file *)NULL; vfsmnt->mnt_dquot.inode_expire[cnt] = 0; vfsmnt->mnt_dquot.block_expire[cnt] = 0; + fput(filp); + } + + /* + * Check whether any quota is still enabled, + * and if not clear the dq_op pointer. + */ + vfsmnt = lookup_vfsmnt(dev); + if (vfsmnt && vfsmnt->mnt_sb) { + int enabled = 0; + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + enabled |= is_enabled(vfsmnt, cnt); + if (!enabled) + vfsmnt->mnt_sb->dq_op = NULL; } +out: return(0); } @@ -1316,10 +1431,9 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) flags |= QUOTA_SYSCALL; + ret = -ESRCH; if (dev_has_quota_enabled(dev, type)) ret = set_dqblk(dev, id, type, flags, (struct dqblk *) addr); - else - ret = -ESRCH; out: unlock_kernel(); return ret; @@ -22,41 +22,25 @@ * formats. */ -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> +#include <linux/config.h> #include <linux/slab.h> #include <linux/file.h> #include <linux/mman.h> #include <linux/a.out.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/string.h> #include <linux/stat.h> #include <linux/fcntl.h> -#include <linux/ptrace.h> #include <linux/user.h> -#include <linux/binfmts.h> -#include <linux/personality.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/init.h> -#include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> -#include <linux/config.h> - #ifdef CONFIG_KMOD #include <linux/kmod.h> #endif -asmlinkage int sys_exit(int exit_code); -asmlinkage int sys_brk(unsigned long); - /* * Here are the actual binaries that will be accepted: * add more with "register_binfmt()" if using modules... @@ -285,18 +269,16 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page, p -= len; pos = p; while (len) { - char *pag = (char *) page[pos/PAGE_SIZE]; + char *pag; int offset, bytes_to_copy; offset = pos % PAGE_SIZE; - if(!pag) { - pag = (char *) page[pos/PAGE_SIZE] = get_user_page(pos); - if(!pag) { - if(from_kmem == 2) - set_fs(old_fs); - return 0; - } - clear_page(pag); + if (!(pag = (char *) page[pos/PAGE_SIZE]) && + !(pag = (char *) page[pos/PAGE_SIZE] = + (unsigned long *) get_free_page(GFP_USER))) { + if (from_kmem==2) + set_fs(old_fs); + return 0; } bytes_to_copy = PAGE_SIZE - offset; if (bytes_to_copy > len) @@ -450,9 +432,9 @@ fail_nomem: /* * This function makes sure the current process has its own signal table, - * so that flush_old_signals can later reset the signals without disturbing - * other processes. (Other processes might share the signal table via - * the CLONE_SIGHAND option to clone().) + * so that flush_signal_handlers can later reset the handlers without + * disturbing other processes. (Other processes might share the signal + * table via the CLONE_SIGHAND option to clone().) */ static inline int make_private_signals(void) @@ -491,12 +473,6 @@ static inline void release_old_signals(struct signal_struct * oldsig) * so that a new one can be started */ -static inline void flush_old_signals(struct task_struct *t) -{ - flush_signals(t); - flush_signal_handlers(t); -} - static inline void flush_old_files(struct files_struct * files) { unsigned long j; @@ -558,7 +534,7 @@ int flush_old_exec(struct linux_binprm * bprm) permission(bprm->dentry->d_inode,MAY_READ)) current->dumpable = 0; - flush_old_signals(current); + flush_signal_handlers(current); flush_old_files(current->files); return 0; @@ -587,7 +563,7 @@ static inline int must_not_trace_exec(struct task_struct * p) int prepare_binprm(struct linux_binprm *bprm) { int mode; - int retval,id_change; + int retval,id_change,cap_raised; struct inode * inode = bprm->dentry->d_inode; mode = inode->i_mode; @@ -607,7 +583,7 @@ int prepare_binprm(struct linux_binprm *bprm) bprm->e_uid = current->euid; bprm->e_gid = current->egid; - id_change = 0; + id_change = cap_raised = 0; /* Set-uid? */ if (mode & S_ISUID) { @@ -653,21 +629,22 @@ int prepare_binprm(struct linux_binprm *bprm) cap_set_full(bprm->cap_effective); } - /* We use a conservative definition of suid for capabilities. - * The process is suid if the permitted set is not a subset of - * the current permitted set after the exec call. - * new permitted set = forced | (allowed & inherited) - * pP' = fP | (fI & pI) - */ - - if ((bprm->cap_permitted.cap | - (current->cap_inheritable.cap & - bprm->cap_inheritable.cap)) & - ~current->cap_permitted.cap) { - id_change = 1; + /* Only if pP' is _not_ a subset of pP, do we consider there + * has been a capability related "change of capability". In + * such cases, we need to check that the elevation of + * privilege does not go against other system constraints. + * The new Permitted set is defined below -- see (***). */ + { + kernel_cap_t working = + cap_combine(bprm->cap_permitted, + cap_intersect(bprm->cap_inheritable, + current->cap_inheritable)); + if (!cap_issubset(working, current->cap_permitted)) { + cap_raised = 1; + } } - if (id_change) { + if (id_change || cap_raised) { /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */ @@ -676,10 +653,10 @@ int prepare_binprm(struct linux_binprm *bprm) || (atomic_read(¤t->fs->count) > 1) || (atomic_read(¤t->sig->count) > 1) || (atomic_read(¤t->files->count) > 1)) { - if (id_change && !capable(CAP_SETUID)) - return -EPERM; - if (!suser()) - return -EPERM; + if (id_change && !capable(CAP_SETUID)) + return -EPERM; + if (cap_raised && !capable(CAP_SETPCAP)) + return -EPERM; } } @@ -694,7 +671,7 @@ int prepare_binprm(struct linux_binprm *bprm) * The formula used for evolving capabilities is: * * pI' = pI - * pP' = fP | (fI & pI) + * (***) pP' = fP | (fI & pI) * pE' = pP' & fE [NB. fE is 0 or ~0] * * I=Inheritable, P=Permitted, E=Effective // p=process, f=file @@ -703,18 +680,25 @@ int prepare_binprm(struct linux_binprm *bprm) void compute_creds(struct linux_binprm *bprm) { - int new_permitted = bprm->cap_permitted.cap | - (bprm->cap_inheritable.cap & current->cap_inheritable.cap); - - current->cap_permitted.cap = new_permitted; - current->cap_effective.cap = new_permitted & bprm->cap_effective.cap; + int new_permitted = cap_t(bprm->cap_permitted) | + (cap_t(bprm->cap_inheritable) & + cap_t(current->cap_inheritable)); + + /* For init, we want to retain the capabilities set + * in the init_task struct. Thus we skip the usual + * capability rules */ + if (current->pid != 1) { + cap_t(current->cap_permitted) = new_permitted; + cap_t(current->cap_effective) = new_permitted & + cap_t(bprm->cap_effective); + } /* AUD: Audit candidate if current->cap_effective is set */ current->suid = current->euid = current->fsuid = bprm->e_uid; current->sgid = current->egid = current->fsgid = bprm->e_gid; if (current->euid != current->uid || current->egid != current->gid || - !cap_isclear(current->cap_permitted)) + !cap_issubset(new_permitted, current->cap_permitted)) current->dumpable = 0; } @@ -746,24 +730,28 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) /* handle /sbin/loader.. */ { struct exec * eh = (struct exec *) bprm->buf; + struct linux_binprm bprm_loader; if (!bprm->loader && eh->fh.f_magic == 0x183 && (eh->fh.f_flags & 0x3000) == 0x3000) { + int i; char * dynloader[] = { "/sbin/loader" }; struct dentry * dentry; dput(bprm->dentry); bprm->dentry = NULL; - remove_arg_zero(bprm); - bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2); - bprm->argc++; - bprm->loader = bprm->p; + + bprm_loader.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ + bprm_loader.page[i] = 0; + dentry = open_namei(dynloader[0], 0, 0); retval = PTR_ERR(dentry); if (IS_ERR(dentry)) return retval; bprm->dentry = dentry; + bprm->loader = bprm_loader.p; retval = prepare_binprm(bprm); if (retval<0) return retval; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 8f69f0baf..2c7ba02d7 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -86,11 +86,12 @@ static int read_block_bitmap (struct super_block * sb, { struct ext2_group_desc * gdp; struct buffer_head * bh = NULL; - int retval = 0; + int retval = -EIO; gdp = ext2_get_group_desc (sb, block_group, NULL); if (!gdp) goto error_out; + retval = 0; bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize); if (!bh) { ext2_error (sb, "read_block_bitmap", @@ -686,6 +687,12 @@ static int test_root(int a, int b) } } +int ext2_group_sparse(int group) +{ + return (test_root(group, 3) || test_root(group, 5) || + test_root(group, 7)); +} + void ext2_check_blocks_bitmap (struct super_block * sb) { struct buffer_head * bh; @@ -714,9 +721,9 @@ void ext2_check_blocks_bitmap (struct super_block * sb) bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; - if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) & + if (!(sb->u.ext2_sb.s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) || - (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) { + ext2_group_sparse(i)) { if (!ext2_test_bit (0, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Superblock in group %d " diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index c5af9d8e1..22af55334 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -70,7 +70,7 @@ struct inode_operations ext2_dir_inode_operations = { NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ - ext2_truncate, /* truncate */ + NULL, /* truncate */ ext2_permission, /* permission */ NULL /* smap */ }; diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 8b310fc20..7a7629e58 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -179,9 +179,9 @@ static int load_inode_bitmap (struct super_block * sb, */ void ext2_free_inode (struct inode * inode) { + struct super_block * sb = inode->i_sb; int is_directory; unsigned long ino; - struct super_block * sb; struct buffer_head * bh; struct buffer_head * bh2; unsigned long block_group; @@ -190,8 +190,6 @@ void ext2_free_inode (struct inode * inode) struct ext2_group_desc * gdp; struct ext2_super_block * es; - if (!inode) - return; if (!inode->i_dev) { printk ("ext2_free_inode: inode has no device\n"); return; @@ -205,7 +203,7 @@ void ext2_free_inode (struct inode * inode) (int) inode->i_nlink); return; } - if (!inode->i_sb) { + if (!sb) { printk("ext2_free_inode: inode on nonexistent device\n"); return; } @@ -213,15 +211,21 @@ void ext2_free_inode (struct inode * inode) ino = inode->i_ino; ext2_debug ("freeing inode %lu\n", ino); - sb = inode->i_sb; + /* + * Note: we must free any quota before locking the superblock, + * as writing the quota to disk may need the lock as well. + */ + DQUOT_FREE_INODE(sb, inode); + DQUOT_DROP(inode); + lock_super (sb); - if (ino < EXT2_FIRST_INO(sb) || - ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { + es = sb->u.ext2_sb.s_es; + if (ino < EXT2_FIRST_INO(sb) || + ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "free_inode", "reserved inode or nonexistent inode"); goto error_return; } - es = sb->u.ext2_sb.s_es; block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); @@ -233,7 +237,6 @@ void ext2_free_inode (struct inode * inode) is_directory = S_ISDIR(inode->i_mode); /* Do this BEFORE marking the inode not in use */ - DQUOT_FREE_INODE(sb, inode); clear_inode (inode); /* Ok, now we can actually update the inode bitmaps.. */ @@ -315,7 +318,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err) sb = dir->i_sb; inode->i_sb = sb; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; lock_super (sb); es = sb->u.ext2_sb.s_es; repeat: diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index c235de7d7..cfea342a3 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -257,7 +257,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, ext2_debug ("creating next block\n"); de = (struct ext2_dir_entry_2 *) bh->b_data; - de->inode = le32_to_cpu(0); + de->inode = 0; de->rec_len = le16_to_cpu(sb->s_blocksize); dir->i_size = offset + sb->s_blocksize; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; @@ -291,7 +291,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); de = de1; } - de->inode = cpu_to_le32(0); + de->inode = 0; de->name_len = namelen; de->file_type = 0; memcpy (de->name, name, namelen); @@ -344,7 +344,8 @@ static int ext2_delete_entry (struct ext2_dir_entry_2 * dir, pde->rec_len = cpu_to_le16(le16_to_cpu(pde->rec_len) + le16_to_cpu(dir->rec_len)); - dir->inode = le32_to_cpu(0); + else + dir->inode = 0; return 0; } i += le16_to_cpu(de->rec_len); @@ -660,32 +661,14 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) if (le32_to_cpu(de->inode) != inode->i_ino) goto end_rmdir; - down(&inode->i_sem); - /* - * Prune any child dentries so that this dentry becomes negative. - */ - if (dentry->d_count > 1) - shrink_dcache_parent(dentry); - if (!empty_dir (inode)) retval = -ENOTEMPTY; else if (le32_to_cpu(de->inode) != inode->i_ino) retval = -ENOENT; else { - if (dentry->d_count > 1) { - /* - * Are we deleting the last instance of a busy directory? - * Better clean up if so. - * - * Make directory empty (it will be truncated when finally - * dereferenced). This also inhibits ext2_add_entry. - */ - inode->i_size = 0; - } retval = ext2_delete_entry (de, bh); dir->i_version = ++event; } - up(&inode->i_sem); if (retval) goto end_rmdir; mark_buffer_dirty(bh, 1); @@ -699,6 +682,7 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) (int) inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; + inode->i_size = 0; mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -732,8 +716,6 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) DQUOT_INIT(inode); retval = -EPERM; - if (S_ISDIR(inode->i_mode)) - goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; if ((dir->i_mode & S_ISVTX) && diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fa84e498f..674a5043c 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -764,8 +764,8 @@ void cleanup_module(void) int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { unsigned long overhead; - unsigned long overhead_per_group; struct statfs tmp; + int ngroups, i; if (test_opt (sb, MINIX_DF)) overhead = 0; @@ -773,13 +773,35 @@ int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) /* * Compute the overhead (FS structures) */ - overhead_per_group = 1 /* super block */ + - sb->u.ext2_sb.s_db_per_group /* descriptors */ + - 1 /* block bitmap */ + - 1 /* inode bitmap */ + - sb->u.ext2_sb.s_itb_per_group /* inode table */; - overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block) + - sb->u.ext2_sb.s_groups_count * overhead_per_group; + + /* + * All of the blocks before first_data_block are + * overhead + */ + overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block); + + /* + * Add the overhead attributed to the superblock and + * block group descriptors. If this is sparse + * superblocks is turned on, then not all groups have + * this. + */ + if (sb->u.ext2_sb.s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { + ngroups = 0; + for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++) + if (ext2_group_sparse(i)) + ngroups++; + } else + ngroups = sb->u.ext2_sb.s_groups_count; + overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group); + + /* + * Every block group has an inode bitmap, a block + * bitmap, and an inode table. + */ + overhead += (sb->u.ext2_sb.s_groups_count * + (2 + sb->u.ext2_sb.s_itb_per_group)); } tmp.f_type = EXT2_SUPER_MAGIC; diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 2996a5f33..f01224b68 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -25,7 +25,7 @@ #include <linux/stat.h> static int ext2_readlink (struct dentry *, char *, int); -static struct dentry *ext2_follow_link(struct dentry *, struct dentry *); +static struct dentry *ext2_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -52,7 +52,8 @@ struct inode_operations ext2_symlink_inode_operations = { }; static struct dentry * ext2_follow_link(struct dentry * dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct inode *inode = dentry->d_inode; struct buffer_head * bh = NULL; @@ -68,7 +69,7 @@ static struct dentry * ext2_follow_link(struct dentry * dentry, link = bh->b_data; } UPDATE_ATIME(inode); - base = lookup_dentry(link, base, 1); + base = lookup_dentry(link, base, follow); if (bh) brelse(bh); return base; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index a90329f7c..b393fd28a 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -62,8 +62,6 @@ static int ext2_secrm_seed = 152; /* Random generator base */ #define TINDIRECT_BLOCK(inode,offset) \ (INDIRECT_BLOCK(inode,offset) / (addr_per_block*addr_per_block)) -static u32 le32_zero = 0; /* egcs doesn like cpu_to_le32(0); */ - /* * Truncate has the most races in the whole filesystem: coding it is * a pain in the a**. Especially as I don't do any locking... @@ -128,18 +126,16 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh, } while (retry); for (i = 0; i < addr_per_block; i++) - if (le32_to_cpu(*(ind++))) + if (*(ind++)) goto in_use; if (bh->b_count == 1) { int tmp; - if (ind_bh) { + if (ind_bh) tmp = le32_to_cpu(*p); - *p = le32_zero; - } else { + else tmp = *p; - *p = 0; - } + *p = 0; inode->i_blocks -= (inode->i_sb->s_blocksize / 512); mark_inode_dirty(inode); /* @@ -232,13 +228,11 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, ext2_error(inode->i_sb, "trunc_indirect", "Read failure, inode=%ld, block=%d", inode->i_ino, tmp); - if (dind_bh) { - *p = le32_zero; + *p = 0; + if (dind_bh) mark_buffer_dirty(dind_bh, 1); - } else { - *p = 0; + else mark_inode_dirty(inode); - } return 0; } @@ -268,7 +262,7 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, } } - *ind = le32_zero; + *ind = 0; inode->i_blocks -= blocks; mark_inode_dirty(inode); bforget(bh); @@ -316,13 +310,11 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p, ext2_error(inode->i_sb, "trunc_dindirect", "Read failure, inode=%ld, block=%d", inode->i_ino, tmp); - if (tind_bh) { - *p = le32_zero; + *p = 0; + if (tind_bh) mark_buffer_dirty(tind_bh, 1); - } else { - *p = 0; + else mark_inode_dirty(inode); - } return 0; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 25194e9a3..e123a9f16 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -221,13 +221,13 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug, if (value) ret = 0; else opts->sys_immutable = 1; } - else if (!strcmp(this_char,"codepage")) { + else if (!strcmp(this_char,"codepage") && value) { opts->codepage = simple_strtoul(value,&value,0); if (*value) ret = 0; else printk ("MSDOS FS: Using codepage %d\n", opts->codepage); } - else if (!strcmp(this_char,"iocharset")) { + else if (!strcmp(this_char,"iocharset") && value) { p = value; while (*value && *value != ',') value++; len = value - p; @@ -624,7 +624,8 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { printk("dev = %s, ino = %ld\n", kdevname(inode->i_dev), inode->i_ino); - panic("fat_read_inode: unable to read i-node block"); + fat_fs_panic(sb, "fat_read_inode: unable to read i-node block"); + return; } raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [inode->i_ino & (MSDOS_DPB-1)]; @@ -713,7 +714,8 @@ void fat_write_inode(struct inode *inode) if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { printk("dev = %s, ino = %ld\n", kdevname(inode->i_dev), inode->i_ino); - panic("msdos_write_inode: unable to read i-node block"); + fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block"); + return; } raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [inode->i_ino & (MSDOS_DPB-1)]; diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index 448437ede..4cda79196 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -58,6 +58,7 @@ static unsigned long fat_file_mmap_nopage( } filp.f_reada = 0; filp.f_pos = pos; + filp.f_dentry=area->vm_file->f_dentry; need_read = PAGE_SIZE - clear; { mm_segment_t cur_fs = get_fs(); diff --git a/fs/fcntl.c b/fs/fcntl.c index 919204088..036c9eb1f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -4,18 +4,10 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/stat.h> -#include <linux/fcntl.h> -#include <linux/file.h> -#include <linux/string.h> #include <linux/mm.h> -#include <linux/smp.h> +#include <linux/file.h> #include <linux/smp_lock.h> -#include <asm/bitops.h> #include <asm/uaccess.h> extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); @@ -97,7 +89,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg) /* Did FASYNC state change? */ if ((arg ^ filp->f_flags) & FASYNC) { - if (filp->f_op->fasync) + if (filp->f_op && filp->f_op->fasync) filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); } @@ -4,10 +4,6 @@ * written by Paul H. Hargrove */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/fcntl.h> #include <linux/mm.h> static int fifo_open(struct inode * inode,struct file * filp) diff --git a/fs/file_table.c b/fs/file_table.c index 6a4b1df43..200b6049b 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -5,13 +5,8 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/fs.h> -#include <linux/file.h> -#include <linux/string.h> -#include <linux/mm.h> #include <linux/slab.h> +#include <linux/file.h> #include <linux/init.h> /* SLAB cache for filp's. */ diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog index 423f86e67..0bd14f24c 100644 --- a/fs/hfs/ChangeLog +++ b/fs/hfs/ChangeLog @@ -1,3 +1,85 @@ +1998-11-21 a sun <asun@hecate.darksunrising.blah> + + * hfs_sysdep.h, hfs_fs.h: added hfs_from_utc/to_utc to deal with + date differences on hfs formatted media. + + NOTE: hfs extended keeps everything in utc, so we'll need to deal + with that when appropriate. + +1998-11-12 a sun <asun@hecate.darksunrising.blah> + + * extent.c (shrink_fork): added some lock_bitmap/unlock_bitmap's + to protect hfs_clear_vbm_bits. we should no longer have problems + with free_ablocks wrapping around. + +1998-11-02 a sun <asun@hecate.darksunrising.blah> + + * mdb.c (hfs_mdb_get): plugged up an mdb failed initialization + leak. + +1998-10-31 a sun <asun@hecate.darksunrising.blah> + + * version.c (hfs_version): bumped to version 0.96. + + * mdb.c (hfs_mdb_commit): you only write out the alternate MDB + when the catalog or extents overflow files grow. that just leaves + the btree corruption problems. bleah (whilst deleting a bunch of + files, more of the btree can get pruned away than desired). + +1998-10-30 a sun <asun@hecate.darksunrising.blah> + + * dir.c: fixed a bunch of silliness with deletions. make sure to + zero out stuff and set mark_inode_dirty(). + +1998-10-29 a sun <asun@hecate.darksunrising.blah> + + * string.c (hfs_strcmp, hfs_streq, hfs_strhash): converted them to + take name/len arguments instead of hfs_name to reduce copying. + + * dir.c, dir_nat.c, dir_cap.c, dir_dbl.c, sysdep.c: modified + relevant areas to reflect string.c changes. + +1998-10-28 a sun <asun@hecate.darksunrising.blah> + + * hfs.h (hfs_lookup_dentry): oh my. more silliness. make sure to + have the d_lookup use the same hash value as the one generated by + hfs_hash_dentry. i also changed the argument order. + (hfs_drop_special): change the argument order to be more in line + with what the dcache stuff looks like. + + * sysdep.c (hfs_compare_dentry): the compare was returning the + wrong value for correct matches and causing all sorts of + mischief. this fixes both directory counts and mounting on top of + hfs volumes. + + * file.c, file_cap.c, file_hdr.c: added mark_inode_dirty()'s in + the relevant places. + +1998-10-11 root <asun@hecate.darksunrising.blah> + + * mdb.c (hfs_mdb_get): moved initialization of mdb->entry_dirty + list to here to deal with trying to read a bad hfs volume. + +1998-10-10 a sun <asun@zoology.washington.edu> + + * inode.c, catalog.c, dir_*.c, sysdep.c: parts of the dcache + conversion didn't get done properly. specifically, i forgot to + move the hfs_cat_puts into the right place. that's fixed now. + +1998-09-11 a sun <asun@purgatorius.zoology.washington.edu> + + * mdb.c: altered mdb struct to reflect hfs plus usage. + +1998-08-27 a sun <asun@purgatorius.zoology.washington.edu> + + * file.c, file_hdr.c, file_cap.c: dealt with the remaining + copy_to/from_user() error cases. + +1998-08-26 a sun <asun@purgatorius.zoology.washington.edu> + + * super.c (hfs_read_super): fixed to deal with cdroms. why doesn't + the cdrom layer call the partition table code? + Wed Jan 21 14:04:26 1998 a sun <asun@zoology.washington.edu> * inode.c, sysdep.c @@ -109,7 +191,7 @@ Sun Dec 14 01:12:58 1997 a sun <asun@zoology.washington.edu> * dir.c changed mark_inodes_deleted to dget/d_delete/dput the dentry instead of just dropping it. the bytes available should now - be updated updated properly upon deletion. + be updated properly upon deletion. Wed Dec 10 00:01:25 1997 a sun <asun@zoology.washington.edu> @@ -615,7 +697,7 @@ Tue Sep 10 12:05:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu> * file.c, file_cap.c, hfs_fs.h: Rename fix_perms() to hfs_file_fix_mode() and - move it from from file_cap.c to file.c. + move it from file_cap.c to file.c. * README.sgml, super.c: Make the default for the names mount option vary with the fork option. diff --git a/fs/hfs/HFS.txt b/fs/hfs/HFS.txt index 407d04174..5c1ae5fcc 100644 --- a/fs/hfs/HFS.txt +++ b/fs/hfs/HFS.txt @@ -273,7 +273,7 @@ header files. Their format is described briefly in the section ``A Guide to Special File Formats'' in this document. They are documented in detail in ``AppleSingle/AppleDouble Formats: - Developer's Note (9/94)'', available from from Apple's Developer + Developer's Note (9/94)'', available from Apple's Developer Services Page <http://devworld.apple.com>. Note that the naming convention for the header file can cause diff --git a/fs/hfs/TODO b/fs/hfs/TODO index f989c3a91..fbbc6e16c 100644 --- a/fs/hfs/TODO +++ b/fs/hfs/TODO @@ -10,10 +10,8 @@ Genuine bugs: 1. Header files have compiled-in limit (currently 10) on descriptors. Missing features: -1. The partition code should be migrated into the kernel to allow - simultaneous access to multiple partitions on a single disk. -2. 1k block support is needed for some devices. -3. An ioctl()-based interface is needed to provide a consistent way +1. 1k block support is needed for some devices. +2. An ioctl()-based interface is needed to provide a consistent way to do things under all of the representations of forked files. Possible additional "fork" mount options: diff --git a/fs/hfs/binsert.c b/fs/hfs/binsert.c index daab8c22d..0c0c5076a 100644 --- a/fs/hfs/binsert.c +++ b/fs/hfs/binsert.c @@ -17,6 +17,20 @@ /*================ File-local functions ================*/ +/* btree locking functions */ +static inline void hfs_btree_lock(struct hfs_btree *tree) +{ + while (tree->lock) + hfs_sleep_on(&tree->wait); + tree->lock = 1; +} + +static inline void hfs_btree_unlock(struct hfs_btree *tree) +{ + tree->lock = 0; + hfs_wake_up(&tree->wait); +} + /* * binsert_nonfull() * @@ -512,15 +526,11 @@ restart: /* make certain we have enough nodes to proceed */ if ((tree->bthFree - tree->reserved) < reserve) { hfs_brec_relse(&brec, NULL); - while (tree->lock) { - hfs_sleep_on(&tree->wait); - } - tree->lock = 1; + hfs_btree_lock(tree); if ((tree->bthFree - tree->reserved) < reserve) { hfs_btree_extend(tree); } - tree->lock = 0; - hfs_wake_up(&tree->wait); + hfs_btree_unlock(tree); if ((tree->bthFree - tree->reserved) < reserve) { return -ENOSPC; } else { diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 762278667..0b4b669a4 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -510,6 +510,8 @@ return_it: } else { hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count, bn->buf->b_count, bn->ndNHeight, bnode_count); + hfs_warn("hfs_bnode_find: blnk %u flnk %u recs %u\n", + bn->ndBLink, bn->ndFLink, bn->ndNRecs); } #endif diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 5aa735388..900bddf33 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -176,6 +176,14 @@ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid, bt->wait = NULL; bt->dirt = 0; memset(bt->cache, 0, sizeof(bt->cache)); + +#if 0 /* this is a fake entry. so we don't need to initialize it. */ + memset(&bt->entry, 0, sizeof(bt->entry)); + hfs_init_waitqueue(&bt->entry.wait); + INIT_LIST_HEAD(&bt->entry.hash); + INIT_LIST_HEAD(&bt->entry.list); +#endif + bt->entry.mdb = mdb; bt->entry.cnid = cnid; bt->entry.type = HFS_CDR_FIL; diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 2435ceb31..306c018e7 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -127,17 +127,15 @@ static inline hfs_u32 brec_to_id(struct hfs_brec *brec) * * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer. */ -static inline unsigned long hashfn(const struct hfs_mdb *mdb, +static inline unsigned int hashfn(const struct hfs_mdb *mdb, const struct hfs_cat_key *key) { -#define LSB(X) (((unsigned char *)(&X))[3]) - unsigned long hash; + unsigned int hash; - hash = (unsigned long) mdb | (unsigned long) key->ParID[3] | - hfs_strhash(&key->CName); + hash = (unsigned long) mdb | (unsigned long) key->ParID[3] | + hfs_strhash(key->CName.Name, key->CName.Len); hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2); return hash & C_HASHMASK; -#undef LSB } /* @@ -533,7 +531,6 @@ add_new_entry: HFS_BKEY(key), HFS_BFIND_READ_EQ)) { /* uh oh. we failed to read the record. * the entry doesn't actually exist. */ - entry->state |= HFS_DELETED; goto read_fail; } @@ -564,12 +561,14 @@ add_new_entry: return NULL; read_fail: - /* spinlock unlocked already. we don't need to mark the entry - * dirty here because we know that it doesn't exist. */ - remove_hash(entry); - entry->state &= ~HFS_LOCK; - hfs_wake_up(&entry->wait); - hfs_cat_put(entry); + /* short-cut hfs_cat_put by doing everything here. */ + spin_lock(&entry_lock); + list_del(&entry->hash); + list_del(&entry->list); + init_entry(entry); + list_add(&entry->list, &entry_unused); + entries_stat.nr_free_entries++; + spin_unlock(&entry_lock); return NULL; } @@ -586,6 +585,11 @@ static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb, { struct hfs_cat_entry * entry; +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n", + mdb, key->CName.Name, read); +#endif + spin_lock(&entry_lock); entry = find_entry(mdb, key); if (!entry) { @@ -759,6 +763,7 @@ static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key, /* complete the cache entry and return success */ __read_entry(entry, record); unlock_entry(entry); + if (result) { *result = entry; } else { @@ -786,17 +791,7 @@ done: * * Release an entry we aren't using anymore. * - * NOTE: We must be careful any time we sleep on a non-deleted - * entry that the entry is in a consistent state, since another - * process may get the entry while we sleep. That is why we - * 'goto repeat' after each operation that might sleep. - * - * ADDITIONAL NOTE: the sys_entries will remove themselves from - * the sys_entry list on the final iput, so we don't need - * to worry about them here. - * - * nothing in hfs_cat_put goes to sleep now except - * on the initial entry. + * nothing in hfs_cat_put goes to sleep now except on the initial entry. */ void hfs_cat_put(struct hfs_cat_entry * entry) { @@ -810,9 +805,13 @@ void hfs_cat_put(struct hfs_cat_entry * entry) return; } +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n", + entry, entry->count, entry->type, entry->state); +#endif spin_lock(&entry_lock); if (!--entry->count) { - if ((entry->state & HFS_DELETED)) + if ((entry->state & HFS_DELETED)) goto entry_deleted; if ((entry->type == HFS_CDR_FIL)) { @@ -840,10 +839,7 @@ entry_deleted: /* deleted entries have already been removed HFS_DELETE(entry); entries_stat.nr_entries--; } else { - spin_unlock(&entry_lock); - wait_on_entry(entry); init_entry(entry); - spin_lock(&entry_lock); list_add(&entry->list, &entry_unused); entries_stat.nr_free_entries++; } @@ -919,7 +915,7 @@ static void delete_list(struct list_head *head) * hfs_cat_invalidate() * * Called by hfs_mdb_put() to remove all the entries - * in the cache which are associated with a given MDB. + * in the cache that are associated with a given MDB. */ void hfs_cat_invalidate(struct hfs_mdb *mdb) { @@ -931,6 +927,11 @@ void hfs_cat_invalidate(struct hfs_mdb *mdb) spin_unlock(&entry_lock); delete_list(&throw_away); +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_cat_invalidate: free=%d total=%d\n", + entries_stat.nr_free_entries, + entries_stat.nr_entries); +#endif } /* @@ -941,7 +942,7 @@ void hfs_cat_invalidate(struct hfs_mdb *mdb) void hfs_cat_commit(struct hfs_mdb *mdb) { struct list_head *tmp, *head = &mdb->entry_dirty; - struct hfs_cat_entry * entry; + struct hfs_cat_entry *entry; spin_lock(&entry_lock); while ((tmp = head->prev) != head) { @@ -1018,7 +1019,8 @@ int hfs_cat_compare(const struct hfs_cat_key *key1, if (parents != 0) { retval = (int)parents; } else { - retval = hfs_strcmp(&key1->CName, &key2->CName); + retval = hfs_strcmp(key1->CName.Name, key1->CName.Len, + key2->CName.Name, key2->CName.Len); } return retval; } @@ -1170,9 +1172,6 @@ struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry) * Create a new file with the indicated name in the indicated directory. * The file will have the indicated flags, type and creator. * If successful an (struct hfs_cat_entry) is returned in '*result'. - * - * XXX: the presence of "record" probably means that the following two - * aren't currently SMP safe and need spinlocks. */ int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key, hfs_u8 flags, hfs_u32 type, hfs_u32 creator, @@ -1182,6 +1181,10 @@ int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key, hfs_u32 id = new_cnid(parent->mdb); hfs_u32 mtime = hfs_time(); +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n", + parent, key->CName.Name, flags, result); +#endif /* init some fields for the file record */ memset(&record, 0, sizeof(record)); record.cdrType = HFS_CDR_FIL; @@ -1209,6 +1212,11 @@ int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key, hfs_u32 id = new_cnid(parent->mdb); hfs_u32 mtime = hfs_time(); +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name, + result); +#endif + /* init some fields for the directory record */ memset(&record, 0, sizeof(record)); record.cdrType = HFS_CDR_DIR; @@ -1234,6 +1242,10 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, struct hfs_mdb *mdb = parent->mdb; int is_dir, error = 0; +#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) + hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n", + parent, entry, entry->type, entry->state, with_thread); +#endif if (parent->mdb != entry->mdb) { return -EINVAL; } @@ -1252,39 +1264,39 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, if (entry->type == HFS_CDR_DIR) { start_read(entry); - if (entry->u.dir.files || entry->u.dir.dirs) { - error = -ENOTEMPTY; - } + error = -ENOTEMPTY; + if (entry->u.dir.files || entry->u.dir.dirs) + goto hfs_delete_end; } /* try to delete the file or directory */ - if (!error) { - lock_entry(entry); - if ((entry->state & HFS_DELETED)) { - /* somebody beat us to it */ - error = -ENOENT; - } else { - error = hfs_bdelete(mdb->cat_tree, - HFS_BKEY(&entry->key)); - } - unlock_entry(entry); + lock_entry(entry); + error = -ENOENT; + if ((entry->state & HFS_DELETED)) { + /* somebody beat us to it. */ + goto hfs_delete_unlock; + } + + /* delete the catalog record */ + if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) { + goto hfs_delete_unlock; } - if (!error) { - /* Mark the entry deleted and remove it from the cache */ - lock_entry(entry); - delete_entry(entry); - - /* try to delete the thread entry if it exists */ - if (with_thread) { - hfs_cat_build_key(entry->cnid, NULL, &key); - (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key)); - } + /* Mark the entry deleted and remove it from the cache */ + delete_entry(entry); - unlock_entry(entry); - update_dir(mdb, parent, is_dir, -1); + /* try to delete the thread entry if it exists */ + if (with_thread) { + hfs_cat_build_key(entry->cnid, NULL, &key); + (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key)); } + + update_dir(mdb, parent, is_dir, -1); + +hfs_delete_unlock: + unlock_entry(entry); +hfs_delete_end: if (entry->type == HFS_CDR_DIR) { end_read(entry); } @@ -1332,10 +1344,10 @@ int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, return -EINVAL; } - spin_lock(&entry_lock); while (mdb->rename_lock) { hfs_sleep_on(&mdb->rename_wait); } + spin_lock(&entry_lock); mdb->rename_lock = 1; spin_unlock(&entry_lock); @@ -1575,5 +1587,3 @@ void hfs_cat_init(void) i--; } while (i); } - - diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index afd794155..4ef71500d 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -41,7 +41,8 @@ static int build_key(struct hfs_cat_key *key, struct inode *dir, /* check against reserved names */ reserved = HFS_SB(dir->i_sb)->s_reserved1; while (reserved->Len) { - if (hfs_streq(reserved, &cname)) { + if (hfs_streq(reserved->Name, reserved->Len, + cname.Name, cname.Len)) { return 1; } ++reserved; @@ -51,7 +52,8 @@ static int build_key(struct hfs_cat_key *key, struct inode *dir, if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) { reserved = HFS_SB(dir->i_sb)->s_reserved2; while (reserved->Len) { - if (hfs_streq(reserved, &cname)) { + if (hfs_streq(reserved->Name, reserved->Len, + cname.Name, cname.Len)) { return 1; } ++reserved; @@ -129,16 +131,23 @@ static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir) * * Update inodes associated with a deleted entry to reflect its deletion. * Well, we really just drop the dentry. + * + * XXX: we should be using delete_inode for some of this stuff. */ static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, struct dentry *dentry) { struct dentry *de; + struct inode *tmp; int i; for (i = 0; i < 4; ++i) { if ((de = entry->sys_entry[i]) && (dentry != de)) { dget(de); + tmp = de->d_inode; + tmp->i_nlink = 0; + tmp->i_ctime = CURRENT_TIME; + mark_inode_dirty(tmp); d_delete(de); dput(de); } @@ -177,32 +186,32 @@ int hfs_create(struct inode * dir, struct dentry *dentry, int mode) int error; /* build the key, checking against reserved names */ - if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) { - error = -EEXIST; - } else { - /* try to create the file */ - error = hfs_cat_create(entry, &key, - (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK, - HFS_SB(dir->i_sb)->s_type, - HFS_SB(dir->i_sb)->s_creator, &new); - } - - if (!error) { - update_dirs_plus(entry, 0); - - /* create an inode for the new file */ - inode = hfs_iget(new, HFS_I(dir)->file_type, dentry); - if (!inode) { - /* XXX correct error? */ - error = -EIO; - } else { - if (HFS_I(dir)->d_drop_op) - HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type); - d_instantiate(dentry, inode); - } + if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) + return -EEXIST; + + if ((error = hfs_cat_create(entry, &key, + (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK, + HFS_SB(dir->i_sb)->s_type, + HFS_SB(dir->i_sb)->s_creator, &new))) + return error; + + /* create an inode for the new file. back out if we run + * into trouble. */ + new->count++; /* hfs_iget() eats one */ + if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) { + hfs_cat_delete(entry, new, 1); + hfs_cat_put(new); + return -EIO; } - return error; + hfs_cat_put(new); + update_dirs_plus(entry, 0); + /* toss any relevant negative dentries */ + if (HFS_I(dir)->d_drop_op) + HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type); + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + return 0; } /* @@ -223,23 +232,26 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) /* build the key, checking against reserved names */ if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) { - error = -EEXIST; - } else { - /* try to create the directory */ - error = hfs_cat_mkdir(entry, &key, &new); - } - - if (!error) { - update_dirs_plus(entry, 1); - inode = hfs_iget(new, HFS_I(parent)->file_type, dentry); - if (!inode) { - error = -EIO; - } else - d_instantiate(dentry, inode); + dentry->d_name.len)) + return -EEXIST; + + /* try to create the directory */ + if ((error = hfs_cat_mkdir(entry, &key, &new))) + return error; + + /* back out if we run into trouble */ + new->count++; /* hfs_iget eats one */ + if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) { + hfs_cat_delete(entry, new, 1); + hfs_cat_put(new); + return -EIO; } - return error; + hfs_cat_put(new); + update_dirs_plus(entry, 1); + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + return 0; } /* @@ -257,16 +269,14 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) */ int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { - int error; + if (!dir) + return -ENOENT; - if (!dir) { - error = -ENOENT; - } else if (S_ISREG(mode)) { - error = hfs_create(dir, dentry, mode); - } else { - error = -EPERM; - } - return error; + /* the only thing we currently do. */ + if (S_ISREG(mode)) + return hfs_create(dir, dentry, mode); + + return -EPERM; } /* @@ -285,18 +295,28 @@ int hfs_unlink(struct inode * dir, struct dentry *dentry) int error; if (build_key(&key, dir, dentry->d_name.name, - dentry->d_name.len)) { - error = -EPERM; - } else if (!(victim = hfs_cat_get(entry->mdb, &key))) { - error = -ENOENT; - } else if (victim->type != HFS_CDR_FIL) { - error = -EPERM; - } else if (!(error = hfs_cat_delete(entry, victim, 1))) { + dentry->d_name.len)) + return -EPERM; + + if (!(victim = hfs_cat_get(entry->mdb, &key))) + return -ENOENT; + + error = -EPERM; + if (victim->type != HFS_CDR_FIL) + goto hfs_unlink_put; + + if (!(error = hfs_cat_delete(entry, victim, 1))) { + struct inode *inode = dentry->d_inode; + mark_inodes_deleted(victim, dentry); + inode->i_nlink--; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); d_delete(dentry); update_dirs_minus(entry, 0); } +hfs_unlink_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ return error; } @@ -313,31 +333,50 @@ int hfs_rmdir(struct inode * parent, struct dentry *dentry) { struct hfs_cat_entry *entry = HFS_I(parent)->entry; struct hfs_cat_entry *victim = NULL; + struct inode *inode = dentry->d_inode; struct hfs_cat_key key; int error; + if (parent == inode) /* can't delete . */ + return -EPERM; + if (build_key(&key, parent, dentry->d_name.name, - dentry->d_name.len)) { - error = -EPERM; - } else if (!(victim = hfs_cat_get(entry->mdb, &key))) { - error = -ENOENT; - } else if (victim->type != HFS_CDR_DIR) { - error = -ENOTDIR; - } else if (/* we only have to worry about 2 and 3 for mount points */ - (victim->sys_entry[2] && - (victim->sys_entry[2] != - victim->sys_entry[2]->d_mounts)) || - (victim->sys_entry[3] && - (victim->sys_entry[3] != - victim->sys_entry[3]->d_mounts)) - ) { - error = -EBUSY; - } else if (!(error = hfs_cat_delete(entry, victim, 1))) { - mark_inodes_deleted(victim, dentry); - d_delete(dentry); - update_dirs_minus(entry, 1); - } + dentry->d_name.len)) + return -EPERM; + + if (!(victim = hfs_cat_get(entry->mdb, &key))) + return -ENOENT; + + error = -ENOTDIR; + if (victim->type != HFS_CDR_DIR) + goto hfs_rmdir_put; + + error = -EBUSY; + if (!list_empty(&dentry->d_hash)) + goto hfs_rmdir_put; + + if (/* we only have to worry about 2 and 3 for mount points */ + (victim->sys_entry[2] && + (victim->sys_entry[2]->d_mounts != + victim->sys_entry[2]->d_covers)) || + (victim->sys_entry[3] && + (victim->sys_entry[3]->d_mounts != + victim->sys_entry[3]->d_covers)) + ) + goto hfs_rmdir_put; + + + if ((error = hfs_cat_delete(entry, victim, 1))) + goto hfs_rmdir_put; + + mark_inodes_deleted(victim, dentry); + inode->i_nlink = 0; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + d_delete(dentry); + update_dirs_minus(entry, 1); +hfs_rmdir_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ return error; } @@ -365,35 +404,40 @@ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (build_key(&key, old_dir, old_dentry->d_name.name, old_dentry->d_name.len) || - (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) { - error = -EPERM; - } else if (!(victim = hfs_cat_get(old_parent->mdb, &key))) { - error = -ENOENT; - } else if (build_key(&key, new_dir, new_dentry->d_name.name, - new_dentry->d_name.len)) { - error = -EPERM; - } else if (!(error = hfs_cat_move(old_parent, new_parent, - victim, &key, &deleted))) { - int is_dir = (victim->type == HFS_CDR_DIR); + (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) + return -EPERM; + + if (!(victim = hfs_cat_get(old_parent->mdb, &key))) + return -ENOENT; + error = -EPERM; + if (build_key(&key, new_dir, new_dentry->d_name.name, + new_dentry->d_name.len)) + goto hfs_rename_put; + + if (!(error = hfs_cat_move(old_parent, new_parent, + victim, &key, &deleted))) { + int is_dir = (victim->type == HFS_CDR_DIR); + /* drop the old dentries */ mark_inodes_deleted(victim, old_dentry); update_dirs_minus(old_parent, is_dir); if (deleted) { - mark_inodes_deleted(deleted, new_dentry); - hfs_cat_put(deleted); + mark_inodes_deleted(deleted, new_dentry); + hfs_cat_put(deleted); } else { - /* no existing inodes. just drop negative dentries */ - if (HFS_I(new_dir)->d_drop_op) - HFS_I(new_dir)->d_drop_op(new_dentry, - HFS_I(new_dir)->file_type); - update_dirs_plus(new_parent, is_dir); + /* no existing inodes. just drop negative dentries */ + if (HFS_I(new_dir)->d_drop_op) + HFS_I(new_dir)->d_drop_op(new_dentry, + HFS_I(new_dir)->file_type); + update_dirs_plus(new_parent, is_dir); } - + /* update dcache */ d_move(old_dentry, new_dentry); } +hfs_rename_put: hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ return error; } diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index a40854e66..03fa7f7ae 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -167,43 +167,26 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry) hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - /* Check for "." */ - if (hfs_streq(&cname, DOT)) { - /* this little trick skips the iget and iput */ - d_add(dentry, dir); - return 0; - } - - /* Check for "..". */ - if (hfs_streq(&cname, DOT_DOT)) { - struct hfs_cat_entry *parent; - - if (dtype != HFS_CAP_NDIR) { - /* Case for ".." in ".finderinfo" or ".resource" */ - parent = entry; - ++entry->count; /* __hfs_iget() eats one */ - } else { - /* Case for ".." in a normal directory */ - parent = hfs_cat_parent(entry); - } - inode = hfs_iget(parent, HFS_CAP_NDIR, dentry); - goto done; - } + /* no need to check for "." or ".." */ /* Check for special directories if in a normal directory. Note that cap_dupdir() does an iput(dir). */ if (dtype==HFS_CAP_NDIR) { /* Check for ".resource", ".finderinfo" and ".rootinfo" */ - if (hfs_streq(&cname, DOT_RESOURCE)) { + if (hfs_streq(cname.Name, cname.Len, + DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_RDIR, dentry); goto done; - } else if (hfs_streq(&cname, DOT_FINDERINFO)) { + } else if (hfs_streq(cname.Name, cname.Len, + DOT_FINDERINFO->Name, + DOT_FINDERINFO_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_FDIR, dentry); goto done; } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - hfs_streq(&cname, DOT_ROOTINFO)) { + hfs_streq(cname.Name, cname.Len, + DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_FNDR, dentry); goto done; @@ -372,15 +355,14 @@ static int cap_readdir(struct file * filp, void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type) { if (type == HFS_CAP_DATA) { /* given name */ - hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry); - hfs_drop_special(DOT_RESOURCE, dentry->d_parent, dentry); + hfs_drop_special(dentry->d_parent, DOT_FINDERINFO, dentry); + hfs_drop_special(dentry->d_parent, DOT_RESOURCE, dentry); } else { struct dentry *de; - /* look for name */ - if ((de = hfs_lookup_dentry(dentry->d_name.name, - dentry->d_name.len, - dentry->d_parent->d_parent))) { + /* given {.resource,.finderinfo}/name, look for name */ + if ((de = hfs_lookup_dentry(dentry->d_parent->d_parent, + dentry->d_name.name, dentry->d_name.len))) { if (!de->d_inode) d_drop(de); dput(de); @@ -389,13 +371,13 @@ void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type) switch (type) { case HFS_CAP_RSRC: /* given .resource/name */ /* look for .finderinfo/name */ - hfs_drop_special(DOT_FINDERINFO, dentry->d_parent->d_parent, + hfs_drop_special(dentry->d_parent->d_parent, DOT_FINDERINFO, dentry); break; case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this * happens. */ /* look for .resource/name */ - hfs_drop_special(DOT_RESOURCE, dentry->d_parent->d_parent, + hfs_drop_special(dentry->d_parent->d_parent, DOT_RESOURCE, dentry); break; } diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 1e14b3f24..4e4e6fcc3 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -147,22 +147,12 @@ static int dbl_lookup(struct inode * dir, struct dentry *dentry) /* Perform name-mangling */ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - /* Check for "." */ - if (hfs_streq(&cname, DOT)) { - /* this little trick skips the iget and iput */ - d_add(dentry, dir); - return 0; - } - - /* Check for "..". */ - if (hfs_streq(&cname, DOT_DOT)) { - inode = hfs_iget(hfs_cat_parent(entry), HFS_DBL_DIR, dentry); - goto done; - } + /* no need to check for "." or ".." */ /* Check for "%RootInfo" if in the root directory. */ if ((entry->cnid == htonl(HFS_ROOT_CNID)) && - hfs_streq(&cname, PCNT_ROOTINFO)) { + hfs_streq(cname.Name, cname.Len, + PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_DBL_HDR, dentry); goto done; @@ -445,15 +435,15 @@ void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type) switch (type) { case HFS_DBL_HDR: /* given %name, look for name. i don't think this happens. */ - de = hfs_lookup_dentry(dentry->d_name.name + 1, dentry->d_name.len - 1, - dentry->d_parent); + de = hfs_lookup_dentry(dentry->d_parent, + dentry->d_name.name + 1, dentry->d_name.len - 1); break; case HFS_DBL_DATA: /* given name, look for %name */ tmp_name[0] = '%'; strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1); - de = hfs_lookup_dentry(tmp_name, dentry->d_name.len + 1, - dentry->d_parent); + de = hfs_lookup_dentry(dentry->d_parent, + tmp_name, dentry->d_name.len + 1); } if (de) { diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index b69a6bd70..a4078e891 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -42,6 +42,7 @@ static int nat_hdr_rename(struct inode *, struct dentry *, #define DOT_DOT_LEN 2 #define DOT_APPLEDOUBLE_LEN 12 #define DOT_PARENT_LEN 7 +#define ROOTINFO_LEN 8 const struct hfs_name hfs_nat_reserved1[] = { {DOT_LEN, "."}, @@ -52,13 +53,14 @@ const struct hfs_name hfs_nat_reserved1[] = { }; const struct hfs_name hfs_nat_reserved2[] = { - {0, ""}, + {ROOTINFO_LEN, "RootInfo"}, }; #define DOT (&hfs_nat_reserved1[0]) #define DOT_DOT (&hfs_nat_reserved1[1]) #define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2]) #define DOT_PARENT (&hfs_nat_reserved1[3]) +#define ROOTINFO (&hfs_nat_reserved2[0]) static struct file_operations hfs_nat_dir_operations = { NULL, /* lseek - default */ @@ -153,44 +155,33 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry) /* Perform name-mangling */ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - /* Check for "." */ - if (hfs_streq(&cname, DOT)) { - /* this little trick skips the iget and iput */ - d_add(dentry, dir); - return 0; - } - - /* Check for "..". */ - if (hfs_streq(&cname, DOT_DOT)) { - struct hfs_cat_entry *parent; - - if (dtype != HFS_NAT_NDIR) { - /* Case for ".." in ".AppleDouble" */ - parent = entry; - ++entry->count; /* __hfs_iget() eats one */ - } else { - /* Case for ".." in a normal directory */ - parent = hfs_cat_parent(entry); - } - inode = hfs_iget(parent, HFS_NAT_NDIR, dentry); - goto done; - } + /* no need to check for "." or ".." */ /* Check for ".AppleDouble" if in a normal directory, and for ".Parent" in ".AppleDouble". */ if (dtype==HFS_NAT_NDIR) { /* Check for ".AppleDouble" */ - if (hfs_streq(&cname, DOT_APPLEDOUBLE)) { + if (hfs_streq(cname.Name, cname.Len, + DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_NAT_HDIR, dentry); goto done; } } else if (dtype==HFS_NAT_HDIR) { - if (hfs_streq(&cname, DOT_PARENT)) { + if (hfs_streq(cname.Name, cname.Len, + DOT_PARENT->Name, DOT_PARENT_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_NAT_HDR, dentry); goto done; } + + if ((entry->cnid == htonl(HFS_ROOT_CNID)) && + hfs_streq(cname.Name, cname.Len, + ROOTINFO->Name, ROOTINFO_LEN)) { + ++entry->count; /* __hfs_iget() eats one */ + inode = hfs_iget(entry, HFS_NAT_HDR, dentry); + goto done; + } } /* Do an hfs_iget() on the mangled name. */ @@ -271,7 +262,7 @@ static int nat_readdir(struct file * filp, filp->f_pos = 2; } - if (filp->f_pos < (dir->i_size - 1)) { + if (filp->f_pos < (dir->i_size - 2)) { hfs_u32 cnid; hfs_u8 type; @@ -279,7 +270,7 @@ static int nat_readdir(struct file * filp, hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) { return 0; } - while (filp->f_pos < (dir->i_size - 1)) { + while (filp->f_pos < (dir->i_size - 2)) { if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) { return 0; } @@ -302,7 +293,7 @@ static int nat_readdir(struct file * filp, hfs_cat_close(entry, &brec); } - if (filp->f_pos == (dir->i_size - 1)) { + if (filp->f_pos == (dir->i_size - 2)) { if (type == HFS_NAT_NDIR) { /* In normal dirs entry 2 is for ".AppleDouble" */ if (filldir(dirent, DOT_APPLEDOUBLE->Name, @@ -321,6 +312,19 @@ static int nat_readdir(struct file * filp, ++filp->f_pos; } + if (filp->f_pos == (dir->i_size - 1)) { + /* handle ROOT/.AppleDouble/RootInfo as the last entry. */ + if ((entry->cnid == htonl(HFS_ROOT_CNID)) && + (type == HFS_NAT_HDIR)) { + if (filldir(dirent, ROOTINFO->Name, + ROOTINFO_LEN, filp->f_pos, + ntohl(entry->cnid) | HFS_NAT_HDR)) { + return 0; + } + } + ++filp->f_pos; + } + return 0; } @@ -337,9 +341,9 @@ void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type) switch (type) { case HFS_NAT_HDR: /* given .AppleDouble/name */ /* look for name */ - de = hfs_lookup_dentry(dentry->d_name.name, - dentry->d_name.len, - dentry->d_parent->d_parent); + de = hfs_lookup_dentry(dentry->d_parent->d_parent, + dentry->d_name.name, dentry->d_name.len); + if (de) { if (!de->d_inode) d_drop(de); @@ -348,9 +352,10 @@ void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type) break; case HFS_NAT_DATA: /* given name */ /* look for .AppleDouble/name */ - hfs_drop_special(DOT_APPLEDOUBLE, dentry->d_parent, dentry); + hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry); break; } + } /* @@ -370,7 +375,8 @@ static int nat_rmdir(struct inode *parent, struct dentry *dentry) int error; hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len); - if (hfs_streq(&cname, DOT_APPLEDOUBLE)) { + if (hfs_streq(cname.Name, cname.Len, + DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) { if (!HFS_SB(parent->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; @@ -415,7 +421,8 @@ static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry) hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); - if (!hfs_streq(&cname, DOT_PARENT)) { + if (!hfs_streq(cname.Name, cname.Len, + DOT_PARENT->Name, DOT_PARENT_LEN)) { struct hfs_cat_entry *victim; struct hfs_cat_key key; @@ -465,7 +472,8 @@ static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry, hfs_nameout(old_dir, &cname, old_dentry->d_name.name, old_dentry->d_name.len); - if (!hfs_streq(&cname, DOT_PARENT)) { + if (!hfs_streq(cname.Name, cname.Len, + DOT_PARENT->Name, DOT_PARENT_LEN)) { struct hfs_cat_entry *victim; struct hfs_cat_key key; diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c index 03d6b0acd..1fc749f34 100644 --- a/fs/hfs/extent.c +++ b/fs/hfs/extent.c @@ -339,6 +339,7 @@ static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext) if (error) { hfs_warn("hfs_truncate: error %d deleting an extent.\n", error); } + HFS_DELETE(ext); } @@ -486,6 +487,7 @@ static void shrink_fork(struct hfs_fork *fork, int ablocks) if ((count = next + 1 - ablocks) > 0) { for (i=2; (i>=0) && !ext->length[i]; --i) {}; + lock_bitmap(mdb); while (count && (ext->length[i] <= count)) { ext->end -= ext->length[i]; count -= ext->length[i]; @@ -508,6 +510,7 @@ static void shrink_fork(struct hfs_fork *fork, int ablocks) "blocks.\n", error); } } + unlock_bitmap(mdb); update_ext(fork, ext); } @@ -611,7 +614,9 @@ more_extents: } else { if (!(ext = new_extent(fork, ext, blocks/ablksz, start, len, ablksz))) { + lock_bitmap(mdb); hfs_clear_vbm_bits(mdb, start, len); + unlock_bitmap(mdb); return; } } diff --git a/fs/hfs/file.c b/fs/hfs/file.c index 6157afb47..00bebd017 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -98,11 +98,11 @@ struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create) /* If writing the block, then we have exclusive access to the file until we return, so it can't have moved. */ - if (tmp) { - hfs_cat_mark_dirty(fork->entry); - return getblk(dev, tmp, HFS_SECTOR_SIZE); - } - return NULL; + if (tmp) { + hfs_cat_mark_dirty(fork->entry); + return getblk(dev, tmp, HFS_SECTOR_SIZE); + } + return NULL; } else { /* If reading the block, then retry since the location on disk could have changed while @@ -220,8 +220,10 @@ static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf, pos += written; *ppos = pos; - if (*ppos > inode->i_size) + if (*ppos > inode->i_size) { inode->i_size = *ppos; + mark_inode_dirty(inode); + } return written; } @@ -236,7 +238,6 @@ static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf, */ static void hfs_file_truncate(struct inode * inode) { - /*struct inode *inode = dentry->d_inode;*/ struct hfs_fork *fork = HFS_I(inode)->fork; fork->lsize = inode->i_size; @@ -245,6 +246,7 @@ static void hfs_file_truncate(struct inode * inode) inode->i_size = fork->lsize; inode->i_blocks = fork->psize; + mark_inode_dirty(inode); } /* @@ -267,15 +269,19 @@ static inline void xlate_to_user(char *buf, const char *data, int count) * * Like copy_from_user() while translating NL->CR; */ -static inline void xlate_from_user(char *data, const char *buf, int count) +static inline int xlate_from_user(char *data, const char *buf, int count) { - count -= copy_from_user(data, buf, count); + int i; + + i = copy_from_user(data, buf, count); + count -= i; while (count--) { if (*data == '\n') { *data = '\r'; } ++data; } + return i; } /*================ Global functions ================*/ @@ -404,6 +410,13 @@ hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, xlate_to_user(buf, p, chars); } else { chars -= copy_to_user(buf, p, chars); + if (!chars) { + brelse(*bhe); + count = 0; + if (!read) + read = -EFAULT; + break; + } } brelse(*bhe); count -= chars; @@ -477,10 +490,13 @@ hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, } } p = (pos % HFS_SECTOR_SIZE) + bh->b_data; - if (convert) { - xlate_from_user(p, buf, c); - } else { - c -= copy_from_user(p, buf, c); + c -= convert ? xlate_from_user(p, buf, c) : + copy_from_user(p, buf, c); + if (!c) { + brelse(bh); + if (!written) + written = -EFAULT; + break; } update_vm_cache(inode,pos,p,c); pos += c; diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index 4fbd1f090..b3a58912c 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -180,6 +180,7 @@ static hfs_rwret_t cap_info_read(struct file *filp, char *buf, if (read) { inode->i_atime = CURRENT_TIME; *ppos = pos; + mark_inode_dirty(inode); } return read; @@ -236,7 +237,7 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, end = pos + mem_count; cap_build_meta(&meta, entry); - copy_from_user(((char *)&meta) + pos, buf, mem_count); + mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count); /* Update finder attributes if changed */ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) { @@ -280,6 +281,7 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, } inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); return count; } @@ -291,9 +293,8 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, */ static void cap_info_truncate(struct inode *inode) { - /*struct inode *inode = dentry->d_inode;*/ - if (inode->i_size > HFS_FORK_MAX) { inode->i_size = HFS_FORK_MAX; + mark_inode_dirty(inode); } } diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index b12a4606b..3ae2d5b5d 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -559,6 +559,7 @@ done: if (read) { inode->i_atime = CURRENT_TIME; *ppos = pos; + mark_inode_dirty(inode); } return read; } @@ -610,7 +611,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, left = count; } - copy_from_user(((char *)&meta) + pos, buf, left); + left -= copy_from_user(((char *)&meta) + pos, buf, left); layout->magic = hfs_get_nl(meta.magic); layout->version = hfs_get_nl(meta.version); layout->entries = hfs_get_hs(meta.entries); @@ -642,7 +643,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, left = count; } - copy_from_user(((char *)&meta) + pos, buf, left); + left -= copy_from_user(((char *)&meta) + pos, buf, left); init_layout(layout, meta.descrs); count -= left; @@ -782,7 +783,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, /* transfer the data from user space */ if (p) { - copy_from_user(p + offset, buf, left); + left -= copy_from_user(p + offset, buf, left); } else if (fork) { left = hfs_do_write(inode, fork, offset, buf, left); } @@ -874,6 +875,7 @@ done: if (pos > inode->i_size) inode->i_size = pos; inode->i_mtime = inode->i_atime = CURRENT_TIME; + mark_inode_dirty(inode); } return written; } @@ -887,7 +889,6 @@ done: */ static void hdr_truncate(struct inode *inode) { - /*struct inode *inode = dentry->d_inode;*/ struct hfs_cat_entry *entry = HFS_I(inode)->entry; struct hfs_hdr_layout *layout; size_t size = inode->i_size; @@ -936,7 +937,7 @@ static void hdr_truncate(struct inode *inode) if (fork->lsize != descr->length) { fork->lsize = descr->length; hfs_extent_adj(fork); - hfs_cat_mark_dirty(HFS_I(inode)->entry); + hfs_cat_mark_dirty(entry); } } } diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 9112a6db8..06138ab2d 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -64,12 +64,16 @@ #define HFS_EXT_CNID 3 /* EXTents B-tree */ #define HFS_CAT_CNID 4 /* CATalog B-tree */ #define HFS_BAD_CNID 5 /* BAD blocks file */ +#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */ +#define HFS_START_CNID 7 /* STARTup file (HFS+) */ +#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */ +#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */ /* values for hfs_cat_rec.cdrType */ -#define HFS_CDR_DIR 0x01 -#define HFS_CDR_FIL 0x02 -#define HFS_CDR_THD 0x03 -#define HFS_CDR_FTH 0x04 +#define HFS_CDR_DIR 0x01 /* folder (directory) */ +#define HFS_CDR_FIL 0x02 /* file */ +#define HFS_CDR_THD 0x03 /* folder (directory) thread */ +#define HFS_CDR_FTH 0x04 /* file thread */ /* legal values for hfs_ext_key.FkType and hfs_file.fork */ #define HFS_FK_DATA 0x00 @@ -484,42 +488,43 @@ extern void hfs_mdb_put(struct hfs_mdb *, int); extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *); /* string.c */ -extern unsigned long hfs_strhash(const struct hfs_name *); -extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *); -extern int hfs_streq(const struct hfs_name *, const struct hfs_name *); +extern unsigned int hfs_strhash(const unsigned char *, unsigned int); +extern int hfs_strcmp(const unsigned char *, unsigned int, + const unsigned char *, unsigned int); +extern int hfs_streq(const unsigned char *, unsigned int, + const unsigned char *, unsigned int); extern void hfs_tolower(unsigned char *, int); -extern __inline__ struct dentry -*hfs_lookup_dentry(const char *name, const int len, - struct dentry *base) +static __inline__ struct dentry +*hfs_lookup_dentry(struct dentry *base, const char *name, const int len) { struct qstr this; this.name = name; this.len = len; - this.hash = full_name_hash(name, len); + this.hash = hfs_strhash(name, len); return d_lookup(base, &this); } -/* drop a dentry for one of the special subdirectories */ -extern __inline__ void hfs_drop_special(const struct hfs_name *name, - struct dentry *base, +/* drop a dentry for one of the special directories. + * it's in the form of base/name/dentry. */ +static __inline__ void hfs_drop_special(struct dentry *base, + const struct hfs_name *name, struct dentry *dentry) { struct dentry *dparent, *de; - dparent = hfs_lookup_dentry(name->Name, name->Len, base); + dparent = hfs_lookup_dentry(base, name->Name, name->Len); if (dparent) { - de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len, - dparent); - dput(dparent); - - if (de) { - if (!de->d_inode) - d_drop(de); - dput(de); - } + de = hfs_lookup_dentry(dparent, dentry->d_name.name, + dentry->d_name.len); + if (de) { + if (!de->d_inode) + d_drop(de); + dput(de); + } + dput(dparent); } } diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 340e9be79..4d997baa8 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -73,6 +73,9 @@ static void init_file_inode(struct inode *inode, hfs_u8 fork) */ void hfs_put_inode(struct inode * inode) { + struct hfs_cat_entry *entry = HFS_I(inode)->entry; + + hfs_cat_put(entry); if (inode->i_count == 1) { struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; @@ -243,7 +246,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, if (inode->i_dev != sb->s_dev) { iput(inode); /* automatically does an hfs_cat_put */ inode = NULL; - } else if (!inode->i_mode) { + } else if (!inode->i_mode || (*sys_entry == NULL)) { /* Initialize the inode */ struct hfs_sb_info *hsb = HFS_SB(sb); @@ -266,8 +269,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, inode->i_mode &= ~hsb->s_umask; if (!inode->i_mode) { - clear_inode(inode); - hfs_cat_put(entry); + iput(inode); /* does an hfs_cat_put */ inode = NULL; } else *sys_entry = dentry; /* cache dentry */ @@ -398,7 +400,7 @@ void hfs_nat_ifill(struct inode * inode, ino_t type) struct hfs_dir *hdir = &entry->u.dir; inode->i_blocks = 0; - inode->i_size = hdir->files + hdir->dirs + 3; + inode->i_size = hdir->files + hdir->dirs + 4; inode->i_mode = S_IRWXUGO | S_IFDIR; HFS_I(inode)->dir_size = 1; if (type == HFS_NAT_NDIR) { diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index 45ad05022..c72673731 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -29,6 +29,8 @@ * the HFS equivalent of a superblock. * * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62 + * + * modified for HFS Extended */ struct raw_mdb { hfs_word_t drSigWord; /* Signature word indicating fs type */ @@ -60,9 +62,11 @@ struct raw_mdb { hfs_lword_t drFilCnt; /* number of files in the fs */ hfs_lword_t drDirCnt; /* number of directories in the fs */ hfs_byte_t drFndrInfo[32]; /* data used by the Finder */ - hfs_word_t drVCSize; /* MacOS caching parameter */ - hfs_word_t drVCBMSize; /* MacOS caching parameter */ - hfs_word_t drCtlCSize; /* MacOS caching parameter */ + hfs_word_t drEmbedSigWord; /* embedded volume signature */ + hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN) + and number of allocation blocks + (xdrNumABlks) occupied by embedded + volume */ hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */ hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */ hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */ @@ -95,24 +99,30 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, memset(mdb, 0, sizeof(*mdb)); mdb->magic = HFS_MDB_MAGIC; mdb->sys_mdb = sys_mdb; + INIT_LIST_HEAD(&mdb->entry_dirty); /* See if this is an HFS filesystem */ buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1); if (!hfs_buffer_ok(buf)) { hfs_warn("hfs_fs: Unable to read superblock\n"); + HFS_DELETE(mdb); goto bail2; } + raw = (struct raw_mdb *)hfs_buffer_data(buf); if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) { hfs_buffer_put(buf); + HFS_DELETE(mdb); goto bail2; } mdb->buf = buf; - + bs = hfs_get_hl(raw->drAlBlkSiz); if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) { hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs); - goto bail1; + hfs_buffer_put(buf); + HFS_DELETE(mdb); + goto bail2; } mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS; @@ -123,7 +133,7 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, mdb->backup_date = hfs_get_hl(raw->drVolBkUp); mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz) >> HFS_SECTOR_SIZE_BITS; - memcpy(mdb->vname, raw->drVN, 28); + memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN)); /* These parameters are read from and written to the MDB */ mdb->modify_date = hfs_get_nl(raw->drLsMod); @@ -135,8 +145,8 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs); mdb->file_count = hfs_get_hl(raw->drFilCnt); mdb->dir_count = hfs_get_hl(raw->drDirCnt); - - /* TRY to get the alternate (backup) MDB */ + + /* TRY to get the alternate (backup) MDB. */ lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz; limit = lcv + mdb->alloc_blksz; for (; lcv < limit; ++lcv) { @@ -144,20 +154,21 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, if (hfs_buffer_ok(buf)) { struct raw_mdb *tmp = (struct raw_mdb *)hfs_buffer_data(buf); - + if (hfs_get_ns(tmp->drSigWord) == - htons(HFS_SUPER_MAGIC)) { + htons(HFS_SUPER_MAGIC)) { mdb->alt_buf = buf; break; } - hfs_buffer_put(buf); } + hfs_buffer_put(buf); } + if (mdb->alt_buf == NULL) { hfs_warn("hfs_fs: unable to locate alternate MDB\n"); hfs_warn("hfs_fs: continuing without an alternate MDB\n"); } - + /* read in the bitmap */ block = hfs_get_hs(raw->drVBMSt) + part_start; bmbuf = mdb->bitmap; @@ -252,20 +263,29 @@ void hfs_mdb_commit(struct hfs_mdb *mdb, int backup) /* write MDB to disk */ hfs_buffer_dirty(mdb->buf); - /* write the backup MDB, not returning until it is written */ + /* write the backup MDB, not returning until it is written. + * we only do this when either the catalog or extents overflow + * files grow. */ if (backup && hfs_buffer_ok(mdb->alt_buf)) { - memcpy(hfs_buffer_data(mdb->alt_buf), - hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE); - hfs_buffer_dirty(mdb->alt_buf); - hfs_buffer_sync(mdb->alt_buf); + struct raw_mdb *tmp = (struct raw_mdb *) + hfs_buffer_data(mdb->alt_buf); + + if ((hfs_get_hl(tmp->drCTFlSize) < + hfs_get_hl(raw->drCTFlSize)) || + (hfs_get_hl(tmp->drXTFlSize) < + hfs_get_hl(raw->drXTFlSize))) { + memcpy(hfs_buffer_data(mdb->alt_buf), + hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE); + hfs_buffer_dirty(mdb->alt_buf); + hfs_buffer_sync(mdb->alt_buf); + } } } /* * hfs_mdb_put() * - * Release the resources associated with the in-core MDB. - */ + * Release the resources associated with the in-core MDB. */ void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) { int lcv; @@ -283,7 +303,7 @@ void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) { /* update volume attributes */ if (!readonly) { - struct raw_mdb *raw = + struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf); hfs_put_ns(mdb->attrib, raw->drAtrb); hfs_buffer_dirty(mdb->buf); diff --git a/fs/hfs/string.c b/fs/hfs/string.c index 030850b82..18e59de5d 100644 --- a/fs/hfs/string.c +++ b/fs/hfs/string.c @@ -81,13 +81,13 @@ static unsigned char casefold[256] = { /* * Hash a string to an integer in a case-independent way */ -unsigned long hfs_strhash(const struct hfs_name *cname) +unsigned int hfs_strhash(const unsigned char *name, unsigned int len) { unsigned long hash = init_name_hash(); - unsigned int i; - for (i = 0; i < cname->Len; i++) { - hash = partial_name_hash(caseorder[cname->Name[i]], hash); - } + + while (len--) + hash = partial_name_hash(caseorder[*name++], + hash); return end_name_hash(hash); } @@ -98,45 +98,34 @@ unsigned long hfs_strhash(const struct hfs_name *cname) * Equivalent to ARDI's call: * ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0]) */ -int hfs_strcmp(const struct hfs_name *s1, const struct hfs_name *s2) +int hfs_strcmp(const unsigned char *s1, unsigned int len1, + const unsigned char *s2, unsigned int len2) { int len, tmp; - const unsigned char *p1, *p2; - - if (!s1 || !s2) { - return 0; - } - len = (s1->Len > s2->Len) ? s2->Len : s1->Len; - p1 = s1->Name; - p2 = s2->Name; + len = (len1 > len2) ? len2 : len1; while (len--) { - if ((tmp = (int)caseorder[*(p1++)]-(int)caseorder[*(p2++)])) { + if ((tmp = (int)caseorder[*(s1++)] - + (int)caseorder[*(s2++)])) { return tmp; } } - return s1->Len - s2->Len; + return len1 - len2; } /* * Test for equality of two strings in the HFS filename character ordering. */ -int hfs_streq(const struct hfs_name *s1, const struct hfs_name *s2) +int hfs_streq(const unsigned char *s1, unsigned int len1, + const unsigned char *s2, unsigned int len2) { - int len; - const unsigned char *p1, *p2; - - if (!s1 || !s2 || (s1->Len != s2->Len)) { + if (len1 != len2) { return 0; } - len = s1->Len; - p1 = s1->Name; - p2 = s2->Name; - - while (len--) { - if (caseorder[*(p1++)] != caseorder[*(p2++)]) { + while (len1--) { + if (caseorder[*(s1++)] != caseorder[*(s2++)]) { return 0; } } diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 381969d34..6f177f136 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -34,7 +34,7 @@ /*================ Forward declarations ================*/ -static void hfs_read_inode(struct inode *inode); +static void hfs_read_inode(struct inode *); static void hfs_put_super(struct super_block *); static int hfs_statfs(struct super_block *, struct statfs *, int); static void hfs_write_super(struct super_block *); @@ -45,7 +45,7 @@ static struct super_operations hfs_super_operations = { hfs_read_inode, /* read_inode */ NULL, /* write_inode */ hfs_put_inode, /* put_inode - in inode.c */ - NULL, /* delete inode */ + NULL, /* delete_inode */ hfs_notify_change, /* notify_change - in inode.c */ hfs_put_super, /* put_super */ hfs_write_super, /* write_super */ @@ -75,7 +75,6 @@ static void hfs_read_inode(struct inode *inode) inode->i_op = NULL; } - /* * hfs_write_super() * @@ -134,8 +133,6 @@ static void hfs_put_super(struct super_block *sb) set_blocksize(sb->s_dev, BLOCK_SIZE); MOD_DEC_USE_COUNT; - - return; } /* @@ -145,7 +142,7 @@ static void hfs_put_super(struct super_block *sb) * HFS filesystems. The purpose is to return various data about the * filesystem. * - * XXX: changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. + * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. */ static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len) { @@ -434,14 +431,13 @@ struct super_block *hfs_read_super(struct super_block *s, void *data, if (!mdb) { if (!silent) { - printk("VFS: Can't find a HFS filesystem on dev %s.\n", + hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", kdevname(dev)); } goto bail2; } - HFS_SB(s)->s_mdb = mdb; - INIT_LIST_HEAD(&mdb->entry_dirty); + HFS_SB(s)->s_mdb = mdb; if (HFS_ITYPE(mdb->next_id) != 0) { hfs_warn("hfs_fs: too many files.\n"); goto bail1; @@ -480,8 +476,8 @@ bail1: hfs_mdb_put(mdb, s->s_flags & MS_RDONLY); bail2: set_blocksize(dev, BLOCK_SIZE); - MOD_DEC_USE_COUNT; unlock_super(s); + MOD_DEC_USE_COUNT; bail3: s->s_dev = 0; return NULL; diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index 659d0b2fc..c1fb812de 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c @@ -61,29 +61,23 @@ hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) { /* hfs_strhash now uses the same hashing function as the dcache. */ static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this) { - struct hfs_name cname; - - if ((cname.Len = this->len) > HFS_NAMELEN) + if (this->len > HFS_NAMELEN) return 0; - strncpy(cname.Name, this->name, this->len); - this->hash = hfs_strhash(&cname); + this->hash = hfs_strhash(this->name, this->len); return 0; } +/* return 1 on failure and 0 on success */ static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) { - struct hfs_name s1, s2; - if (a->len != b->len) return 1; - if ((s1.Len = s2.Len = a->len) > HFS_NAMELEN) + if (a->len > HFS_NAMELEN) return 1; - strncpy(s1.Name, a->name, s1.Len); - strncpy(s2.Name, b->name, s2.Len); - return hfs_streq(&s1, &s2); + return !hfs_streq(a->name, a->len, b->name, b->len); } static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode) @@ -91,6 +85,5 @@ static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode) struct hfs_cat_entry *entry = HFS_I(inode)->entry; entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL; - hfs_cat_put(entry); iput(inode); } diff --git a/fs/hfs/version.c b/fs/hfs/version.c index 8652c1cca..365e9f375 100644 --- a/fs/hfs/version.c +++ b/fs/hfs/version.c @@ -7,4 +7,4 @@ * This file contains the version string for this release. */ -const char hfs_version[]="0.95+asun3"; +const char hfs_version[]="0.96"; diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index a0d77893f..feafa2861 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -340,7 +340,7 @@ struct super_block *hpfs_read_super(struct super_block *s, struct hpfs_boot_block *bootblock; struct hpfs_super_block *superblock; struct hpfs_spare_block *spareblock; - struct hpfs_dirent *de; + struct hpfs_dirent *de = NULL; struct buffer_head *bh0, *bh1, *bh2; struct quad_buffer_head qbh; dnode_secno root_dno; diff --git a/fs/inode.c b/fs/inode.c index d1dd70265..e1fae751e 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -131,6 +131,7 @@ static inline void init_once(struct inode * inode) INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); + sema_init(&inode->i_atomic_write, 1); } static inline void write_inode(struct inode *inode) @@ -558,6 +559,9 @@ add_new_inode: /* * This is called with the inode lock held.. Be careful. + * + * We no longer cache the sb_flags in i_flags - see fs.h + * -- rmk@arm.uk.linux.org */ static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head) { @@ -574,7 +578,7 @@ add_new_inode: inode->i_sb = sb; inode->i_dev = sb->s_dev; inode->i_ino = ino; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; inode->i_count = 1; inode->i_state = I_LOCK; spin_unlock(&inode_lock); @@ -714,8 +718,11 @@ if (inode->i_count) printk(KERN_ERR "iput: device %s inode %ld count changed, count=%d\n", kdevname(inode->i_dev), inode->i_ino, inode->i_count); if (atomic_read(&inode->i_sem.count) != 1) -printk(KERN_ERR "iput: Aieee, semaphore in use device %s, count=%d\n", -kdevname(inode->i_dev), atomic_read(&inode->i_sem.count)); +printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n", +kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); +if (atomic_read(&inode->i_atomic_write.count) != 1) +printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n", +kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); #endif } if (inode->i_count > (1<<31)) { diff --git a/fs/ioctl.c b/fs/ioctl.c index 59429ba16..9ac7c3bf2 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -4,15 +4,8 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/sched.h> #include <linux/mm.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/termios.h> -#include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/fcntl.h> /* for f_flags values */ #include <linux/file.h> #include <asm/uaccess.h> diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index f73ec5271..a57f9680c 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -59,7 +59,7 @@ struct inode_operations isofs_dir_inode_operations = NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ - isofs_bmap, /* bmap */ + NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 06de6ac7c..26c72dab9 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -320,7 +320,7 @@ static int parse_options(char *options, struct iso9660_options * popt) *value++ = 0; #ifdef CONFIG_JOLIET - if (!strcmp(this_char,"iocharset")) { + if (!strcmp(this_char,"iocharset") && value) { popt->iocharset = value; while (*value && *value != ',') value++; @@ -445,25 +445,31 @@ static unsigned int isofs_get_last_session(kdev_t dev) return vol_desc_start; } +/* + * Initialize the superblock and read the root inode. + * + * Note: a check_disk_change() has been done immediately prior + * to this call, so we don't need to check again. + */ struct super_block *isofs_read_super(struct super_block *s, void *data, int silent) { - struct buffer_head * bh = NULL, *pri_bh = NULL; - unsigned int blocksize; - unsigned int blocksize_bits; kdev_t dev = s->s_dev; + struct buffer_head * bh = NULL, *pri_bh = NULL; struct hs_primary_descriptor * h_pri = NULL; struct iso_primary_descriptor * pri = NULL; struct iso_supplementary_descriptor *sec = NULL; struct iso_directory_record * rootp; + int joliet_level = 0; int high_sierra; int iso_blknum, block; - int joliet_level = 0; int orig_zonesize; + int table; + unsigned int blocksize, blocksize_bits; unsigned int vol_desc_start; + unsigned long first_data_zone; struct inode * inode; struct iso9660_options opt; - int table; MOD_INC_USE_COUNT; /* lock before any blocking operations */ @@ -592,7 +598,6 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, root_found: brelse(pri_bh); - s->u.isofs_sb.s_joliet_level = joliet_level; if (joliet_level && opt.rock == 'n') { /* This is the case of Joliet with the norock mount flag. @@ -623,10 +628,7 @@ root_found: s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */ - /* RDE: convert log zone size to bit shift */ - orig_zonesize = s -> u.isofs_sb.s_log_zone_size; - /* * If the zone size is smaller than the hardware sector size, * this is a fatal error. This would occur if the disc drive @@ -637,6 +639,7 @@ root_found: if(blocksize != 0 && orig_zonesize < blocksize) goto out_bad_size; + /* RDE: convert log zone size to bit shift */ switch (s -> u.isofs_sb.s_log_zone_size) { case 512: s -> u.isofs_sb.s_log_zone_size = 9; break; case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break; @@ -657,14 +660,15 @@ root_found: /* RDE: data zone now byte offset! */ - s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); + first_data_zone = ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); + s->u.isofs_sb.s_firstdatazone = first_data_zone; #ifndef BEQUIET printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n", s->u.isofs_sb.s_max_size, 1UL << s->u.isofs_sb.s_log_zone_size); - printk(KERN_DEBUG "First datazone:%ld Root inode number %d\n", + printk(KERN_DEBUG "First datazone:%ld Root inode number:%ld\n", s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size, s->u.isofs_sb.s_firstdatazone); if(high_sierra) @@ -672,6 +676,29 @@ root_found: #endif /* + * If the Joliet level is set, we _may_ decide to use the + * secondary descriptor, but can't be sure until after we + * read the root inode. But before reading the root inode + * we may need to change the device blocksize, and would + * rather release the old buffer first. So, we cache the + * first_data_zone value from the secondary descriptor. + */ + if (joliet_level) { + pri = (struct iso_primary_descriptor *) sec; + rootp = (struct iso_directory_record *) + pri->root_directory_record; + first_data_zone = ((isonum_733 (rootp->extent) + + isonum_711 (rootp->ext_attr_length)) + << s -> u.isofs_sb.s_log_zone_size); + } + + /* + * We're all done using the volume descriptor, and may need + * to change the device blocksize, so release the buffer now. + */ + brelse(bh); + + /* * Force the blocksize to 512 for 512 byte sectors. The file * read primitives really get it wrong in a bad way if we don't * do this. @@ -688,22 +715,15 @@ root_found: * entries. By forcing the blocksize in this way, we ensure * that we will never be required to do this. */ - if( orig_zonesize != opt.blocksize ) - { - opt.blocksize = orig_zonesize; - blocksize_bits = 0; - { - int i = opt.blocksize; - while (i != 1){ - blocksize_bits++; - i >>=1; - } - } - set_blocksize(dev, opt.blocksize); + if ( orig_zonesize != opt.blocksize ) { + set_blocksize(dev, orig_zonesize); #ifndef BEQUIET - printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize); + printk(KERN_DEBUG + "ISOFS: Forcing new log zone size:%d\n", orig_zonesize); #endif - } + } + s->s_blocksize = orig_zonesize; + s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size; s->u.isofs_sb.s_nls_iocharset = NULL; @@ -732,8 +752,12 @@ root_found: * as suid, so we merely allow them to set the default permissions. */ s->u.isofs_sb.s_mode = opt.mode & 0777; - s->s_blocksize = opt.blocksize; - s->s_blocksize_bits = blocksize_bits; + + /* + * Read the root inode, which _may_ result in changing + * the s_rock flag. Once we have the final s_rock value, + * we then decide whether to use the Joliet descriptor. + */ inode = iget(s, s->u.isofs_sb.s_firstdatazone); /* @@ -744,21 +768,17 @@ root_found: * CD with Unicode names. Until someone sees such a beast, it * will not be supported. */ - if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) { + if (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 *) - pri->root_directory_record; - s->u.isofs_sb.s_firstdatazone = - ((isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); - inode = iget(s, s->u.isofs_sb.s_firstdatazone); + } else if (joliet_level) { s->u.isofs_sb.s_rock = 0; - opt.rock = 'n'; + if (s->u.isofs_sb.s_firstdatazone != first_data_zone) { + s->u.isofs_sb.s_firstdatazone = first_data_zone; + printk(KERN_DEBUG + "ISOFS: changing to secondary root\n"); + iput(inode); + inode = iget(s, s->u.isofs_sb.s_firstdatazone); + } } if (opt.check == 'u') { @@ -766,32 +786,46 @@ root_found: if (joliet_level) opt.check = 'r'; else opt.check = 's'; } + s->u.isofs_sb.s_joliet_level = joliet_level; + /* check the root inode */ + if (!inode) + goto out_no_root; + if (!inode->i_op) + goto out_bad_root; + /* get the root dentry */ 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); - unlock_super(s); - return s; - } - /* - * Disk changed? Free the root dentry and clean up ... - */ - dput(s->s_root); - goto out_freechar; + unlock_super(s); + return s; /* - * Display error message + * Display error messages and free resources. */ -out_no_root: - printk(KERN_ERR "isofs_read_super: get root inode failed\n"); +out_bad_root: + printk(KERN_WARNING "isofs_read_super: root inode not initialized\n"); goto out_iput; +out_no_root: + printk(KERN_WARNING "isofs_read_super: get root inode failed\n"); +out_iput: + iput(inode); +#ifdef CONFIG_JOLIET + if (s->u.isofs_sb.s_nls_iocharset) + unload_nls(s->u.isofs_sb.s_nls_iocharset); +#endif + goto out_unlock; +out_no_read: + printk(KERN_WARNING "isofs_read_super: " + "bread failed, dev=%s, iso_blknum=%d, block=%d\n", + kdevname(dev), iso_blknum, block); + goto out_unlock; out_bad_zone_size: printk(KERN_WARNING "Bad logical zone size %ld\n", s->u.isofs_sb.s_log_zone_size); @@ -808,23 +842,7 @@ out_no_support: out_unknown_format: if (!silent) printk(KERN_WARNING "Unable to identify CD-ROM format.\n"); - goto out_freebh; -out_no_read: - printk(KERN_WARNING "isofs_read_super: " - "bread failed, dev=%s, iso_blknum=%d, block=%d\n", - kdevname(dev), iso_blknum, block); - goto out_unlock; - /* - * Cascaded error cleanup to ensure all resources are freed. - */ -out_iput: - iput(inode); -out_freechar: -#ifdef CONFIG_JOLIET - if (s->u.isofs_sb.s_nls_iocharset) - unload_nls(s->u.isofs_sb.s_nls_iocharset); -#endif out_freebh: brelse(bh); out_unlock: @@ -945,101 +963,113 @@ static void test_and_set_uid(uid_t *p, uid_t value) static int isofs_read_level3_size(struct inode * inode) { + unsigned long ino = inode->i_ino; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; struct buffer_head * bh = NULL; - struct iso_directory_record * raw_inode = NULL; /* quiet gcc */ - unsigned char *pnt = NULL; - void *cpnt = NULL; - int block = 0; /* Quiet GCC */ - unsigned long ino; - int i; + int block = 0; + int i = 0; + void *cpnt; + struct iso_directory_record * raw_inode; inode->i_size = 0; inode->u.isofs_i.i_next_section_ino = 0; - ino = inode->i_ino; - i = 0; do { - if(i > 100) { - printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n" - "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino); - return 0; - } + unsigned char *pnt; + unsigned int reclen; + int offset = (ino & (bufsize - 1)); - if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) { - if(bh) brelse(bh); + cpnt = NULL; + /* Check whether to update our buffer */ + if (block != ino >> ISOFS_BUFFER_BITS(inode)) { block = ino >> ISOFS_BUFFER_BITS(inode); - if (!(bh=bread(inode->i_dev,block, bufsize))) { - printk("unable to read i-node block"); - return 1; - } - } - pnt = ((unsigned char *) bh->b_data - + (ino & (bufsize - 1))); - - if ((ino & (bufsize - 1)) + *pnt > bufsize){ - int frag1, offset; - - offset = (ino & (bufsize - 1)); - frag1 = bufsize - offset; - cpnt = kmalloc(*pnt,GFP_KERNEL); - if (cpnt == NULL) { - printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino); - brelse(bh); - return 1; - } - memcpy(cpnt, bh->b_data + offset, frag1); brelse(bh); - if (!(bh = bread(inode->i_dev,++block, bufsize))) { - kfree(cpnt); - printk("unable to read i-node block"); - return 1; - } - offset += *pnt - bufsize; - memcpy((char *)cpnt+frag1, bh->b_data, offset); - pnt = ((unsigned char *) cpnt); + bh = bread(inode->i_dev, block, bufsize); + if (!bh) + goto out_noread; } - - if(*pnt == 0) { + pnt = ((unsigned char *) bh->b_data + offset); + raw_inode = ((struct iso_directory_record *) pnt); + /* + * Note: this is invariant even if the record + * spans buffers and must be copied ... + */ + reclen = *pnt; + + /* N.B. this test doesn't trigger the i++ code ... */ + if(reclen == 0) { ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE; continue; } - raw_inode = ((struct iso_directory_record *) pnt); + + /* Check whether the raw inode spans the buffer ... */ + if (offset + reclen > bufsize){ + int frag1 = bufsize - offset; + + cpnt = kmalloc(reclen, GFP_KERNEL); + if (cpnt == NULL) + goto out_nomem; + memcpy(cpnt, pnt, frag1); + brelse(bh); + bh = bread(inode->i_dev, ++block, bufsize); + if (!bh) + goto out_noread; + offset += reclen - bufsize; + memcpy((char *)cpnt+frag1, bh->b_data, offset); + raw_inode = ((struct iso_directory_record *) cpnt); + } inode->i_size += isonum_733 (raw_inode->size); if(i == 1) inode->u.isofs_i.i_next_section_ino = ino; - ino += *pnt; - if (cpnt) { + ino += reclen; + if (cpnt) kfree (cpnt); - cpnt = NULL; - } i++; - } while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80); + if(i > 100) + goto out_toomany; + } while(raw_inode->flags[-high_sierra] & 0x80); +out: brelse(bh); return 0; + +out_nomem: + printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino); + brelse(bh); + return 1; +out_noread: + printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block); + if (cpnt) + kfree(cpnt); + return 1; +out_toomany: + printk(KERN_INFO "isofs_read_level3_size: " + "More than 100 file sections ?!?, aborting...\n" + "isofs_read_level3_size: inode=%lu ino=%lu\n", + inode->i_ino, ino); + goto out; } void isofs_read_inode(struct inode * inode) { + struct super_block *sb = inode->i_sb; unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode); + int high_sierra = sb->u.isofs_sb.s_high_sierra; struct buffer_head * bh; struct iso_directory_record * raw_inode; - unsigned char *pnt = NULL; - int high_sierra; - int block; - int volume_seq_no ; - int i; + unsigned char *pnt; + int volume_seq_no, i; - block = inode->i_ino >> ISOFS_BUFFER_BITS(inode); - if (!(bh=bread(inode->i_dev,block, bufsize))) { - printk("unable to read i-node block"); - goto fail; + bh = bread(inode->i_dev, block, bufsize); + if (!bh) { + printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); + goto fail; } pnt = ((unsigned char *) bh->b_data + (inode->i_ino & (bufsize - 1))); raw_inode = ((struct iso_directory_record *) pnt); - high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; if (raw_inode->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; @@ -1049,10 +1079,13 @@ void isofs_read_inode(struct inode * inode) easier to give 1 which tells find to do it the hard way. */ } else { - inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */ + /* Everybody gets to read the file. */ + inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; inode->i_nlink = 1; inode->i_mode |= S_IFREG; -/* If there are no periods in the name, then set the execute permission bit */ + /* If there are no periods in the name, + * then set the execute permission bit + */ for(i=0; i< raw_inode->name_len[0]; i++) if(raw_inode->name[i]=='.' || raw_inode->name[i]==';') break; @@ -1133,14 +1166,16 @@ void isofs_read_inode(struct inode * inode) #ifdef DEBUG printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent); #endif - brelse(bh); - - inode->i_op = NULL; /* get the volume sequence number */ volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ; /* + * All done with buffer ... no more references to buffer memory! + */ + brelse(bh); + + /* * Disable checking if we see any volume number other than 0 or 1. * We could use the cruft option, but that has multiple purposes, one * of which is limiting the file size to 16Mb. Thus we silently allow @@ -1152,6 +1187,8 @@ void isofs_read_inode(struct inode * inode) inode->i_sb->u.isofs_sb.s_cruft = 'y'; } + /* Install the inode operations vector */ + inode->i_op = NULL; #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS if (inode->i_sb->u.isofs_sb.s_cruft != 'y' && (volume_seq_no != 0) && (volume_seq_no != 1)) { @@ -1173,6 +1210,7 @@ void isofs_read_inode(struct inode * inode) init_fifo(inode); } return; + fail: /* With a data error we return this information */ inode->i_mtime = inode->i_atime = inode->i_ctime = 0; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index d59340f10..5891b493e 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -201,8 +201,8 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) * Skip hidden or associated files unless unhide is set */ match = 0; - if( !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) - || dir->i_sb->u.isofs_sb.s_unhide == 'y' ) + if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) + || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen) { match = (isofs_cmp(dentry,dpnt,dlen) == 0); } diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index f3069c2c3..8b5c8befd 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -54,25 +54,23 @@ {if (buffer) kfree(buffer); \ if (cont_extent){ \ int block, offset, offset1; \ - struct buffer_head * bh; \ + struct buffer_head * pbh; \ buffer = kmalloc(cont_size,GFP_KERNEL); \ if (!buffer) goto out; \ block = cont_extent; \ offset = cont_offset; \ offset1 = 0; \ - if(buffer) { \ - bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \ - if(bh){ \ - memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \ - brelse(bh); \ - chr = (unsigned char *) buffer; \ - len = cont_size; \ - cont_extent = 0; \ - cont_size = 0; \ - cont_offset = 0; \ - goto LABEL; \ - }; \ - } \ + pbh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \ + if(pbh){ \ + memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \ + brelse(pbh); \ + chr = (unsigned char *) buffer; \ + len = cont_size; \ + cont_extent = 0; \ + cont_size = 0; \ + cont_offset = 0; \ + goto LABEL; \ + }; \ printk("Unable to read rock-ridge attributes\n"); \ }} @@ -162,7 +160,6 @@ int get_rock_ridge_filename(struct iso_directory_record * de, if (!inode->i_sb->u.isofs_sb.s_rock) return 0; *retname = 0; - retnamlen = 0; SETUP_ROCK_RIDGE(de, chr, len); repeat: @@ -364,6 +361,8 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) << inode -> i_sb -> u.isofs_sb.s_log_zone_size; reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent); + if (!reloc) + goto out; inode->i_mode = reloc->i_mode; inode->i_nlink = reloc->i_nlink; inode->i_uid = reloc->i_uid; @@ -396,8 +395,8 @@ char * get_rock_ridge_symlink(struct inode * inode) unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); struct buffer_head * bh; + char * rpnt = NULL; unsigned char * pnt; - char * rpnt; struct iso_directory_record * raw_inode; CONTINUE_DECLS; int block; @@ -410,13 +409,10 @@ char * get_rock_ridge_symlink(struct inode * inode) if (!inode->i_sb->u.isofs_sb.s_rock) panic("Cannot have symlink with high sierra variant of iso filesystem\n"); - rpnt = 0; - block = inode->i_ino >> bufbits; - if (!(bh=bread(inode->i_dev,block, bufsize))) { - printk("unable to read i-node block"); - return NULL; - }; + bh = bread(inode->i_dev, block, bufsize); + if (!bh) + goto out_noread; pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1)); @@ -425,10 +421,8 @@ char * get_rock_ridge_symlink(struct inode * inode) /* * If we go past the end of the buffer, there is some sort of error. */ - if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){ - printk("symlink spans iso9660 blocks\n"); - return NULL; - }; + if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize) + goto out_bad_span; /* Now test for possible Rock Ridge extensions which will override some of these numbers in the inode structure. */ @@ -511,16 +505,23 @@ char * get_rock_ridge_symlink(struct inode * inode) }; }; MAYBE_CONTINUE(repeat,inode); - brelse(bh); - return rpnt; - out: - if(buffer) kfree(buffer); - return 0; +out_freebh: + brelse(bh); + return rpnt; + + /* error exit from macro */ +out: + if(buffer) + kfree(buffer); + if(rpnt) + kfree(rpnt); + rpnt = NULL; + goto out_freebh; +out_noread: + printk("unable to read i-node block"); + goto out_freebh; +out_bad_span: + printk("symlink spans iso9660 blocks\n"); + goto out_freebh; } - - - - - - diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c index c85510b53..0f909c428 100644 --- a/fs/isofs/symlink.c +++ b/fs/isofs/symlink.c @@ -19,7 +19,7 @@ #include <asm/uaccess.h> static int isofs_readlink(struct dentry *, char *, int); -static struct dentry * isofs_follow_link(struct dentry *, struct dentry *); +static struct dentry * isofs_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -66,7 +66,8 @@ static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen) } static struct dentry * isofs_follow_link(struct dentry * dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { char * pnt; @@ -76,7 +77,7 @@ static struct dentry * isofs_follow_link(struct dentry * dentry, return ERR_PTR(-ELOOP); } - base = lookup_dentry(pnt, base, 1); + base = lookup_dentry(pnt, base, follow); kfree(pnt); return base; diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index ad70f7b55..b1edd0953 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -15,6 +15,7 @@ #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svc.h> #include <linux/lockd/lockd.h> +#include <linux/smp_lock.h> #define NLMDBG_FACILITY NLMDBG_CIENT @@ -70,7 +71,11 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp) * a 1 minute timeout would do. See the comment before * nlmclnt_lock for an explanation. */ - current->timeout = jiffies + 30 * HZ; + /* + * FIXME, can we be not interruptible and so be allowed to use + * a timeout here? -arca + */ +/* current->timeout = jiffies + 30 * HZ; */ sleep_on(&block.b_wait); for (head = &nlm_blocked; *head; head = &(*head)->b_next) { @@ -167,6 +172,7 @@ reclaimer(void *ptr) /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ + lock_kernel(); lockd_up(); /* First, reclaim all locks that have been granted previously. */ @@ -198,6 +204,7 @@ reclaimer(void *ptr) /* Release host handle after use */ nlm_release_host(host); lockd_down(); + unlock_kernel(); return 0; } diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index bbfdefc71..5f07b1c66 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -174,9 +174,9 @@ static inline int nlmclnt_grace_wait(struct nlm_host *host) { if (!host->h_reclaiming) - current->timeout = jiffies + 10 * HZ; - interruptible_sleep_on(&host->h_gracewait); - current->timeout = 0; + interruptible_sleep_on_timeout(&host->h_gracewait, 10*HZ); + else + interruptible_sleep_on(&host->h_gracewait); return signalled()? -ERESTARTSYS : 0; } @@ -194,10 +194,8 @@ nlmclnt_alloc_call(void) if (call) return call; printk("nlmclnt_alloc_call: failed, waiting for memory\n"); - current->timeout = jiffies + 5 * HZ; current->state = TASK_INTERRUPTIBLE; - schedule(); - current->timeout = 0; + schedule_timeout(5*HZ); } return NULL; } @@ -247,9 +245,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) } /* Back off a little and try again */ - current->timeout = jiffies + 15 * HZ; - interruptible_sleep_on(&host->h_gracewait); - current->timeout = 0; + interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ); } while (!signalled()); return -ERESTARTSYS; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 6bdcef4e8..49cb39c4d 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -46,7 +46,7 @@ static pid_t nlmsvc_pid = 0; unsigned long nlmsvc_grace_period = 0; unsigned long nlmsvc_timeout = 0; -static struct wait_queue * lockd_start = NULL; +static struct semaphore lockd_start = MUTEX_LOCKED; static struct wait_queue * lockd_exit = NULL; /* @@ -73,7 +73,7 @@ lockd(struct svc_rqst *rqstp) * Let our maker know we're running. */ nlmsvc_pid = current->pid; - wake_up(&lockd_start); + up(&lockd_start); exit_mm(current); current->session = 1; @@ -121,6 +121,7 @@ lockd(struct svc_rqst *rqstp) */ while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) { + long timeout = MAX_SCHEDULE_TIMEOUT; if (signalled()) { spin_lock_irq(¤t->sigmask_lock); flush_signals(current); @@ -134,7 +135,7 @@ lockd(struct svc_rqst *rqstp) * during grace period). */ if (!nlmsvc_grace_period) { - current->timeout = nlmsvc_retry_blocked(); + timeout = nlmsvc_retry_blocked(); } else if (nlmsvc_grace_period < jiffies) nlmsvc_grace_period = 0; @@ -142,7 +143,7 @@ lockd(struct svc_rqst *rqstp) * Find a socket with data available and call its * recvfrom routine. */ - if ((err = svc_recv(serv, rqstp)) == -EAGAIN) + if ((err = svc_recv(serv, rqstp, timeout)) == -EAGAIN) continue; if (err < 0) { if (err != -EINTR) @@ -250,7 +251,7 @@ lockd_up(void) "lockd_up: create thread failed, error=%d\n", error); goto destroy_and_out; } - sleep_on(&lockd_start); + down(&lockd_start); /* * Note: svc_serv structures have an initial use count of 1, @@ -291,9 +292,7 @@ lockd_down(void) * the lockd semaphore, we can't wait around forever ... */ current->sigpending = 0; - current->timeout = jiffies + HZ; - interruptible_sleep_on(&lockd_exit); - current->timeout = 0; + interruptible_sleep_on_timeout(&lockd_exit, HZ); if (nlmsvc_pid) { printk(KERN_WARNING "lockd_down: lockd failed to exit, clearing pid\n"); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index ac650b5f3..b1f1f5588 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -615,6 +615,7 @@ nlmsvc_retry_blocked(void) } if ((block = nlm_blocked) && block->b_when != NLM_NEVER) - return block->b_when; - return 0; + return (block->b_when - jiffies); + + return MAX_SCHEDULE_TIMEOUT; } diff --git a/fs/locks.c b/fs/locks.c index 3f4f9cd81..022206bbb 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -106,13 +106,7 @@ */ #include <linux/malloc.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/stat.h> -#include <linux/fcntl.h> #include <linux/file.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -192,7 +186,7 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but - * it seems seems like the reasonable thing to do. + * it seems like the reasonable thing to do. */ static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 49dde4e44..ded1ea371 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -249,7 +249,7 @@ struct inode * minix_new_inode(const struct inode * dir) return NULL; sb = dir->i_sb; inode->i_sb = sb; - inode->i_flags = inode->i_sb->s_flags; + inode->i_flags = 0; j = 8192; bh = NULL; for (i = 0; i < sb->u.minix_sb.s_imap_blocks; i++) { diff --git a/fs/minix/dir.c b/fs/minix/dir.c index dc3e63347..d7db754ff 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -55,7 +55,7 @@ struct inode_operations minix_dir_inode_operations = { NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ - minix_truncate, /* truncate */ + NULL, /* truncate */ NULL /* permission */ }; diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 44606d260..ef3d15db9 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -144,7 +144,7 @@ static int V1_sync_dindirect(struct inode *inode, unsigned short *diblock, return err; } -int V1_minix_sync_file(struct inode * inode, struct file * file) +static int V1_minix_sync_file(struct inode * inode, struct file * file) { int wait, err = 0; @@ -305,7 +305,7 @@ static int V2_sync_tindirect(struct inode *inode, unsigned long *tiblock, return err; } -int V2_minix_sync_file(struct inode * inode, struct file * file) +static int V2_minix_sync_file(struct inode * inode, struct file * file) { int wait, err = 0; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 46ae5c11c..e8b1e7660 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -26,6 +26,11 @@ #include <linux/minix_fs.h> +static void minix_read_inode(struct inode * inode); +static void minix_write_inode(struct inode * inode); +static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static int minix_remount (struct super_block * sb, int * flags, char * data); + static void minix_delete_inode(struct inode *inode) { inode->i_size = 0; @@ -40,7 +45,7 @@ static void minix_commit_super (struct super_block * sb, sb->s_dirt = 0; } -void minix_write_super (struct super_block * sb) +static void minix_write_super (struct super_block * sb) { struct minix_super_block * ms; @@ -55,7 +60,7 @@ void minix_write_super (struct super_block * sb) } -void minix_put_super(struct super_block *sb) +static void minix_put_super(struct super_block *sb) { int i; @@ -86,7 +91,7 @@ static struct super_operations minix_sops = { minix_remount }; -int minix_remount (struct super_block * sb, int * flags, char * data) +static int minix_remount (struct super_block * sb, int * flags, char * data) { struct minix_super_block * ms; @@ -162,7 +167,7 @@ static const char * minix_checkroot(struct super_block *s, struct inode *dir) return errmsg; } -struct super_block *minix_read_super(struct super_block *s, void *data, +static struct super_block *minix_read_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh; @@ -326,7 +331,7 @@ out_bad_sb: return NULL; } -int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -830,7 +835,7 @@ static void V2_minix_read_inode(struct inode * inode) /* * The global function to read an inode. */ -void minix_read_inode(struct inode * inode) +static void minix_read_inode(struct inode * inode) { if (INODE_VERSION(inode) == MINIX_V1) V1_minix_read_inode(inode); @@ -916,7 +921,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) return bh; } -struct buffer_head *minix_update_inode(struct inode *inode) +static struct buffer_head *minix_update_inode(struct inode *inode) { if (INODE_VERSION(inode) == MINIX_V1) return V1_minix_update_inode(inode); @@ -924,7 +929,7 @@ struct buffer_head *minix_update_inode(struct inode *inode) return V2_minix_update_inode(inode); } -void minix_write_inode(struct inode * inode) +static void minix_write_inode(struct inode * inode) { struct buffer_head *bh; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 17ff53aed..ad2211d25 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -88,7 +88,7 @@ static int minix_hash(struct dentry *dentry, struct qstr *qstr) { unsigned long hash; int i; - const char *name; + const unsigned char *name; i = dentry->d_inode->i_sb->u.minix_sb.s_namelen; if (i >= qstr->len) @@ -452,7 +452,7 @@ int minix_rmdir(struct inode * dir, struct dentry *dentry) retval = -ENOENT; goto end_rmdir; } - if (dentry->d_count > 1) { + if (!list_empty(&dentry->d_hash)) { retval = -EBUSY; goto end_rmdir; } @@ -490,8 +490,6 @@ repeat: inode = dentry->d_inode; retval = -EPERM; - if (S_ISDIR(inode->i_mode)) - goto end_unlink; if (de->inode != inode->i_ino) { brelse(bh); current->counter = 0; diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c index cdc237235..4f3661105 100644 --- a/fs/minix/symlink.c +++ b/fs/minix/symlink.c @@ -15,7 +15,7 @@ #include <asm/uaccess.h> static int minix_readlink(struct dentry *, char *, int); -static struct dentry *minix_follow_link(struct dentry *, struct dentry *); +static struct dentry *minix_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -41,7 +41,8 @@ struct inode_operations minix_symlink_inode_operations = { }; static struct dentry * minix_follow_link(struct dentry * dentry, - struct dentry * base) + struct dentry * base, + unsigned int follow) { struct inode *inode = dentry->d_inode; struct buffer_head * bh; @@ -52,7 +53,7 @@ static struct dentry * minix_follow_link(struct dentry * dentry, return ERR_PTR(-EIO); } UPDATE_ATIME(inode); - base = lookup_dentry(bh->b_data, base, 1); + base = lookup_dentry(bh->b_data, base, follow); brelse(bh); return base; } diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 0f0afa604..a94806fdf 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -176,7 +176,7 @@ repeat: return retry; } -void V1_minix_truncate(struct inode * inode) +static void V1_minix_truncate(struct inode * inode) { int retry; diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 87c62e4c9..7d18237e7 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -5,17 +5,15 @@ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> */ -#include <linux/config.h> #define __NO_VERSION__ +#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/msdos_fs.h> -#include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> -#include <linux/stat.h> #include <asm/uaccess.h> @@ -149,10 +147,10 @@ static int msdos_format_name(char conv,const char *name,int len, static int msdos_find(struct inode *dir,const char *name,int len, struct buffer_head **bh,struct msdos_dir_entry **de,int *ino) { - char msdos_name[MSDOS_NAME]; int res; char dotsOK; char scantype; + char msdos_name[MSDOS_NAME]; dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK; res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, @@ -225,7 +223,9 @@ static struct dentry_operations msdos_dentry_operations = { NULL, /* d_revalidate */ msdos_hash, msdos_cmp, - NULL /* d_delete */ + NULL, /* d_delete */ + NULL, + NULL }; struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) @@ -253,49 +253,46 @@ out_fail: int msdos_lookup(struct inode *dir,struct dentry *dentry) { struct super_block *sb = dir->i_sb; - int ino,res; + struct inode *inode = NULL; struct msdos_dir_entry *de; struct buffer_head *bh; - struct inode *inode; + int ino,res; PRINTK (("msdos_lookup\n")); dentry->d_op = &msdos_dentry_operations; - if(!dir) { /* N.B. This test is bogus -- should never happen */ - d_add(dentry, NULL); - return 0; - } + res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh, + &de, &ino); - if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) { - if(res == -ENOENT) { - d_add(dentry, NULL); - res = 0; - } - return res; - } - PRINTK (("msdos_lookup 4\n")); + if (res == -ENOENT) + goto add; + if (res < 0) + goto out; if (bh) fat_brelse(sb, bh); - PRINTK (("msdos_lookup 4.5\n")); - if (!(inode = iget(dir->i_sb,ino))) - return -EACCES; - PRINTK (("msdos_lookup 5\n")); + + /* try to get the inode */ + res = -EACCES; + inode = iget(sb, ino); + if (!inode) + goto out; if (!inode->i_sb || - (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { - /* crossed a mount point into a non-msdos fs */ - d_add(dentry, inode); - return 0; + (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { + printk(KERN_WARNING "msdos_lookup: foreign inode??\n"); } - if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */ + /* mkdir in progress? */ + if (MSDOS_I(inode)->i_busy) { + printk(KERN_WARNING "msdos_lookup: %s/%s busy\n", + dentry->d_parent->d_name.name, dentry->d_name.name); iput(inode); - d_add(dentry, NULL); /* N.B. Do we really want a negative? */ - return 0; + goto out; } - PRINTK (("msdos_lookup 6\n")); +add: d_add(dentry, inode); - PRINTK (("msdos_lookup 7\n")); - return 0; + res = 0; +out: + return res; } @@ -308,9 +305,6 @@ static int msdos_create_entry(struct inode *dir, const char *name, struct msdos_dir_entry *de; int res,ino; - if(!dir) - return -ENOENT; - *result = NULL; if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) { if (res != -ENOENT) return res; @@ -350,14 +344,14 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode) struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode; - char msdos_name[MSDOS_NAME]; int ino,res,is_hid; + char msdos_name[MSDOS_NAME]; - if (!dir) return -ENOENT; - if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) + res = msdos_format_name(MSDOS_SB(sb)->options.name_check, + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(sb)->options.dotsOK); + if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); @@ -434,8 +428,8 @@ static int msdos_empty(struct inode *dir) /***** Remove a directory */ int msdos_rmdir(struct inode *dir, struct dentry *dentry) { - struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; + struct inode *inode = dentry->d_inode; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; @@ -451,15 +445,14 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) if (dir->i_dev != inode->i_dev || dir == inode) printk("msdos_rmdir: impossible condition\n"); /* - * Prune any child dentries, then verify that - * the directory is empty and not in use. + * Check whether the directory is empty, then prune + * any child dentries and make sure it's not in use. */ - shrink_dcache_parent(dentry); res = msdos_empty(inode); if (res) goto rmdir_done; res = -EBUSY; - if (dentry->d_count > 1) { + if (!list_empty(&dentry->d_hash)) { #ifdef MSDOS_DEBUG printk("msdos_rmdir: %s/%s busy, d_count=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); @@ -481,6 +474,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); res = 0; + rmdir_done: fat_brelse(sb, bh); return res; @@ -493,28 +487,32 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode,*dot; - char msdos_name[MSDOS_NAME]; int ino,res,is_hid; + char msdos_name[MSDOS_NAME]; - if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) + res = msdos_format_name(MSDOS_SB(sb)->options.name_check, + dentry->d_name.name,dentry->d_name.len, + msdos_name,0, + MSDOS_SB(sb)->options.dotsOK); + if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); fat_lock_creation(); - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) { - fat_unlock_creation(); - /* N.B. does this need to be released on the other path? */ - fat_brelse(sb, bh); - return -EEXIST; - } + if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) + goto out_exist; + res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode); if (res < 0) goto out_unlock; + dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ + /* + * Instantiate the dentry now, in case we need to cleanup. + */ + d_instantiate(dentry, inode); + if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) @@ -525,9 +523,9 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) dot->i_nlink = inode->i_nlink; mark_inode_dirty(dot); iput(dot); + if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0) goto mkdir_error; - fat_unlock_creation(); dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; @@ -535,21 +533,26 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); - d_instantiate(dentry, inode); - return 0; -mkdir_error: - if (msdos_rmdir(dir,dentry) < 0) - fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); + res = 0; + out_unlock: fat_unlock_creation(); return res; + +mkdir_error: + printk("msdos_mkdir: error=%d, attempting cleanup\n", res); + if (msdos_rmdir(dir,dentry) < 0) + fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); + goto out_unlock; + +out_exist: + fat_brelse(sb, bh); + res = -EEXIST; + goto out_unlock; } /***** Unlink a file */ -static int msdos_unlinkx( - struct inode *dir, - struct dentry *dentry, - int nospc) /* Flag special file ? */ +static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; @@ -558,22 +561,25 @@ static int msdos_unlinkx( struct msdos_dir_entry *de; bh = NULL; - if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, - &bh,&de,&ino)) < 0) + res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, + &bh, &de, &ino); + if (res < 0) goto unlink_done; res = -EPERM; if (!S_ISREG(inode->i_mode) && nospc) goto unlink_done; if (IS_IMMUTABLE(inode)) goto unlink_done; + /* N.B. check for busy files? */ + inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; MSDOS_I(inode)->i_busy = 1; mark_inode_dirty(inode); mark_inode_dirty(dir); + d_delete(dentry); /* This also frees the inode */ de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); - d_delete(dentry); /* This also frees the inode */ res = 0; unlink_done: fat_brelse(sb, bh); @@ -592,28 +598,40 @@ int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry) return msdos_unlinkx (dir,dentry,0); } +#define MSDOS_CHECK_BUSY 1 + /***** Rename within a directory */ -static int rename_same_dir(struct inode *old_dir,char *old_name, +static int msdos_rename_same(struct inode *old_dir,char *old_name, struct dentry *old_dentry, struct inode *new_dir,char *new_name,struct dentry *new_dentry, struct buffer_head *old_bh, - struct msdos_dir_entry *old_de,int old_ino,int is_hid) + struct msdos_dir_entry *old_de, int old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh; struct msdos_dir_entry *new_de; struct inode *new_inode,*old_inode; - int new_ino,exists,error; + int new_ino, exists, error; + + if (!strncmp(old_name, new_name, MSDOS_NAME)) + goto set_hid; + error = -ENOENT; + if (*(unsigned char *) old_de->name == DELETED_FLAG) + goto out; - if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid; exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - if (*(unsigned char *) old_de->name == DELETED_FLAG) { - if (exists) - fat_brelse(sb, new_bh); - return -ENOENT; - } if (exists) { + error = -EIO; new_inode = new_dentry->d_inode; + /* Make sure it really exists ... */ + if (!new_inode) { + printk(KERN_ERR + "msdos_rename_same: %s/%s inode NULL, ino=%d\n", + new_dentry->d_parent->d_name.name, + new_dentry->d_name.name, new_ino); + d_drop(new_dentry); + goto out_error; + } error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -621,23 +639,46 @@ static int rename_same_dir(struct inode *old_dir,char *old_name, : (old_de->attr & ATTR_DIR) ? -EPERM : 0; - if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; - if (error) { - fat_brelse(sb, new_bh); - return error; - } + if (error) + goto out_error; + error = -EPERM; + if ((old_de->attr & ATTR_SYS)) + goto out_error; + if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_error; +#ifdef MSDOS_CHECK_BUSY + /* check for a busy dentry */ + error = -EBUSY; + shrink_dcache_parent(new_dentry); + if (new_dentry->d_count > 1) { +printk("msdos_rename_same: %s/%s busy, count=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_count); + goto out_error; + } +#endif new_dir->i_nlink--; mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); + new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); fat_brelse(sb, new_bh); } - memcpy(old_de->name,new_name,MSDOS_NAME); + memcpy(old_de->name, new_name, MSDOS_NAME); /* Update the dcache */ d_move(old_dentry, new_dentry); set_hid: @@ -650,51 +691,63 @@ set_hid: MSDOS_I(old_inode)->i_attrs = is_hid ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); - return 0; + error = 0; +out: + return error; + +out_error: + fat_brelse(sb, new_bh); + goto out; } /***** Rename across directories - a nonphysical move */ -static int rename_diff_dir(struct inode *old_dir,char *old_name, +static int msdos_rename_diff(struct inode *old_dir, char *old_name, struct dentry *old_dentry, - struct inode *new_dir,char *new_name,struct dentry *new_dentry, + struct inode *new_dir,char *new_name, struct dentry *new_dentry, struct buffer_head *old_bh, - struct msdos_dir_entry *old_de,int old_ino,int is_hid) + struct msdos_dir_entry *old_de, int old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode; - struct dentry *walk; int new_ino,free_ino,dotdot_ino; - int error,exists; + int error, exists; - if (old_dir->i_dev != new_dir->i_dev) return -EINVAL; - if (old_ino == new_dir->i_ino) return -EINVAL; - walk = new_dentry; + error = -EINVAL; + if (old_ino == new_dir->i_ino) + goto out; /* prevent moving directory below itself */ - for (;;) { - if (walk == old_dentry) return -EINVAL; - if (walk == walk->d_parent) break; - walk = walk->d_parent; - } + if (is_subdir(new_dentry, old_dentry)) + goto out; + + error = -ENOENT; + if (*(unsigned char *) old_de->name == DELETED_FLAG) + goto out; + /* find free spot */ - while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino, - SCAN_ANY)) < 0) { - if (error != -ENOENT) return error; + while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, &free_ino, + SCAN_ANY)) < 0) { + if (error != -ENOENT) + goto out; error = fat_add_cluster(new_dir); - if (error) return error; + if (error) + goto out; } + exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - old_inode = old_dentry->d_inode; - if (*(unsigned char *) old_de->name == DELETED_FLAG) { - fat_brelse(sb, free_bh); - if (exists) - fat_brelse(sb, new_bh); - return -ENOENT; - } - new_inode = NULL; /* to make GCC happy */ if (exists) { /* Trash the old file! */ + error = -EIO; new_inode = new_dentry->d_inode; + /* Make sure it really exists ... */ + if (!new_inode) { + printk(KERN_ERR + "msdos_rename_diff: %s/%s inode NULL, ino=%d\n", + new_dentry->d_parent->d_name.name, + new_dentry->d_name.name, new_ino); + d_drop(new_dentry); + goto out_new; + } error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? msdos_empty(new_inode) @@ -702,40 +755,107 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, : (old_de->attr & ATTR_DIR) ? -EPERM : 0; - if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM; - if (error) { - fat_brelse(sb, new_bh); - return error; + if (error) + goto out_new; + error = -EPERM; + if ((old_de->attr & ATTR_SYS)) + goto out_new; + +#ifdef MSDOS_CHECK_BUSY + /* check for a busy dentry */ + error = -EBUSY; + if (new_dentry->d_count > 1) { + shrink_dcache_parent(new_dentry); + if (new_dentry->d_count > 1) { +printk("msdos_rename_diff: target %s/%s busy, count=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_count); + goto out_new; + } + } +#endif + if (S_ISDIR(new_inode->i_mode)) { + /* make sure it's empty */ + error = msdos_empty(new_inode); + if (error) + goto out_new; + new_dir->i_nlink--; + mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; mark_inode_dirty(new_inode); + /* + * Make it negative if it's not busy; + * otherwise let d_move() drop it. + */ + if (new_dentry->d_count == 1) + d_delete(new_dentry); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); + fat_brelse(sb, new_bh); } - memcpy(free_de,old_de,sizeof(struct msdos_dir_entry)); - memcpy(free_de->name,new_name,MSDOS_NAME); + + old_inode = old_dentry->d_inode; + /* Get the dotdot inode if we'll need it ... */ + dotdot_bh = NULL; + dotdot_inode = NULL; + if (S_ISDIR(old_inode->i_mode)) { + error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, + &dotdot_de, &dotdot_ino, SCAN_ANY); + if (error < 0) { + printk(KERN_WARNING + "MSDOS: %s/%s, get dotdot failed, ret=%d\n", + old_dentry->d_parent->d_name.name, + old_dentry->d_name.name, error); + goto rename_done; + } + error = -EIO; + dotdot_inode = iget(sb, dotdot_ino); + if (!dotdot_inode) + goto out_dotdot; + } + + /* get an inode for the new name */ + memcpy(free_de, old_de, sizeof(struct msdos_dir_entry)); + memcpy(free_de->name, new_name, MSDOS_NAME); free_de->attr = is_hid ? (free_de->attr|ATTR_HIDDEN) : (free_de->attr&~ATTR_HIDDEN); - if (!(free_inode = iget(new_dir->i_sb,free_ino))) { - free_de->name[0] = DELETED_FLAG; - /* - * Don't mark free_bh as dirty. Both states - * are supposed to be equivalent. - */ - fat_brelse(sb, free_bh); - if (exists) - fat_brelse(sb, new_bh); - return -EIO; - } - if (exists && S_ISDIR(new_inode->i_mode)) { - new_dir->i_nlink--; - mark_inode_dirty(new_dir); - } + + error = -EIO; + free_inode = iget(sb, free_ino); + if (!free_inode) + goto out_iput; + /* make sure it's not busy! */ + if (MSDOS_I(free_inode)->i_busy) + printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n", + (ino_t) free_ino); + if (!list_empty(&free_inode->i_dentry)) + printk("msdos_rename_diff: free inode has aliases??\n"); msdos_read_inode(free_inode); + /* + * Make sure the old dentry isn't busy, + * as we need to change inodes ... + */ + error = -EBUSY; + if (old_dentry->d_count > 1) { + shrink_dcache_parent(old_dentry); + if (old_dentry->d_count > 1) { +printk("msdos_rename_diff: source %s/%s busy, count=%d\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, +old_dentry->d_count); + goto out_iput; + } + } + + /* keep the inode for a bit ... */ + old_inode->i_count++; + d_delete(old_dentry); + free_inode->i_mode = old_inode->i_mode; + free_inode->i_nlink = old_inode->i_nlink; free_inode->i_size = old_inode->i_size; free_inode->i_blocks = old_inode->i_blocks; free_inode->i_mtime = old_inode->i_mtime; @@ -747,49 +867,62 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart; MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs; - /* Detach d_alias from old inode and attach to new inode */ - list_del(&old_dentry->d_alias); + /* release the old inode's resources */ + MSDOS_I(old_inode)->i_start = 0; + MSDOS_I(old_inode)->i_logstart = 0; + old_inode->i_nlink = 0; + + /* + * Install the new inode ... + */ d_instantiate(old_dentry, free_inode); - iput(old_inode); + fat_mark_buffer_dirty(sb, free_bh, 1); fat_cache_inval_inode(old_inode); mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); - fat_mark_buffer_dirty(sb, free_bh, 1); + iput(old_inode); - if (exists) { - /* free_inode is put after putting new_inode and old_inode */ - fat_brelse(sb, new_bh); - } - if (S_ISDIR(old_inode->i_mode)) { - if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, - &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done; - if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) { - fat_brelse(sb, dotdot_bh); - error = -EIO; - goto rename_done; - } + /* a directory? */ + if (dotdot_bh) { MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); - mark_inode_dirty(dotdot_inode); - fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; /* no need to mark them dirty */ dotdot_inode->i_nlink = new_dir->i_nlink; + mark_inode_dirty(dotdot_inode); iput(dotdot_inode); + fat_mark_buffer_dirty(sb, dotdot_bh, 1); fat_brelse(sb, dotdot_bh); } /* Update the dcache */ d_move(old_dentry, new_dentry); error = 0; + rename_done: fat_brelse(sb, free_bh); +out: return error; + +out_iput: + free_de->name[0] = DELETED_FLAG; + /* + * Don't mark free_bh as dirty. Both states + * are supposed to be equivalent. + */ + iput(free_inode); /* may be NULL */ + iput(dotdot_inode); +out_dotdot: + fat_brelse(sb, dotdot_bh); + goto rename_done; +out_new: + fat_brelse(sb, new_bh); + goto rename_done; } /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */ @@ -797,36 +930,45 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, struct inode *new_dir,struct dentry *new_dentry) { struct super_block *sb = old_dir->i_sb; - char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME]; struct buffer_head *old_bh; struct msdos_dir_entry *old_de; - int old_ino,error; + int old_ino, error; int is_hid,old_hid; /* if new file and old file are hidden */ - - if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check, - old_dentry->d_name.name, - old_dentry->d_name.len,old_msdos_name,1, - MSDOS_SB(old_dir->i_sb)->options.dotsOK)) - < 0) goto rename_done; - if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check, - new_dentry->d_name.name, - new_dentry->d_name.len,new_msdos_name,0, - MSDOS_SB(new_dir->i_sb)->options.dotsOK)) - < 0) goto rename_done; - is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); + char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; + + error = -EINVAL; + if (sb != new_dir->i_sb) + goto rename_done; + error = msdos_format_name(MSDOS_SB(sb)->options.name_check, + old_dentry->d_name.name, old_dentry->d_name.len, + old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK); + if (error < 0) + goto rename_done; + error = msdos_format_name(MSDOS_SB(sb)->options.name_check, + new_dentry->d_name.name, new_dentry->d_name.len, + new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK); + if (error < 0) + goto rename_done; + + is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.'); - if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de, - &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done; + error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, + &old_ino, old_hid?SCAN_HID:SCAN_NOTHID); + if (error < 0) + goto rename_done; + fat_lock_creation(); if (old_dir == new_dir) - error = rename_same_dir(old_dir,old_msdos_name,old_dentry, - new_dir,new_msdos_name,new_dentry, - old_bh,old_de,old_ino,is_hid); - else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry, - new_dir,new_msdos_name,new_dentry, - old_bh,old_de,old_ino,is_hid); + error = msdos_rename_same(old_dir, old_msdos_name, old_dentry, + new_dir, new_msdos_name, new_dentry, + old_bh, old_de, (ino_t)old_ino, is_hid); + else + error = msdos_rename_diff(old_dir, old_msdos_name, old_dentry, + new_dir, new_msdos_name, new_dentry, + old_bh, old_de, (ino_t)old_ino, is_hid); fat_unlock_creation(); fat_brelse(sb, old_bh); + rename_done: return error; } @@ -848,7 +990,7 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ diff --git a/fs/namei.c b/fs/namei.c index 6d1ed898f..84208808a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -12,22 +12,29 @@ * lookup logic. */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/stat.h> #include <linux/mm.h> #include <linux/proc_fs.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> #include <asm/uaccess.h> #include <asm/unaligned.h> #include <asm/semaphore.h> -#include <asm/spinlock.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +/* + * The bitmask for a lookup event: + * - follow links at the end + * - require a directory + * - ending slashes ok even for nonexistent files + * - internal "there are more path compnents" flag + */ +#define LOOKUP_FOLLOW (1) +#define LOOKUP_DIRECTORY (2) +#define LOOKUP_SLASHOK (4) +#define LOOKUP_CONTINUE (8) + #include <asm/namei.h> /* This can be removed after the beta phase. */ @@ -84,6 +91,8 @@ /* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink * semantics. See the comments in "open_namei" and "do_link" below. + * + * [10-Sep-98 Alan Modra] Another symlink change. */ static inline char * get_page(void) @@ -278,7 +287,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) return result; } -static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry) +static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow) { struct inode * inode = dentry->d_inode; @@ -288,7 +297,7 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry current->link_count++; /* This eats the base */ - result = inode->i_op->follow_link(dentry, base); + result = inode->i_op->follow_link(dentry, base, follow); current->link_count--; dput(dentry); return result; @@ -318,17 +327,19 @@ static inline struct dentry * follow_mount(struct dentry * dentry) * This is the basic name resolution function, turning a pathname * into the final dentry. */ -struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link) +struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags) { struct dentry * dentry; + struct inode *inode; if (*name == '/') { if (base) dput(base); - base = dget(current->fs->root); do { name++; } while (*name == '/'); + __prefix_lookup_dentry(name, lookup_flags); + base = dget(current->fs->root); } else if (!base) { base = dget(current->fs->pwd); } @@ -336,22 +347,16 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo if (!*name) goto return_base; + inode = base->d_inode; + lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK; + /* At this point we know we have a real path component. */ for(;;) { int err; unsigned long hash; struct qstr this; - struct inode *inode; - char c, follow; - - dentry = ERR_PTR(-ENOENT); - inode = base->d_inode; - if (!inode) - break; - - dentry = ERR_PTR(-ENOTDIR); - if (!inode->i_op || !inode->i_op->lookup) - break; + unsigned int flags; + unsigned int c; err = permission(inode, MAY_EXEC); dentry = ERR_PTR(err); @@ -359,23 +364,28 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo break; this.name = name; - c = *name; + c = *(const unsigned char *)name; hash = init_name_hash(); do { + name++; hash = partial_name_hash(c, hash); - c = *++name; + c = *(const unsigned char *)name; } while (c && (c != '/')); this.len = name - (const char *) this.name; this.hash = end_name_hash(hash); /* remove trailing slashes? */ - follow = follow_link; + flags = lookup_flags; if (c) { - follow |= c; + char tmp; + + flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; do { - c = *++name; - } while (c == '/'); + tmp = *++name; + } while (tmp == '/'); + if (tmp) + flags |= LOOKUP_CONTINUE; } /* @@ -405,15 +415,46 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo /* Check mountpoints.. */ dentry = follow_mount(dentry); - if (!follow) + if (!(flags & LOOKUP_FOLLOW)) break; - base = do_follow_link(base, dentry); - if (c && !IS_ERR(base)) - continue; + base = do_follow_link(base, dentry, flags); + if (IS_ERR(base)) + goto return_base; + inode = base->d_inode; + if (flags & LOOKUP_DIRECTORY) { + if (!inode) + goto no_inode; + dentry = ERR_PTR(-ENOTDIR); + if (!inode->i_op || !inode->i_op->lookup) + break; + if (flags & LOOKUP_CONTINUE) + continue; + } return_base: return base; +/* + * The case of a nonexisting file is special. + * + * In the middle of a pathname lookup (ie when + * LOOKUP_CONTINUE is set), it's an obvious + * error and returns ENOENT. + * + * At the end of a pathname lookup it's legal, + * and we return a negative dentry. However, we + * get here only if there were trailing slashes, + * which is legal only if we know it's supposed + * to be a directory (ie "mkdir"). Thus the + * LOOKUP_SLASHOK flag. + */ +no_inode: + dentry = ERR_PTR(-ENOENT); + if (flags & LOOKUP_CONTINUE) + break; + if (flags & LOOKUP_SLASHOK) + goto return_base; + break; } dput(base); return dentry; @@ -429,7 +470,7 @@ return_base: * namei exists in two versions: namei/lnamei. The only difference is * that namei follows links, while lnamei does not. */ -struct dentry * __namei(const char *pathname, int follow_link) +struct dentry * __namei(const char *pathname, unsigned int lookup_flags) { char *name; struct dentry *dentry; @@ -437,7 +478,7 @@ struct dentry * __namei(const char *pathname, int follow_link) name = getname(pathname); dentry = (struct dentry *) name; if (!IS_ERR(name)) { - dentry = lookup_dentry(name, NULL, follow_link); + dentry = lookup_dentry(name, NULL, lookup_flags); putname(name); if (!IS_ERR(dentry)) { if (!dentry->d_inode) { @@ -488,6 +529,28 @@ static inline struct dentry *lock_parent(struct dentry *dentry) return dir; } +/* + * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security + * reasons. + * + * O_DIRECTORY translates into forcing a directory lookup. + */ +static inline int lookup_flags(unsigned int f) +{ + unsigned long retval = LOOKUP_FOLLOW; + + if (f & O_NOFOLLOW) + retval &= ~LOOKUP_FOLLOW; + + if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) + retval &= ~LOOKUP_FOLLOW; + + if (f & O_DIRECTORY) + retval |= LOOKUP_DIRECTORY; + + return retval; +} + /* * open_namei() * @@ -510,13 +573,7 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; - /* - * Special case: O_CREAT|O_EXCL on a dangling symlink should - * give EEXIST for security reasons. While inconsistent, this - * is the same scheme used by, for example, Solaris 2.5.1. --KAB - */ - dentry = lookup_dentry(pathname, NULL, - (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL)); + dentry = lookup_dentry(pathname, NULL, lookup_flags(flag)); if (IS_ERR(dentry)) return dentry; @@ -563,6 +620,10 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) if (!inode) goto exit; + error = -ELOOP; + if (S_ISLNK(inode->i_mode)) + goto exit; + error = -EISDIR; if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) goto exit; @@ -635,7 +696,7 @@ struct dentry * do_mknod(const char * filename, int mode, dev_t dev) struct dentry *dentry, *retval; mode &= ~current->fs->umask; - dentry = lookup_dentry(filename, NULL, 1); + dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW); if (IS_ERR(dentry)) return dentry; @@ -719,7 +780,7 @@ static inline int do_mkdir(const char * pathname, int mode) struct dentry *dir; struct dentry *dentry; - dentry = lookup_dentry(pathname, NULL, 0); + dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit; @@ -773,6 +834,37 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) return error; } +/* + * Whee.. Deadlock country. Happily there are only two VFS + * operations that do this.. + */ +static inline void double_lock(struct dentry *d1, struct dentry *d2) +{ + struct semaphore *s1 = &d1->d_inode->i_sem; + struct semaphore *s2 = &d2->d_inode->i_sem; + + if (s1 != s2) { + if ((unsigned long) s1 < (unsigned long) s2) { + struct semaphore *tmp = s2; + s2 = s1; s1 = tmp; + } + down(s1); + } + down(s2); +} + +static inline void double_unlock(struct dentry *d1, struct dentry *d2) +{ + struct semaphore *s1 = &d1->d_inode->i_sem; + struct semaphore *s2 = &d2->d_inode->i_sem; + + up(s1); + if (s1 != s2) + up(s2); + dput(d1); + dput(d2); +} + static inline int do_rmdir(const char * name) { int error; @@ -784,13 +876,20 @@ static inline int do_rmdir(const char * name) if (IS_ERR(dentry)) goto exit; - dir = lock_parent(dentry); - error = PTR_ERR(dir); - if (IS_ERR(dir)) - goto exit_dput; + dir = dget(dentry->d_parent); error = -ENOENT; if (!dentry->d_inode) + goto exit; + /* + * The dentry->d_count stuff confuses d_delete() enough to + * not kill the inode from under us while it is locked. This + * wouldn't be needed, except the dentry semaphore is really + * in the inode, not in the dentry.. + */ + dentry->d_count++; + double_lock(dir, dentry); + if (dentry->d_parent != dir) goto exit_lock; error = -EROFS; @@ -819,15 +918,35 @@ static inline int do_rmdir(const char * name) DQUOT_INIT(dir->d_inode); - if (dentry->d_count > 1) + /* + * We try to drop the dentry early: we should have + * a usage count of 2 if we're the only user of this + * dentry, and if that is true (possibly after pruning + * the dcache), then we drop the dentry now. + * + * A low-level filesystem can, if it choses, legally + * do a + * + * if (!list_empty(&dentry->d_hash)) + * return -EBUSY; + * + * if it cannot handle the case of removing a directory + * that is still in use by something else.. + */ + switch (dentry->d_count) { + default: shrink_dcache_parent(dentry); + if (dentry->d_count != 2) + break; + case 2: + d_drop(dentry); + } error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry); exit_lock: - unlock_dir(dir); -exit_dput: - dput(dentry); + dentry->d_count--; + double_unlock(dentry, dir); exit: return error; } @@ -882,13 +1001,16 @@ static inline int do_unlink(const char * name) goto exit_lock; /* + * A directory can't be unlink'ed. * A file cannot be removed from an append-only directory. */ error = -EPERM; + if (S_ISDIR(dentry->d_inode->i_mode)) + goto exit_lock; + if (IS_APPEND(dir->d_inode)) goto exit_lock; - error = -EPERM; if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink) goto exit_lock; @@ -992,20 +1114,20 @@ static inline int do_link(const char * oldname, const char * newname) struct inode *inode; int error; - old_dentry = lookup_dentry(oldname, NULL, 1); - error = PTR_ERR(old_dentry); - if (IS_ERR(old_dentry)) - goto exit; - /* * Hardlinks are often used in delicate situations. We avoid * security-related surprises by not following symlinks on the - * newname. We *do* follow them on the oldname. This is - * the same as Digital Unix 4.0, for example. + * newname. --KAB * - * Solaris 2.5.1 is similar, but for a laugh try linking from - * a dangling symlink. --KAB + * We don't follow them on the oldname either to be compatible + * with linux 2.0, and to avoid hard-linking to directories + * and other special files. --ADM */ + old_dentry = lookup_dentry(oldname, NULL, 0); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit; + new_dentry = lookup_dentry(newname, NULL, 0); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) @@ -1083,37 +1205,6 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } -/* - * Whee.. Deadlock country. Happily there is only one VFS - * operation that does this.. - */ -static inline void double_lock(struct dentry *d1, struct dentry *d2) -{ - struct semaphore *s1 = &d1->d_inode->i_sem; - struct semaphore *s2 = &d2->d_inode->i_sem; - - if (s1 != s2) { - if ((unsigned long) s1 < (unsigned long) s2) { - struct semaphore *tmp = s2; - s2 = s1; s1 = tmp; - } - down(s1); - } - down(s2); -} - -static inline void double_unlock(struct dentry *d1, struct dentry *d2) -{ - struct semaphore *s1 = &d1->d_inode->i_sem; - struct semaphore *s2 = &d2->d_inode->i_sem; - - up(s1); - if (s1 != s2) - up(s2); - dput(d1); - dput(d2); -} - static inline int do_rename(const char * oldname, const char * newname) { int error; @@ -1126,7 +1217,16 @@ static inline int do_rename(const char * oldname, const char * newname) if (IS_ERR(old_dentry)) goto exit; - new_dentry = lookup_dentry(newname, NULL, 0); + error = -ENOENT; + if (!old_dentry->d_inode) + goto exit_old; + + { + unsigned int flags = 0; + if (S_ISDIR(old_dentry->d_inode->i_mode)) + flags = LOOKUP_SLASHOK; + new_dentry = lookup_dentry(newname, NULL, flags); + } error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) @@ -1137,10 +1237,6 @@ static inline int do_rename(const char * oldname, const char * newname) double_lock(new_dir, old_dir); - error = -ENOENT; - if (!old_dentry->d_inode) - goto exit_lock; - error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC); if (error) goto exit_lock; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index caa2dc261..9ef53c41a 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -24,11 +24,6 @@ #include <linux/ncp_fs.h> #include "ncplib_kernel.h" -#ifndef shrink_dcache_parent -#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb) -#endif - - struct ncp_dirent { struct nw_info_struct i; struct nw_search_sequence s; /* given back for i */ @@ -298,7 +293,8 @@ leave_me: #ifdef CONFIG_NCPFS_STRONG static int ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name, - struct inode *new_dir, struct dentry* new_dentry, char *_new_name) + struct inode *new_dir, struct dentry* new_dentry, char *_new_name, + int *done_flag) { int res=0x90,res2; struct iattr ia; @@ -321,19 +317,34 @@ ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_na old_dir, _old_name, new_dir, _new_name); + if (!res) { + ncp_invalid_dir_cache(old_dir); + ncp_invalid_dir_cache(new_dir); + d_move(old_dentry,new_dentry); + *done_flag=1; + + if (!old_dentry->d_inode) { + DPRINTK(KERN_INFO "ncpfs: no inode -- file remains rw\n"); + goto leave_me; + } + if ((res2=ncp_lookup_validate(old_dentry))) { + DPRINTK(KERN_DEBUG "ncpfs: ncp_lookup_validate returned %d\n",res2); + } + } + memset(&ia,0,sizeof(struct iattr)); ia.ia_mode = old_dentry->d_inode->i_mode; ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */ ia.ia_valid = ATTR_MODE; - /* FIXME: uses only inode info, no dentry info... so it is safe to call */ - /* it now with old dentry. If we use name (in future), we have to move */ - /* it after dentry_move in caller */ + DPRINTK(KERN_INFO "calling ncp_notify_change() with %s/%s\n", + old_dentry->d_parent->d_name.name,old_dentry->d_name.name); + res2=ncp_notify_change(old_dentry, &ia); if (res2) { printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2); - goto leave_me; + /* goto leave_me; */ } leave_me: @@ -755,7 +766,7 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry) int len = dentry->d_name.len; struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; - + error = -ENOENT; if (!dir || !S_ISDIR(dir->i_mode)) { printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n"); @@ -983,17 +994,14 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n"); goto out; } + error = -EIO; if (!ncp_conn_valid(NCP_SERVER(dir))) goto out; - if (dentry->d_count > 1) - { - shrink_dcache_parent(dentry); - error = -EBUSY; - if (dentry->d_count > 1) - goto out; - } + error = -EBUSY; + if (!list_empty(&dentry->d_hash)) + goto out; strncpy(_name, dentry->d_name.name, dentry->d_name.len); _name[dentry->d_name.len] = '\0'; @@ -1007,7 +1015,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) if (!result) { ncp_invalid_dir_cache(dir); - d_delete(dentry); error = 0; } out: @@ -1067,7 +1074,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, { int old_len = old_dentry->d_name.len; int new_len = new_dentry->d_name.len; - int error; + int error, done_flag=0; char _old_name[old_dentry->d_name.len + 1]; char _new_name[new_dentry->d_name.len + 1]; @@ -1105,16 +1112,21 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, new_dir, _new_name); #ifdef CONFIG_NCPFS_STRONG if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */ - error = ncp_force_rename(old_dir, old_dentry, _old_name, new_dir, new_dentry, _new_name); + error = ncp_force_rename(old_dir, old_dentry, _old_name, + new_dir, new_dentry, _new_name, + &done_flag); } #endif if (error == 0) { - DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", - old_dentry->d_name.name,new_dentry->d_name.name); - ncp_invalid_dir_cache(old_dir); - ncp_invalid_dir_cache(new_dir); - d_move(old_dentry,new_dentry); + if (done_flag == 0) /* if 1, the following already happened */ + { /* in ncp_force_rename() */ + DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", + old_dentry->d_name.name,new_dentry->d_name.name); + ncp_invalid_dir_cache(old_dir); + ncp_invalid_dir_cache(new_dir); + d_move(old_dentry,new_dentry); + } } else { if (error == 0x9E) error = -ENAMETOOLONG; diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 34f51d23a..fc0bbf013 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -300,6 +300,7 @@ out: return result; } +#ifdef CONFIG_NCPFS_NFS_NS static int ncp_obtain_DOS_dir_base(struct ncp_server *server, __u8 volnum, __u32 dirent, @@ -323,6 +324,7 @@ ncp_obtain_DOS_dir_base(struct ncp_server *server, ncp_unlock_server(server); return result; } +#endif /* CONFIG_NCPFS_NFS_NS */ static inline int ncp_get_known_namespace(struct ncp_server *server, __u8 volume) diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index ea15ae968..cdcfc4220 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -94,7 +94,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) poll_table wait_table; struct poll_table_entry entry; int init_timeout, max_timeout; - int timeout; + int timeout; long tmp_timeout; int retrans; int major_timeout_seen; int acknowledge_seen; @@ -173,6 +173,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) wait_table.entry = &entry; current->state = TASK_INTERRUPTIBLE; if (!(file->f_op->poll(file, &wait_table) & POLLIN)) { + int timed_out; if (timeout > max_timeout) { /* JEJB/JSP 2/7/94 * This is useful to see if the system is @@ -182,17 +183,15 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) } timeout = max_timeout; } - current->timeout = jiffies + timeout; - schedule(); + timed_out = !schedule_timeout(timeout); remove_wait_queue(entry.wait_address, &entry.wait); fput(file); current->state = TASK_RUNNING; if (signal_pending(current)) { - current->timeout = 0; result = -ERESTARTSYS; break; } - if (!current->timeout) { + if (timed_out) { if (n < retrans) continue; if (server->m.flags & NCP_MOUNT_SOFT) { @@ -208,8 +207,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) } major_timeout_seen = 1; continue; - } else - current->timeout = 0; + } } else if (wait_table.nr) { remove_wait_queue(entry.wait_address, &entry.wait); fput(file); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 7cc5ae160..b1dee7366 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -371,6 +371,15 @@ nfs_free_dircache(void) nfs_invalidate_dircache_sb(NULL); } +/* + * Whenever an NFS operation succeeds, we know that the dentry + * is valid, so we update the revalidation timestamp. + */ +static inline void nfs_renew_times(struct dentry * dentry) +{ + dentry->d_time = jiffies; +} + #define NFS_REVALIDATE_INTERVAL (5*HZ) /* * This is called every time the dcache has a lookup hit, @@ -416,23 +425,19 @@ parent->d_name.name, dentry->d_name.name); */ 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); + if (error) goto out_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); + if (fattr.fileid != inode->i_ino) 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); + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) goto out_bad; - } + + /* Ok, remeber that we successfully checked it.. */ + nfs_renew_times(dentry); out_valid: return 1; @@ -481,32 +486,6 @@ inode->i_ino, inode->i_count, inode->i_nlink); } /* - * Called to free the inode from the dentry. We must flush - * any pending writes for this dentry before freeing the inode. - */ -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", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count); -#endif - while (nfs_find_dentry_request(inode, dentry)) { -#ifdef NFS_PARANOIA -printk("nfs_dentry_iput: flushing %s/%s\n", -dentry->d_parent->d_name.name, dentry->d_name.name); -#endif - nfs_flush_dirty_pages(inode, 0, 0, 0); - } - } - iput(inode); -} - -/* * Called when the dentry is being freed to release private memory. */ static void nfs_dentry_release(struct dentry *dentry) @@ -521,7 +500,7 @@ struct dentry_operations nfs_dentry_operations = { NULL, /* d_compare */ nfs_dentry_delete, /* d_delete(struct dentry *) */ nfs_dentry_release, /* d_release(struct dentry *) */ - nfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */ + NULL /* d_iput */ }; #ifdef NFS_PARANOIA @@ -547,15 +526,6 @@ static void show_dentry(struct list_head * dlist) } #endif -/* - * Whenever an NFS operation succeeds, we know that the dentry - * is valid, so we update the revalidation timestamp. - */ -static inline void nfs_renew_times(struct dentry * dentry) -{ - dentry->d_time = jiffies; -} - static int nfs_lookup(struct inode *dir, struct dentry * dentry) { struct inode *inode; @@ -751,7 +721,7 @@ out: */ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { - int error, rehash = 0; + int error; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -761,39 +731,24 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) goto out; error = -EBUSY; - if (dentry->d_count > 1) { - /* Attempt to shrink child dentries ... */ - shrink_dcache_parent(dentry); - if (dentry->d_count > 1) - goto out; - } + if (!list_empty(&dentry->d_hash)) + goto out; + #ifdef NFS_PARANOIA if (dentry->d_inode->i_count > 1) printk("nfs_rmdir: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count, dentry->d_inode->i_nlink); #endif - /* - * Unhash the dentry while we remove the directory. - */ - if (!list_empty(&dentry->d_hash)) { - d_drop(dentry); - rehash = 1; - } + /* * Update i_nlink and free the inode before unlinking. */ if (dentry->d_inode->i_nlink) dentry->d_inode->i_nlink --; - d_delete(dentry); nfs_invalidate_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); - if (!error) { - if (rehash) - d_add(dentry, NULL); - nfs_renew_times(dentry); - } out: return error; } @@ -945,18 +900,7 @@ static int nfs_safe_remove(struct dentry *dentry) /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; - if (inode) { - if (NFS_WRITEBACK(inode)) { - nfs_flush_dirty_pages(inode, 0, 0, 0); - if (NFS_WRITEBACK(inode)) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s writes pending, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - goto out; - } - } - } else { + if (!inode) { #ifdef NFS_PARANOIA printk("nfs_safe_remove: %s/%s already negative??\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -1203,29 +1147,14 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); if (new_dir == old_dir) goto do_rename; /* - * Cross-directory move ... check whether it's a file. - */ - if (S_ISREG(old_inode->i_mode)) { - if (NFS_WRITEBACK(old_inode)) { -#ifdef NFS_PARANOIA -printk("nfs_rename: %s/%s has pending writes\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name); -#endif - nfs_flush_dirty_pages(old_inode, 0, 0, 0); - if (NFS_WRITEBACK(old_inode)) { -#ifdef NFS_PARANOIA -printk("nfs_rename: %s/%s has pending writes after flush\n", -old_dentry->d_parent->d_name.name, old_dentry->d_name.name); -#endif - goto out; - } - } - } - /* - * Moving a directory ... prune child dentries if needed. + * Cross-directory move ... + * + * ... prune child dentries and writebacks if needed. */ - else if (old_dentry->d_count > 1) + if (old_dentry->d_count > 1) { + nfs_wb_all(old_inode); shrink_dcache_parent(old_dentry); + } /* * Now check the use counts ... we can't safely do the diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 973ad52ec..25caa03d3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -91,14 +91,13 @@ struct inode_operations nfs_file_inode_operations = { static int nfs_file_close(struct inode *inode, struct file *file) { - int status, error; + int status; dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino); - status = nfs_flush_dirty_pages(inode, 0, 0, 0); - error = nfs_write_error(inode); + status = nfs_wb_all(inode); if (!status) - status = error; + status = nfs_write_error(inode); return status; } @@ -158,17 +157,17 @@ static int nfs_fsync(struct file *file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - int status, error; + int status; dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino); - status = nfs_flush_dirty_pages(inode, current->pid, 0, 0); - error = nfs_write_error(inode); + status = nfs_wb_pid(inode, current->pid); if (!status) - status = error; + status = nfs_write_error(inode); return status; } + /* * Write to a file (through the page cache). */ @@ -179,14 +178,10 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) struct inode * inode = dentry->d_inode; ssize_t result; - dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n", + dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_count, (unsigned long) count, (unsigned long) *ppos); + inode->i_ino, (unsigned long) count, (unsigned long) *ppos); - if (!inode) { - printk("nfs_file_write: inode = NULL\n"); - return -EINVAL; - } result = -EBUSY; if (IS_SWAPFILE(inode)) goto out_swapfile; @@ -194,14 +189,6 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (result) goto out; -#ifdef NFS_PARANOIA -/* N.B. This should be impossible now -- inodes can't change mode */ -if (!S_ISREG(inode->i_mode)) { - printk("nfs_file_write: write to non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; -} -#endif result = count; if (!count) goto out; @@ -214,7 +201,7 @@ out: return result; out_swapfile: - printk(KERN_ERR "NFS: attempt to write to active swap file!\n"); + printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; } @@ -253,22 +240,21 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX) return -ENOLCK; - /* If unlocking a file region, flush dirty pages (unless we've - * been killed by a signal, that is). */ - if (cmd == F_SETLK && fl->fl_type == F_UNLCK - && !signal_pending(current)) { - status = nfs_flush_dirty_pages(inode, current->pid, - fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 : - fl->fl_end - fl->fl_start + 1); - if (status < 0) - return status; - } + /* + * Flush all pending writes before doing anything + * with locks.. + */ + status = nfs_wb_all(inode); + if (status < 0) + return status; if ((status = nlmclnt_proc(inode, cmd, fl)) < 0) return status; - /* Here, we could turn off write-back of pages in the - * locked file region */ - + /* + * Make sure we re-validate anything we've got cached. + * This makes locking act as a cache coherency point. + */ + NFS_CACHEINV(inode); return 0; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 597821270..d11c20ab6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -105,11 +105,11 @@ nfs_delete_inode(struct inode * inode) #ifdef NFS_DEBUG_VERBOSE printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); #endif - nfs_invalidate_pages(inode); - while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) { + nfs_inval(inode); + while (NFS_WRITEBACK(inode) != NULL && + time_before(jiffies, timeout)) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/10; - schedule(); + schedule_timeout(HZ/10); } current->state = TASK_RUNNING; if (NFS_WRITEBACK(inode) != NULL) @@ -322,18 +322,20 @@ out_no_fh: goto out_shutdown; out_no_iod: - printk("NFS: couldn't start rpciod!\n"); + printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); out_shutdown: rpc_shutdown_client(server->client); - goto out_unlock; + goto out_free_host; out_no_client: - printk("NFS: cannot create RPC client.\n"); + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); xprt_destroy(xprt); - goto out_unlock; + goto out_free_host; out_no_xprt: - printk("NFS: cannot create RPC transport.\n"); + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + +out_free_host: kfree(server->hostname); out_unlock: unlock_super(sb); @@ -393,9 +395,10 @@ restart: tmp = head; while ((tmp = tmp->next) != head) { struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); +printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +dentry->d_count, !list_empty(&dentry->d_hash)); if (!dentry->d_count) { -printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count); dget(dentry); d_drop(dentry); dput(dentry); @@ -478,7 +481,7 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle, goto out; inode->i_sb = sb; inode->i_dev = sb->s_dev; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; inode->i_ino = fattr->fileid; nfs_read_inode(inode); nfs_fill_inode(inode, fattr); @@ -824,7 +827,7 @@ printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages); if (!S_ISDIR(inode->i_mode)) { /* This sends off all dirty pages off to the server. * Note that this function must not sleep. */ - nfs_invalidate_pages(inode); + nfs_inval(inode); invalidate_inode_pages(inode); } else nfs_invalidate_dircache(inode); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 6f1fdd7ff..3f9b16078 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -225,11 +225,24 @@ nfs_readpage(struct file *file, struct page *page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - int error = -1; + int error; dprintk("NFS: nfs_readpage (%p %ld@%ld)\n", page, PAGE_SIZE, page->offset); set_bit(PG_locked, &page->flags); + + /* + * Try to flush any pending writes to the file.. + * + * NOTE! Because we own the page lock, there cannot + * be any new pending writes generated at this point + * for this page (other pages can be written to). + */ + error = nfs_wb_page(inode, page); + if (error) + return error; + + error = -1; atomic_inc(&page->count); if (!IS_SWAPFILE(inode) && !PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE) diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 3ae490c37..bf23ad8aa 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -19,7 +19,7 @@ #include <asm/uaccess.h> static int nfs_readlink(struct dentry *, char *, int); -static struct dentry *nfs_follow_link(struct dentry *, struct dentry *); +static struct dentry *nfs_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -68,7 +68,7 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) } static struct dentry * -nfs_follow_link(struct dentry * dentry, struct dentry *base) +nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) { int error; unsigned int len; @@ -94,7 +94,7 @@ nfs_follow_link(struct dentry * dentry, struct dentry *base) path[len] = 0; kfree(mem); - result = lookup_dentry(path, base, 1); + result = lookup_dentry(path, base, follow); kfree(path); out: return result; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f8658f37f..33b4dd16a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -46,7 +46,6 @@ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de> */ -#include <linux/config.h> #include <linux/types.h> #include <linux/malloc.h> #include <linux/swap.h> @@ -59,7 +58,7 @@ #define NFS_PARANOIA 1 #define NFSDBG_FACILITY NFSDBG_PAGECACHE -static void nfs_wback_lock(struct rpc_task *task); +static void nfs_wback_begin(struct rpc_task *task); static void nfs_wback_result(struct rpc_task *task); static void nfs_cancel_request(struct nfs_wreq *req); @@ -73,9 +72,7 @@ static void nfs_cancel_request(struct nfs_wreq *req); * Limit number of delayed writes */ static int nr_write_requests = 0; -static int nr_failed_requests = 0; static struct rpc_wait_queue write_queue = RPC_INIT_WAITQ("write_chain"); -struct nfs_wreq * nfs_failed_requests = NULL; /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE @@ -83,46 +80,6 @@ struct nfs_wreq * nfs_failed_requests = NULL; #endif /* - * Unlock a page after writing it - */ -static inline void -nfs_unlock_page(struct page *page) -{ - dprintk("NFS: unlock %ld\n", page->offset); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - -#ifdef CONFIG_NFS_SWAP - /* async swap-out support */ - if (test_and_clear_bit(PG_decr_after, &page->flags)) - atomic_dec(&page->count); - if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) { - /* - * We're doing a swap, so check that this page is - * swap-cached and do the necessary cleanup. - */ - swap_after_unlock_page(page->offset); - } -#endif -} - -/* - * Transfer a page lock to a write request waiting for it. - */ -static inline void -transfer_page_lock(struct nfs_wreq *req) -{ - dprintk("NFS: transfer_page_lock\n"); - - req->wb_flags &= ~NFS_WRITE_WANTLOCK; - req->wb_flags |= NFS_WRITE_LOCKED; - rpc_wake_up_task(&req->wb_task); - - dprintk("NFS: wake up task %d (flags %x)\n", - req->wb_task.tk_pid, req->wb_flags); -} - -/* * Write a page synchronously. * Offset is the data offset within the page. */ @@ -195,7 +152,6 @@ io_error: inode->i_ino, fattr.fileid); } - nfs_unlock_page(page); return written? written : result; } @@ -220,11 +176,13 @@ remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq) } /* - * Find a write request for a given page + * Find a non-busy write request for a given page to + * try to combine with. */ static inline struct nfs_wreq * find_write_request(struct inode *inode, struct page *page) { + pid_t pid = current->pid; struct nfs_wreq *head, *req; dprintk("NFS: find_write_request(%x/%ld, %p)\n", @@ -232,76 +190,23 @@ find_write_request(struct inode *inode, struct page *page) if (!(req = head = NFS_WRITEBACK(inode))) return NULL; do { - if (req->wb_page == page) - return req; - } while ((req = WB_NEXT(req)) != head); - return NULL; -} - -/* - * Find any requests for the specified dentry. - */ -int -nfs_find_dentry_request(struct inode *inode, struct dentry *dentry) -{ - struct nfs_wreq *head, *req; - int found = 0; - - req = head = NFS_WRITEBACK(inode); - while (req != NULL) { - if (req->wb_dentry == dentry && !WB_CANCELLED(req)) { - found = 1; - break; - } - if ((req = WB_NEXT(req)) == head) - break; - } - return found; -} - -/* - * Find a failed write request by pid - */ -static struct nfs_wreq * -find_failed_request(struct inode *inode, pid_t pid) -{ - struct nfs_wreq *head, *req; + /* + * We can't combine with canceled requests or + * requests that have already been started.. + */ + if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS)) + continue; - req = head = nfs_failed_requests; - while (req != NULL) { - if (req->wb_inode == inode && (pid == 0 || req->wb_pid == pid)) + if (req->wb_page == page && req->wb_pid == pid) return req; - if ((req = WB_NEXT(req)) == head) - break; - } - return NULL; -} - -/* - * Add a request to the failed list. - */ -static void -append_failed_request(struct nfs_wreq * req) -{ - static int old_max = 16; - append_write_request(&nfs_failed_requests, req); - nr_failed_requests++; - if (nr_failed_requests >= old_max) { - printk("NFS: %d failed requests\n", nr_failed_requests); - old_max = old_max << 1; - } -} + /* + * Ehh, don't keep too many tasks queued.. + */ + rpc_wake_up_task(&req->wb_task); -/* - * Remove a request from the failed list and free it. - */ -static void -remove_failed_request(struct nfs_wreq * req) -{ - remove_write_request(&nfs_failed_requests, req); - kfree(req); - nr_failed_requests--; + } while ((req = WB_NEXT(req)) != head); + return NULL; } /* @@ -310,14 +215,8 @@ remove_failed_request(struct nfs_wreq * req) int nfs_check_failed_request(struct inode * inode) { - struct nfs_wreq * req; - int found = 0; - - while ((req = find_failed_request(inode, 0)) != NULL) { - remove_failed_request(req); - found++; - } - return found; + /* FIXME! */ + return 0; } /* @@ -334,6 +233,10 @@ update_write_request(struct nfs_wreq *req, unsigned int first, dprintk("nfs: trying to update write request %p\n", req); + /* not contiguous? */ + if (rqlast < first || last < rqfirst) + return 0; + /* Check the credentials associated with this write request. * If the buffer is owned by the same user, we can happily * add our data without risking server permission problems. @@ -349,12 +252,25 @@ update_write_request(struct nfs_wreq *req, unsigned int first, rqfirst = first; if (rqlast < last) rqlast = last; + req->wb_offset = rqfirst; req->wb_bytes = rqlast - rqfirst; + req->wb_count++; return 1; } +static inline void +free_write_request(struct nfs_wreq * req) +{ + if (!--req->wb_count) { + struct inode *inode = req->wb_inode; + remove_write_request(&NFS_WRITEBACK(inode), req); + kfree(req); + nr_write_requests--; + } +} + /* * Create and initialize a writeback request */ @@ -371,8 +287,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, page->offset + offset, bytes); /* FIXME: Enforce hard limit on number of concurrent writes? */ - - wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER); + wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL); if (!wreq) goto out_fail; memset(wreq, 0, sizeof(*wreq)); @@ -380,7 +295,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, task = &wreq->wb_task; rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE); task->tk_calldata = wreq; - task->tk_action = nfs_wback_lock; + task->tk_action = nfs_wback_begin; rpcauth_lookupcred(task); /* Obtain user creds */ if (task->tk_status < 0) @@ -393,8 +308,7 @@ create_write_request(struct dentry *dentry, struct inode *inode, wreq->wb_page = page; wreq->wb_offset = offset; wreq->wb_bytes = bytes; - - atomic_inc(&page->count); + wreq->wb_count = 2; /* One for the IO, one for us */ append_write_request(&NFS_WRITEBACK(inode), wreq); @@ -414,8 +328,7 @@ out_fail: * Schedule a writeback RPC call. * If the server is congested, don't add to our backlog of queued * requests but call it synchronously. - * The function returns false if the page has been unlocked as the - * consequence of a synchronous write call. + * The function returns whether we should wait for the thing or not. * * FIXME: Here we could walk the inode's lock list to see whether the * page we're currently writing to has been write-locked by the caller. @@ -438,7 +351,6 @@ schedule_write_request(struct nfs_wreq *req, int sync) 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); @@ -450,39 +362,38 @@ schedule_write_request(struct nfs_wreq *req, int sync) rpc_sleep_on(&write_queue, task, NULL, NULL); } - return sync == 0; + return sync; } /* - * Wait for request to complete - * This is almost a copy of __wait_on_page + * Wait for request to complete. */ -static inline int +static int wait_on_write_request(struct nfs_wreq *req) { + struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); struct wait_queue wait = { current, NULL }; - struct page *page = req->wb_page; - int retval; sigset_t oldmask; - struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode); + int retval; + + /* Make sure it's started.. */ + if (!WB_INPROGRESS(req)) + rpc_wake_up_task(&req->wb_task); rpc_clnt_sigmask(clnt, &oldmask); - add_wait_queue(&page->wait, &wait); - atomic_inc(&page->count); + add_wait_queue(&req->wb_wait, &wait); for (;;) { current->state = TASK_INTERRUPTIBLE; retval = 0; - if (!PageLocked(page)) + if (req->wb_flags & NFS_WRITE_COMPLETE) break; retval = -ERESTARTSYS; if (signalled()) break; schedule(); } - remove_wait_queue(&page->wait, &wait); + remove_wait_queue(&req->wb_wait, &wait); 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; } @@ -505,21 +416,18 @@ nfs_writepage(struct file * file, struct page *page) * things with a page scheduled for an RPC call (e.g. invalidate it). */ int -nfs_updatepage(struct file *file, struct page *page, const char *buffer, - unsigned long offset, unsigned int count, int sync) +nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - u8 *page_addr = (u8 *) page_address(page); struct nfs_wreq *req; - int status = 0, page_locked = 1; + int synchronous = sync; + int retval; dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, page->offset+offset, sync); - set_bit(PG_locked, &page->flags); - /* * Try to find a corresponding request on the writeback queue. * If there is one, we can be sure that this request is not @@ -530,151 +438,54 @@ nfs_updatepage(struct file *file, struct page *page, const char *buffer, * After returning, generic_file_write will wait on the * page and retry the update. */ - 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; - } - dprintk("NFS: wake up conflicting write request.\n"); - transfer_page_lock(req); - return 0; - } - - /* Copy data to page buffer. */ - status = -EFAULT; - if (copy_from_user(page_addr + offset, buffer, count)) - goto done; + req = find_write_request(inode, page); + if (req && update_write_request(req, offset, count)) + goto updated; - /* If wsize is smaller than page size, update and write + /* + * If wsize is smaller than page size, update and write * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_SIZE) return nfs_writepage_sync(dentry, inode, page, offset, count); /* Create the write request. */ - status = -ENOBUFS; req = create_write_request(dentry, inode, page, offset, count); if (!req) - goto done; + return -ENOBUFS; - /* Schedule request */ - page_locked = schedule_write_request(req, sync); - -updated: /* - * If we wrote up to the end of the chunk, transmit request now. - * We should be a bit more intelligent about detecting whether a - * process accesses the file sequentially or not. + * Ok, there's another user of this page with the new request.. + * The IO completion will then free the page and the dentry. */ - if (page_locked && (offset + count >= PAGE_SIZE || sync)) - req->wb_flags |= NFS_WRITE_WANTLOCK; - - /* If the page was written synchronously, return any error that - * may have happened; otherwise return the write count. */ - if (page_locked || (status = nfs_write_error(inode)) >= 0) - status = count; - -done: - /* Unlock page and wake up anyone sleeping on it */ - if (page_locked) { - if (req && WB_WANTLOCK(req)) { - transfer_page_lock(req); - /* rpc_execute(&req->wb_task); */ - if (sync) { - /* 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; - } - } else { - if (status < 0) { -printk("NFS: %s/%s write failed, clearing bit\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - clear_bit(PG_uptodate, &page->flags); - } - nfs_unlock_page(page); - } - } - - dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n", - status, inode->i_size); - return status; -} - -/* - * Flush out a dirty page. - */ -static void -nfs_flush_request(struct nfs_wreq *req) -{ - struct page *page = req->wb_page; - -#ifdef NFS_DEBUG_VERBOSE -if (req->wb_inode != page->inode) -printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page); -#endif - dprintk("NFS: nfs_flush_request(%s/%s, @%ld)\n", - req->wb_dentry->d_parent->d_name.name, - req->wb_dentry->d_name.name, page->offset); - - req->wb_flags |= NFS_WRITE_WANTLOCK; - if (!test_and_set_bit(PG_locked, &page->flags)) { - transfer_page_lock(req); - } else { - printk(KERN_WARNING "NFS oops in %s: can't lock page!\n", - __FUNCTION__); - rpc_wake_up_task(&req->wb_task); - } -} - -/* - * Flush writeback requests. See nfs_flush_dirty_pages for details. - */ -static struct nfs_wreq * -nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len, - int invalidate) -{ - struct nfs_wreq *head, *req, *last = NULL; - off_t rqoffset, rqend, end; + atomic_inc(&page->count); + dget(dentry); - end = len? offset + len : 0x7fffffffUL; + /* Schedule request */ + synchronous = schedule_write_request(req, sync); - req = head = NFS_WRITEBACK(inode); - while (req != NULL) { - dprintk("NFS: %4d nfs_flush inspect %s/%s @%ld fl %x\n", - req->wb_task.tk_pid, - req->wb_dentry->d_parent->d_name.name, - req->wb_dentry->d_name.name, - req->wb_page->offset, req->wb_flags); +updated: + if (req->wb_bytes == PAGE_SIZE) + set_bit(PG_uptodate, &page->flags); - rqoffset = req->wb_page->offset + req->wb_offset; - rqend = rqoffset + req->wb_bytes; - if (rqoffset < end && offset < rqend && - (pid == 0 || req->wb_pid == pid)) { - if (!WB_INPROGRESS(req) && !WB_HAVELOCK(req)) { -#ifdef NFS_DEBUG_VERBOSE -printk("nfs_flush: flushing inode=%ld, %d @ %lu\n", -req->wb_inode->i_ino, req->wb_bytes, rqoffset); -#endif - nfs_flush_request(req); - } - last = req; + retval = count; + if (synchronous) { + int status = wait_on_write_request(req); + if (status) { + nfs_cancel_request(req); + retval = status; + } else { + status = req->wb_status; + if (status < 0) + retval = status; } - if (invalidate) - req->wb_flags |= NFS_WRITE_INVALIDATE; - if ((req = WB_NEXT(req)) == head) - break; + + if (retval < 0) + clear_bit(PG_uptodate, &page->flags); } - return last; + free_write_request(req); + return retval; } /* @@ -711,53 +522,71 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid) } /* - * Flush out all dirty pages belonging to a certain user process and - * maybe wait for the RPC calls to complete. - * - * Another purpose of this function is sync()ing a file range before a - * write lock is released. This is what offset and length are for, even if - * this isn't used by the nlm module yet. + * If we're waiting on somebody else's request + * we need to increment the counter during the + * wait so that the request doesn't disappear + * from under us during the wait.. */ -int -nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len) +static int FASTCALL(wait_on_other_req(struct nfs_wreq *)); +static int wait_on_other_req(struct nfs_wreq *req) { - struct nfs_wreq *last = NULL; - int result = 0; + int retval; + req->wb_count++; + retval = wait_on_write_request(req); + free_write_request(req); + return retval; +} - dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n", - inode->i_dev, inode->i_ino, current->pid, offset, len); +/* + * This writes back a set of requests according to the condition. + * + * If this ever gets much more convoluted, use a fn pointer for + * the condition.. + */ +#define NFS_WB(inode, cond) { int retval = 0 ; \ + do { \ + struct nfs_wreq *req = NFS_WRITEBACK(inode); \ + struct nfs_wreq *head = req; \ + if (!req) break; \ + for (;;) { \ + if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \ + if (cond) break; \ + req = WB_NEXT(req); \ + if (req == head) goto out; \ + } \ + retval = wait_on_other_req(req); \ + } while (!retval); \ +out: return retval; \ +} - for (;;) { - /* Flush all pending writes for the pid and file region */ - last = nfs_flush_pages(inode, pid, offset, len, 0); - if (last == NULL) - break; - result = wait_on_write_request(last); - if (result) { - nfs_cancel_dirty(inode,pid); - break; - } - } +int +nfs_wb_all(struct inode *inode) +{ + NFS_WB(inode, 1); +} - return result; +/* + * Write back all requests on one page - we do this before reading it. + */ +int +nfs_wb_page(struct inode *inode, struct page *page) +{ + NFS_WB(inode, req->wb_page == page); } /* - * Flush out any pending write requests and flag that they be discarded - * after the write is complete. - * - * This function is called from nfs_refresh_inode just before it calls - * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure - * that all dirty pages are locked, so that invalidate_inode_pages does - * not throw away any dirty pages. + * Write back all pending writes for one user.. */ -void -nfs_invalidate_pages(struct inode *inode) +int +nfs_wb_pid(struct inode *inode, pid_t pid) { - dprintk("NFS: nfs_invalidate_pages(%x/%ld)\n", - inode->i_dev, inode->i_ino); + NFS_WB(inode, req->wb_pid == pid); +} - nfs_flush_pages(inode, 0, 0, 0, 1); +void +nfs_inval(struct inode *inode) +{ + nfs_cancel_dirty(inode,0); } /* @@ -795,21 +624,8 @@ nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset) int nfs_check_error(struct inode *inode) { - struct nfs_wreq *req; - int status = 0; - - dprintk("nfs: checking for write error inode %04x/%ld\n", - inode->i_dev, inode->i_ino); - - req = find_failed_request(inode, current->pid); - if (req) { - dprintk("nfs: write error %d inode %04x/%ld\n", - req->wb_task.tk_status, inode->i_dev, inode->i_ino); - - status = req->wb_task.tk_status; - remove_failed_request(req); - } - return status; + /* FIXME! */ + return 0; } /* @@ -819,23 +635,16 @@ nfs_check_error(struct inode *inode) * set up the RPC call info, and pass to the call FSM. */ static void -nfs_wback_lock(struct rpc_task *task) +nfs_wback_begin(struct rpc_task *task) { struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata; struct page *page = req->wb_page; struct dentry *dentry = req->wb_dentry; - dprintk("NFS: %4d nfs_wback_lock (%s/%s, status=%d flags=%x)\n", + dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n", task->tk_pid, dentry->d_parent->d_name.name, dentry->d_name.name, task->tk_status, req->wb_flags); - if (!WB_HAVELOCK(req)) - req->wb_flags |= NFS_WRITE_WANTLOCK; - - if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags)) - goto out_locked; - req->wb_flags &= ~NFS_WRITE_WANTLOCK; - req->wb_flags |= NFS_WRITE_LOCKED; task->tk_status = 0; /* Setup the task struct for a writeback call */ @@ -848,12 +657,6 @@ nfs_wback_lock(struct rpc_task *task) req->wb_flags |= NFS_WRITE_INPROGRESS; return; - -out_locked: - printk("NFS: page already locked in writeback_lock!\n"); - task->tk_timeout = 2 * HZ; - rpc_sleep_on(&write_queue, task, NULL, NULL); - return; } /* @@ -873,15 +676,10 @@ nfs_wback_result(struct rpc_task *task) /* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */ req->wb_flags |= NFS_WRITE_COMPLETE; + req->wb_status = status; + if (status < 0) { - /* - * An error occurred. Report the error back to the - * application by adding the request to the failed - * requests list. - */ - if (find_failed_request(inode, req->wb_pid)) - status = 0; - clear_bit(PG_uptodate, &page->flags); + req->wb_flags |= NFS_WRITE_INVALIDATE; } else if (!WB_CANCELLED(req)) { struct nfs_fattr *fattr = &req->wb_fattr; /* Update attributes as result of writeback. @@ -911,31 +709,28 @@ nfs_wback_result(struct rpc_task *task) } } - /* - * This call might block, so we defer removing the request - * from the inode's writeback list. - */ rpc_release_task(task); if (WB_INVALIDATE(req)) clear_bit(PG_uptodate, &page->flags); - if (WB_HAVELOCK(req)) - nfs_unlock_page(page); + __free_page(page); + dput(req->wb_dentry); + + wake_up(&req->wb_wait); + /* - * Now it's safe to remove the request from the inode's - * writeback list and wake up any tasks sleeping on it. - * If the request failed, add it to the failed list. + * FIXME! + * + * We should not free the request here if it has pending + * error status on it. We should just leave it around, to + * let the error be collected later. However, the error + * collecting routines are too stupid for that right now, + * so we just drop the error on the floor at this point + * for any async writes. + * + * This should not be a major headache to fix, but I want + * to validate basic operations first. */ - remove_write_request(&NFS_WRITEBACK(inode), req); - - if (status >= 0) - kfree(req); - else { - dprintk("NFS: %4d saving write failure code\n", task->tk_pid); - append_failed_request(req); - } - - free_page(page_address(page)); - nr_write_requests--; + free_write_request(req); } diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index c2346d458..a2f80bea7 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -41,7 +41,21 @@ nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) current->fsgid = cred->cr_gid; else current->fsgid = exp->ex_anon_gid; - for (i = 0; i < NGROUPS; i++) - current->groups[i] = cred->cr_groups[i]; + for (i = 0; i < NGROUPS; i++) { + gid_t group = cred->cr_groups[i]; + if (group == (gid_t) NOGROUP) + break; + current->groups[i] = group; + } + current->ngroups = i; + + if ((cred->cr_uid)) { + cap_lower(current->cap_effective, CAP_DAC_OVERRIDE); + cap_lower(current->cap_effective, CAP_DAC_READ_SEARCH); + } else { + cap_raise(current->cap_effective, CAP_DAC_OVERRIDE); + cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH); + } + rqstp->rq_userset = 1; } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index e30953bb2..81bd84bcf 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -33,7 +33,10 @@ typedef struct svc_client svc_client; typedef struct svc_export svc_export; static svc_export * exp_find(svc_client *clp, kdev_t dev); -static svc_export * exp_parent(svc_client *clp, kdev_t dev); +static svc_export * exp_parent(svc_client *clp, kdev_t dev, + struct dentry *dentry); +static svc_export * exp_child(svc_client *clp, kdev_t dev, + struct dentry *dentry); static void exp_unexport_all(svc_client *clp); static void exp_do_unexport(svc_export *unexp); static svc_client * exp_getclientbyname(char *name); @@ -90,8 +93,16 @@ exp_get(svc_client *clp, kdev_t dev, ino_t ino) if (!clp) return NULL; - exp = exp_find(clp, dev); - return (exp && exp->ex_ino == ino)? exp : NULL; + + exp = clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + if (exp->ex_ino == ino && exp->ex_dev == dev) + goto out; + } while (NULL != (exp = exp->ex_next)); + exp = NULL; +out: + return exp; } /* @@ -126,22 +137,80 @@ nfsd_parentdev(kdev_t *devp) } /* - * Find the parent export entry for a given fs. This function is used - * only by the export syscall to keep the export tree consistent. + * Find the export entry for a given dentry. <gam3@acm.org> */ static svc_export * -exp_parent(svc_client *clp, kdev_t dev) +exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry) { - svc_export *exp; + svc_export *exp; kdev_t xdev = dev; + struct dentry *xdentry = dentry; + struct dentry *ndentry = NULL; + + if (clp == NULL || dentry == NULL) + return NULL; do { - exp = exp_find(clp, xdev); - if (exp) - return exp; - } while (nfsd_parentdev(&xdev)); + xdev = dev; + do { + exp = clp->cl_export[EXPORT_HASH(xdev)]; + if (exp) + do { + ndentry = exp->ex_dentry; + if (ndentry == xdentry) { +#ifdef NFSD_PARANOIA +if (dev == xdev) + dprintk("nfsd: exp_parent submount over mount.\n"); +else + dprintk("nfsd: exp_parent found.\n"); +#endif + goto out; + } + } while (NULL != (exp = exp->ex_next)); + } while (nfsd_parentdev(&xdev)); + if (xdentry == xdentry->d_parent) { + break; + } + } while ((xdentry = xdentry->d_parent)); + exp = NULL; +out: + return exp; +} - return NULL; +/* + * Find the child export entry for a given fs. This function is used + * only by the export syscall to keep the export tree consistent. + * <gam3@acm.org> + */ +static svc_export * +exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry) +{ + svc_export *exp; + struct dentry *xdentry = dentry; + struct dentry *ndentry = NULL; + + if (clp == NULL || dentry == NULL) + return NULL; + + exp = clp->cl_export[EXPORT_HASH(dev)]; + if (exp) + do { + ndentry = exp->ex_dentry; + if (ndentry) + while ((ndentry = ndentry->d_parent)) { + if (ndentry == xdentry) { +#ifdef NFSD_PARANOIA +dprintk("nfsd: exp_child mount under submount.\n"); +#endif + goto out; + } + if (ndentry == ndentry->d_parent) + break; + } + } while (NULL != (exp = exp->ex_next)); + exp = NULL; +out: + return exp; } /* @@ -160,9 +229,10 @@ exp_export(struct nfsctl_export *nxp) ino_t ino; /* Consistency check */ + err = -EINVAL; if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) - return -EINVAL; + goto out; dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", nxp->ex_client, nxp->ex_path, @@ -183,15 +253,11 @@ exp_export(struct nfsctl_export *nxp) * If there's already an export for this file, assume this * is just a flag update. */ - if ((exp = exp_find(clp, dev)) != NULL) { - /* Ensure there's only one export per FS. */ - err = -EPERM; - if (exp->ex_ino == ino) { - exp->ex_flags = nxp->ex_flags; - exp->ex_anon_uid = nxp->ex_anon_uid; - exp->ex_anon_gid = nxp->ex_anon_gid; - err = 0; - } + if ((exp = exp_get(clp, dev, ino)) != NULL) { + exp->ex_flags = nxp->ex_flags; + exp->ex_anon_uid = nxp->ex_anon_uid; + exp->ex_anon_gid = nxp->ex_anon_gid; + err = 0; goto out_unlock; } @@ -203,32 +269,32 @@ exp_export(struct nfsctl_export *nxp) err = -ENOENT; inode = dentry->d_inode; - if(!inode) + if (!inode) goto finish; err = -EINVAL; - if(inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { - + if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n", inode->i_dev, dev); /* I'm just being paranoid... */ goto finish; } - /* We currently export only dirs. */ + /* We currently export only dirs and regular files. + * This is what umountd does. + */ err = -ENOTDIR; - if (!S_ISDIR(inode->i_mode)) + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) goto finish; - /* If this is a sub-export, must be root of FS */ err = -EINVAL; - if ((parent = exp_parent(clp, dev)) != NULL) { - struct super_block *sb = inode->i_sb; - - if (inode != sb->s_root->d_inode) { -#ifdef NFSD_PARANOIA -printk("exp_export: sub-export %s not root of device %s\n", -nxp->ex_path, kdevname(sb->s_dev)); -#endif + if ((parent = exp_child(clp, dev, dentry)) != NULL) { + dprintk("exp_export: export not valid (Rule 3).\n"); + goto finish; + } + /* Is this is a sub-export, must be a proper subset of FS */ + if ((parent = exp_parent(clp, dev, dentry)) != NULL) { + if (dev == parent->ex_dev) { + dprintk("exp_export: sub-export not valid (Rule 2).\n"); goto finish; } } @@ -306,7 +372,7 @@ exp_do_unexport(svc_export *unexp) */ if (!exp_device_in_use(unexp->ex_dev)) { printk("exp_do_unexport: %s last use, flushing cache\n", -kdevname(unexp->ex_dev)); + kdevname(unexp->ex_dev)); nfsd_fh_flush(unexp->ex_dev); } @@ -362,18 +428,15 @@ exp_unexport(struct nfsctl_export *nxp) err = -EINVAL; clp = exp_getclientbyname(nxp->ex_client); if (clp) { -printk("exp_unexport: found client %s\n", nxp->ex_client); expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev); while ((exp = *expp) != NULL) { if (exp->ex_dev == nxp->ex_dev) { - if (exp->ex_ino != nxp->ex_ino) { -printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex_ino); + if (exp->ex_ino == nxp->ex_ino) { + *expp = exp->ex_next; + exp_do_unexport(exp); + err = 0; break; } - *expp = exp->ex_next; - exp_do_unexport(exp); - err = 0; - break; } expp = &(exp->ex_next); } @@ -390,28 +453,50 @@ out: * since its harder to fool a kernel module than a user space program. */ int -exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) +exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, + char *path, struct knfs_fh *f) { struct svc_export *exp; - struct dentry *dentry; + struct dentry *dentry = NULL; struct inode *inode; struct svc_fh fh; + int err; - dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); - - exp = exp_get(clp, dev, ino); - if (!exp) - return -EPERM; + err = -EPERM; + if (path) { + if (!(dentry = lookup_dentry(path, NULL, 0))) { + printk("nfsd: exp_rootfh path not found %s", path); + return -EPERM; + } + dev = dentry->d_inode->i_dev; + ino = dentry->d_inode->i_ino; + + dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n", + path, dentry, clp->cl_ident, dev, ino); + exp = exp_parent(clp, dev, dentry); + } else { + dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", + clp->cl_ident, dev, ino); + if ((exp = exp_get(clp, dev, ino))) + if (!(dentry = dget(exp->ex_dentry))) { + printk("exp_rootfh: Aieee, NULL dentry\n"); + return -EPERM; + } + } + if (!exp) { + dprintk("nfsd: exp_rootfh export not found.\n"); + goto out; + } - dentry = exp->ex_dentry; inode = dentry->d_inode; - if(!inode) { + if (!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); - return -EPERM; + goto out; } - if(inode->i_dev != dev || inode->i_ino != ino) { + if (inode->i_dev != dev || inode->i_ino != ino) { printk("exp_rootfh: Aieee, ino/dev mismatch\n"); - printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n", + printk("exp_rootfh: arg[dev(%x):ino(%ld)]" + " inode[dev(%x):ino(%ld)]\n", dev, ino, inode->i_dev, inode->i_ino); } @@ -419,11 +504,14 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) * fh must be initialized before calling fh_compose */ fh_init(&fh); - fh_compose(&fh, exp, dget(dentry)); + fh_compose(&fh, exp, dentry); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); - return 0; + +out: + dput(dentry); + return err; } /* diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 43de72f54..5a8add24d 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -5,6 +5,7 @@ * * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ +#define NFS_GETFH_NEW #include <linux/config.h> #include <linux/module.h> @@ -47,6 +48,7 @@ static int nfsctl_delclient(struct nfsctl_client *data); static int nfsctl_export(struct nfsctl_export *data); static int nfsctl_unexport(struct nfsctl_export *data); static int nfsctl_getfh(struct nfsctl_fhparm *, struct knfs_fh *); +static int nfsctl_getfd(struct nfsctl_fdparm *, struct knfs_fh *); /* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */ static int initialized = 0; @@ -108,6 +110,29 @@ nfsctl_ugidupdate(nfs_ugidmap *data) #endif static inline int +nfsctl_getfd(struct nfsctl_fdparm *data, struct knfs_fh *res) +{ + struct sockaddr_in *sin; + struct svc_client *clp; + int err = 0; + + if (data->gd_addr.sa_family != AF_INET) + return -EPROTONOSUPPORT; + if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) + return -EINVAL; + sin = (struct sockaddr_in *)&data->gd_addr; + + exp_readlock(); + if (!(clp = exp_getclient(sin))) + err = -EPERM; + else + err = exp_rootfh(clp, 0, 0, data->gd_path, res); + exp_unlock(); + + return err; +} + +static inline int nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) { struct sockaddr_in *sin; @@ -124,7 +149,7 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) if (!(clp = exp_getclient(sin))) err = -EPERM; else - err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, res); + err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, res); exp_unlock(); return err; @@ -194,6 +219,9 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) case NFSCTL_GETFH: err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh); break; + case NFSCTL_GETFD: + err = nfsctl_getfd(&arg->ca_getfd, &res->cr_getfh); + break; default: err = -EINVAL; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index b19fd711c..8c3d91f64 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -493,6 +493,9 @@ static struct fh_entry *find_fhe(struct dentry *dentry, int cache, struct fh_entry *fhe; int i, found = (empty == NULL) ? 1 : 0; + if (!dentry) + goto out; + fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0]; for (i = 0; i < NFSD_MAXFH; i++, fhe++) { if (fhe->dentry == dentry) { @@ -504,6 +507,7 @@ static struct fh_entry *find_fhe(struct dentry *dentry, int cache, *empty = fhe; } } +out: return NULL; } @@ -756,8 +760,12 @@ static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh) fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL); if (fhe) { - struct dentry *parent, *dentry = fhe->dentry; - struct inode *inode = dentry->d_inode; + struct dentry *parent, *dentry; + struct inode *inode; + + dentry = fhe->dentry; + inode = dentry->d_inode; + if (!inode) { #ifdef NFSD_PARANOIA printk("find_dentry_in_fhcache: %s/%s has no inode!\n", @@ -1019,7 +1027,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n", fh->fh_xdev, fh->fh_xino, fh->fh_dcookie); - if(fhp->fh_dverified) + if (fhp->fh_dverified) goto check_type; /* * Look up the export entry. @@ -1051,11 +1059,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) dentry = find_fh_dentry(fh); if (!dentry) goto out; + /* * Note: it's possible the returned dentry won't be the one in the - * file handle. We can correct the file handle for our use, but - * unfortunately the client will keep sending the broken one. Let's - * hope the lookup will keep patching things up. + * file handle. We can correct the file handle for our use, but + * unfortunately the client will keep sending the broken one. Let's + * hope the lookup will keep patching things up. */ fhp->fh_dentry = dentry; fhp->fh_export = exp; @@ -1071,6 +1080,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) check_type: dentry = fhp->fh_dentry; inode = dentry->d_inode; + exp = fhp->fh_export; if (type > 0 && (inode->i_mode & S_IFMT) != type) { error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; goto out; @@ -1080,9 +1090,45 @@ check_type: goto out; } + /* + * Security: Check that the export is valid for dentry <gam3@acm.org> + */ + if (fh->fh_dev != fh->fh_xdev) { + printk("fh_verify: Security: export on other device" + " (%d, %d).\n", fh->fh_dev, fh->fh_xdev); + goto out; + } else if (exp->ex_dentry != dentry) { + struct dentry *tdentry = dentry; + int err2 = 0; + + error = nfserr_stale; + do { + tdentry = tdentry->d_parent; + if (exp->ex_dentry == tdentry) { + error = 0; + break; + } + if ((err2 = nfsd_permission(exp, tdentry, MAY_READ))) { + error = err2; +#ifdef NFSD_PARANOIA + goto out1; +#else + goto out; +#endif + } + } while ((tdentry != tdentry->d_parent)); + if (error) { + printk("fh_verify: Security: %s/%s bad export.\n", + dentry->d_parent->d_name.name, + dentry->d_name.name); + goto out; + } + } + /* Finally, check access permissions. */ - error = nfsd_permission(fhp->fh_export, dentry, access); + error = nfsd_permission(exp, dentry, access); #ifdef NFSD_PARANOIA +out1: if (error) printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, access, error); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index d8da2f463..455d0f209 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -115,7 +115,7 @@ nfsd(struct svc_rqst *rqstp) * Find a socket with data available and call its * recvfrom routine. */ - while ((err = svc_recv(serv, rqstp)) == -EAGAIN) + while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN) ; if (err < 0) break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 8b398112d..72d67a13a 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -14,6 +14,7 @@ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> */ +#include <linux/config.h> #include <linux/version.h> #include <linux/sched.h> #include <linux/errno.h> @@ -39,6 +40,7 @@ #endif #define NFSDDBG_FACILITY NFSDDBG_FILEOP +#define NFSD_PARANOIA /* Open mode for nfsd_open */ #define OPEN_READ 0 @@ -168,13 +170,19 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (IS_ERR(dchild)) goto out_nfserr; /* - * Make sure we haven't crossed a mount point ... + * check if we have crossed a mount point ... */ if (dchild->d_sb != dparent->d_sb) { -#ifdef NFSD_PARANOIA -printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, name); -#endif - goto out_dput; + struct dentry *tdentry; + tdentry = dchild->d_covers; + if (tdentry == dchild) + goto out_dput; + dput(dchild); + dchild = dget(tdentry); + if (dchild->d_sb != dparent->d_sb) { +printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, dchild->d_name.name); + goto out_dput; + } } /* @@ -416,8 +424,8 @@ found: * N.B. After this call fhp needs an fh_put */ int -nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, - unsigned long *count) +nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, + char *buf, unsigned long *count) { struct raparms *ra; mm_segment_t oldfs; @@ -547,13 +555,10 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, if (EX_WGATHER(exp) && (inode->i_writecount > 1 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) { #if 0 - current->timeout = jiffies + 10 * HZ / 1000; - interruptible_sleep_on(&inode->i_wait); + interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000); #else dprintk("nfsd: write defer %d\n", current->pid); - current->need_resched = 1; - current->timeout = jiffies + HZ / 100; - schedule(); + schedule_timeout((HZ+99)/100); dprintk("nfsd: write resume %d\n", current->pid); #endif } @@ -1067,6 +1072,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (IS_ERR(rdentry)) goto out_nfserr; + /* + * FIXME!! + * + * This should do a double-lock on both rdentry and the parent + */ err = fh_lock_parent(fhp, rdentry); if (err) goto out; @@ -1240,6 +1250,12 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); */ +#ifndef CONFIG_NFSD_SUN + if (dentry->d_mounts != dentry) { + return nfserr_perm; + } +#endif + if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (EX_RDONLY(exp) || IS_RDONLY(inode)) return nfserr_rofs; diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index afc219838..9654c8ec2 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -49,7 +49,6 @@ utf8_mbtowc(__u16 *p, const __u8 *s, int n) int c0, c, nc; struct utf8_table *t; - printk("utf8_mbtowc\n"); nc = 0; c0 = *s; l = c0; @@ -80,11 +79,9 @@ utf8_mbstowcs(__u16 *pwcs, const __u8 *s, int n) const __u8 *ip; int size; - printk("\nutf8_mbstowcs: n=%d\n", n); op = pwcs; ip = s; while (*ip && n > 0) { - printk(" %02x", *ip); if (*ip & 0x80) { size = utf8_mbtowc(op, ip, n); if (size == -1) { diff --git a/fs/ntfs/attr.c b/fs/ntfs/attr.c index 7c6a64e0d..8b53cb883 100644 --- a/fs/ntfs/attr.c +++ b/fs/ntfs/attr.c @@ -9,7 +9,7 @@ #include "struct.h" #include "attr.h" -#include <errno.h> +#include <linux/errno.h> #include "macros.h" #include "support.h" #include "util.h" diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index c626035aa..ee995c046 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -8,7 +8,7 @@ #include "struct.h" #include "dir.h" -#include <errno.h> +#include <linux/errno.h> #include "super.h" #include "inode.h" #include "attr.h" diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 05c5a047b..4d2ae30c3 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -10,7 +10,7 @@ #include "struct.h" #include "inode.h" -#include <errno.h> +#include <linux/errno.h> #include "macros.h" #include "attr.h" #include "super.h" diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 7c7b005d3..be1aedbea 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -9,7 +9,7 @@ #include "struct.h" #include "super.h" -#include <errno.h> +#include <linux/errno.h> #include "macros.h" #include "inode.h" #include "support.h" diff --git a/fs/ntfs/types.h b/fs/ntfs/types.h index eabcd8590..f0ca0079f 100644 --- a/fs/ntfs/types.h +++ b/fs/ntfs/types.h @@ -14,7 +14,8 @@ #include <linux/fs.h> #endif -#if defined(i386) || defined(__i386__) || defined(__alpha__) +#if defined(i386) || defined(__i386__) || defined(__alpha__) || \ + defined(__MIPSEL__) /* unsigned integral types */ #ifndef NTFS_INTEGRAL_TYPES diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c index 0cd556177..4c5d456a7 100644 --- a/fs/ntfs/util.c +++ b/fs/ntfs/util.c @@ -12,7 +12,7 @@ #include "struct.h" #include "util.h" -#include <errno.h> +#include <linux/errno.h> /* FreeBSD doesn't seem to have EILSEQ in errno.h */ #ifndef EILSEQ # define EILSEQ EINVAL @@ -4,21 +4,11 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/vfs.h> -#include <linux/types.h> +#include <linux/mm.h> #include <linux/utime.h> -#include <linux/errno.h> #include <linux/fcntl.h> #include <linux/stat.h> -#include <linux/string.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/signal.h> -#include <linux/tty.h> -#include <linux/time.h> -#include <linux/mm.h> #include <linux/file.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> @@ -626,7 +616,7 @@ out: * for the internal routines (ie open_namei()/follow_link() etc). 00 is * used by symlinks. */ -static int do_open(const char * filename, int flags, int mode, int fd) +struct file *filp_open(const char * filename, int flags, int mode) { struct inode * inode; struct dentry * dentry; @@ -667,8 +657,7 @@ static int do_open(const char * filename, int flags, int mode, int fd) } f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - fd_install(fd, f); - return 0; + return f; cleanup_all: if (f->f_mode & FMODE_WRITE) @@ -679,7 +668,19 @@ cleanup_dentry: cleanup_file: put_filp(f); out: - return error; + return ERR_PTR(error); +} + +/* should probably go into sys_open() */ +static int do_open(const char * filename, int flags, int mode, int fd) +{ + struct file * f; + + f = filp_open(filename, flags, mode); + if (IS_ERR(f)) + return PTR_ERR(f); + fd_install(fd, f); + return 0; } /* @@ -796,11 +797,11 @@ int close_fp(struct file *filp, fl_owner_t id) printk("VFS: Close: file count is 0\n"); return 0; } - if (dentry->d_inode) - locks_remove_posix(filp, id); retval = 0; if (filp->f_op && filp->f_op->flush) retval = filp->f_op->flush(filp); + if (dentry->d_inode) + locks_remove_posix(filp, id); fput(filp); return retval; } @@ -4,12 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/fcntl.h> -#include <linux/termios.h> #include <linux/mm.h> #include <linux/file.h> #include <linux/poll.h> @@ -92,7 +86,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf, size_t count, loff_t *ppos) { struct inode * inode = filp->f_dentry->d_inode; - ssize_t chars = 0, free = 0, written = 0; + ssize_t chars = 0, free = 0, written = 0, err=0; char *pipebuf; if (ppos != &filp->f_pos) @@ -107,16 +101,26 @@ static ssize_t pipe_write(struct file * filp, const char * buf, free = count; else free = 1; /* can't do it atomically, wait for any free space */ + up(&inode->i_sem); + if (down_interruptible(&inode->i_atomic_write)) { + down(&inode->i_sem); + return -ERESTARTSYS; + } while (count>0) { while ((PIPE_FREE(*inode) < free) || PIPE_LOCK(*inode)) { if (!PIPE_READERS(*inode)) { /* no readers */ send_sig(SIGPIPE,current,0); - return written? :-EPIPE; + err = -EPIPE; + goto errout; + } + if (signal_pending(current)) { + err = -ERESTARTSYS; + goto errout; + } + if (filp->f_flags & O_NONBLOCK) { + err = -EAGAIN; + goto errout; } - if (signal_pending(current)) - return written? :-ERESTARTSYS; - if (filp->f_flags & O_NONBLOCK) - return written? :-EAGAIN; interruptible_sleep_on(&PIPE_WAIT(*inode)); } PIPE_LOCK(*inode)++; @@ -139,7 +143,10 @@ static ssize_t pipe_write(struct file * filp, const char * buf, } inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); - return written; +errout: + up(&inode->i_atomic_write); + down(&inode->i_sem); + return written ? written : err; } static long long pipe_lseek(struct file * file, long long offset, int orig) diff --git a/fs/proc/array.c b/fs/proc/array.c index eeb933628..632d7137c 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -24,11 +24,11 @@ * <Jeff_Tranter@Mitel.COM> * * Bruno Haible : remove 4K limit for the maps file - * <haible@ma2s2.mathematik.uni-karlsruhe.de> + * <haible@ma2s2.mathematik.uni-karlsruhe.de> * * Yves Arrouye : remove removal of trailing spaces in get_array. * <Yves.Arrouye@marin.fdn.fr> - + * * Jerome Forissier : added per-CPU time information to /proc/stat * and /proc/<pid>/cpu extension * <forissier@isia.cma.fr> @@ -37,6 +37,11 @@ * Hans Marcus <crowbar@concepts.nl> * * aeb@cwi.nl : /proc/partitions + * + * + * Alan Cox : security fixes. + * <Alan.Cox@linux.org> + * */ #include <linux/types.h> @@ -578,6 +583,29 @@ static unsigned long get_wchan(struct task_struct *p) fp = *(unsigned long *) (fp - 12); } while (count ++ < 16); } +#elif defined (__sparc__) + { + unsigned long pc, fp, bias = 0; + unsigned long task_base = (unsigned long) p; + struct reg_window *rw; + int count = 0; + +#ifdef __sparc_v9__ + bias = STACK_BIAS; +#endif + fp = p->tss.ksp + bias; + do { + /* Bogus frame pointer? */ + if (fp < (task_base + sizeof(struct task_struct)) || + fp >= (task_base + (2 * PAGE_SIZE))) + break; + rw = (struct reg_window *) fp; + pc = rw->ins[7]; + if (pc < first_sched || pc >= last_sched) + return pc; + fp = rw->ins[6] + bias; + } while (++count < 16); + } #endif return 0; @@ -605,7 +633,7 @@ static unsigned long get_wchan(struct task_struct *p) if ((tsk)->tss.esp0 > PAGE_SIZE && \ MAP_NR((tsk)->tss.esp0) < max_mapnr) \ eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc; \ - eip; }) + eip; }) #define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) #elif defined(__powerpc__) #define KSTK_EIP(tsk) ((tsk)->tss.regs->nip) @@ -695,16 +723,24 @@ static inline const char * get_task_state(struct task_struct *tsk) static inline char * task_state(struct task_struct *p, char *buffer) { + int g; + buffer += sprintf(buffer, "State:\t%s\n" "Pid:\t%d\n" "PPid:\t%d\n" "Uid:\t%d\t%d\t%d\t%d\n" - "Gid:\t%d\t%d\t%d\t%d\n", + "Gid:\t%d\t%d\t%d\t%d\n" + "Groups:\t", get_task_state(p), p->pid, p->p_pptr->pid, p->uid, p->euid, p->suid, p->fsuid, p->gid, p->egid, p->sgid, p->fsgid); + + for (g = 0; g < p->ngroups; g++) + buffer += sprintf(buffer, "%d ", p->groups[g]); + + buffer += sprintf(buffer, "\n"); return buffer; } @@ -798,9 +834,9 @@ extern inline char *task_cap(struct task_struct *p, char *buffer) return buffer + sprintf(buffer, "CapInh:\t%016x\n" "CapPrm:\t%016x\n" "CapEff:\t%016x\n", - p->cap_inheritable.cap, - p->cap_permitted.cap, - p->cap_effective.cap); + cap_t(p->cap_inheritable), + cap_t(p->cap_permitted), + cap_t(p->cap_effective)); } @@ -866,7 +902,7 @@ static int get_stat(int pid, char * buffer) return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \ -%lu %lu %lu %lu %lu %lu %lu %lu\n", +%lu %lu %lu %lu %lu %lu %lu %lu %d\n", pid, tsk->comm, state, @@ -886,7 +922,7 @@ static int get_stat(int pid, char * buffer) tsk->times.tms_cstime, priority, nice, - tsk->timeout, + 0UL /* removed */, tsk->it_real_value, tsk->start_time, vsize, @@ -907,7 +943,8 @@ static int get_stat(int pid, char * buffer) sigcatch .sig[0] & 0x7fffffffUL, wchan, tsk->nswap, - tsk->cnswap); + tsk->cnswap, + tsk->exit_signal); } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -1246,6 +1283,11 @@ static long get_root_array(char * page, int type, char **start, case PROC_PCI: return get_pci_list(page); #endif + +#ifdef CONFIG_NUBUS + case PROC_NUBUS: + return get_nubus_list(page); +#endif case PROC_CPUINFO: return get_cpuinfo(page); @@ -1323,6 +1365,46 @@ static long get_root_array(char * page, int type, char **start, return -EBADF; } +static int process_unauthorized(int type, int pid) +{ + struct task_struct *p; + uid_t euid; /* Save the euid keep the lock short */ + + read_lock(&tasklist_lock); + + /* + * Grab the lock, find the task, save the uid and + * check it has an mm still (ie its not dead) + */ + + p = find_task_by_pid(pid); + if(p) + { + euid=p->euid; + if(!p->mm) /* Scooby scooby doo where are you ? */ + p=NULL; + } + + read_unlock(&tasklist_lock); + + if (!p) + return 1; + + switch(type) + { + case PROC_PID_STATUS: + case PROC_PID_STATM: + case PROC_PID_STAT: + case PROC_PID_MAPS: + case PROC_PID_CMDLINE: + return 0; + } + if(capable(CAP_DAC_OVERRIDE) || current->fsuid == euid) + return 0; + return 1; +} + + static int get_process_array(char * page, int pid, int type) { switch (type) { @@ -1374,6 +1456,13 @@ static ssize_t array_read(struct file * file, char * buf, type &= 0x0000ffff; start = NULL; dp = (struct proc_dir_entry *) inode->u.generic_ip; + + if (pid && process_unauthorized(type, pid)) + { + free_page(page); + return -EIO; + } + if (dp->get_info) length = dp->get_info((char *)page, &start, *ppos, count, 0); diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 5409e2d67..8db99f7f4 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -119,6 +119,7 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) ino = (pid << 16) + PROC_PID_FD_DIR + fd; inode = proc_get_inode(dir->i_sb, ino, NULL); if (inode) { + dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); err = 0; } diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index ff8ec4877..71c5316a9 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -17,23 +17,23 @@ extern unsigned long log_size; extern struct wait_queue * log_wait; -asmlinkage int sys_syslog(int type, char * bug, int count); +extern int do_syslog(int type, char * bug, int count); static int kmsg_open(struct inode * inode, struct file * file) { - return sys_syslog(1,NULL,0); + return do_syslog(1,NULL,0); } static int kmsg_release(struct inode * inode, struct file * file) { - (void) sys_syslog(0,NULL,0); + (void) do_syslog(0,NULL,0); return 0; } static ssize_t kmsg_read(struct file * file, char * buf, size_t count, loff_t *ppos) { - return sys_syslog(2,buf,count); + return do_syslog(2,buf,count); } static unsigned int kmsg_poll(struct file *file, poll_table * wait) diff --git a/fs/proc/link.c b/fs/proc/link.c index df5ea85e6..70c31f21a 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -17,7 +17,7 @@ #include <linux/stat.h> static int proc_readlink(struct dentry *, char *, int); -static struct dentry * proc_follow_link(struct dentry *, struct dentry *); +static struct dentry * proc_follow_link(struct dentry *, struct dentry *, unsigned int); /* * links can't do much... @@ -57,7 +57,8 @@ struct inode_operations proc_link_inode_operations = { }; static struct dentry * proc_follow_link(struct dentry *dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct inode *inode = dentry->d_inode; struct task_struct *p; @@ -172,7 +173,7 @@ static int proc_readlink(struct dentry * dentry, char * buffer, int buflen) { int error; - dentry = proc_follow_link(dentry, NULL); + dentry = proc_follow_link(dentry, NULL, 1); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = -ENOENT; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 8a8ec9bc0..117587d8d 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -215,7 +215,7 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) pgd_t *src_dir, *dest_dir; pmd_t *src_middle, *dest_middle; pte_t *src_table, *dest_table; - unsigned long stmp, dtmp; + unsigned long stmp, dtmp, mapnr; struct vm_area_struct *src_vma = NULL; struct inode *inode = file->f_dentry->d_inode; @@ -296,7 +296,9 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) set_pte(src_table, pte_mkdirty(*src_table)); set_pte(dest_table, *src_table); - atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count); + mapnr = MAP_NR(pte_page(*src_table)); + if (mapnr < max_mapnr) + atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count); stmp += PAGE_SIZE; dtmp += PAGE_SIZE; diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index b3871059e..2c2c84784 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,7 +1,8 @@ -/* $Id: openpromfs.c,v 1.26 1998/01/28 09:55:32 ecd Exp $ +/* $Id: openpromfs.c,v 1.32 1998/11/18 06:15:20 davem Exp $ * openpromfs.c: /proc/openprom handling routines * - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include <linux/module.h> @@ -27,7 +28,9 @@ typedef struct { typedef struct { #define OPP_STRING 0x10 -#define OPP_BINARY 0x20 +#define OPP_STRINGLIST 0x20 +#define OPP_BINARY 0x40 +#define OPP_HEXSTRING 0x80 #define OPP_DIRTY 0x01 #define OPP_QUOTED 0x02 #define OPP_NOTQUOTED 0x04 @@ -83,7 +86,7 @@ static ssize_t property_read(struct file *filp, char *buf, struct inode *inode = filp->f_dentry->d_inode; int i, j, k; u32 node; - char *p; + char *p, *s; u32 *q; openprom_property *op; char buffer[64]; @@ -129,28 +132,51 @@ static ssize_t property_read(struct file *filp, char *buf, return -EIO; op->value [k] = 0; if (k) { - for (p = op->value; *p >= ' ' && *p <= '~'; p++); - if (p >= op->value + k - 1 && !*p) { - op->flag |= OPP_STRING; - if (p == op->value + k - 1) { - op->flag |= OPP_ASCIIZ; - op->len--; + for (s = 0, p = op->value; p < op->value + k; p++) { + if ((*p >= ' ' && *p <= '~') || *p == '\n') { + op->flag |= OPP_STRING; + s = p; + continue; } - } else if (!(k & 3)) - op->flag |= OPP_BINARY; - else { - printk ("/proc/openprom: Strange property " - "size %d\n", i); - return -EIO; + if (p > op->value && !*p && s == p - 1) { + if (p < op->value + k - 1) + op->flag |= OPP_STRINGLIST; + else + op->flag |= OPP_ASCIIZ; + continue; + } + if (k == 1 && !*p) { + op->flag |= (OPP_STRING|OPP_ASCIIZ); + break; + } + op->flag &= ~(OPP_STRING|OPP_STRINGLIST); + if (k & 3) + op->flag |= OPP_HEXSTRING; + else + op->flag |= OPP_BINARY; + break; } + if (op->flag & OPP_STRINGLIST) + op->flag &= ~(OPP_STRING); + if (op->flag & OPP_ASCIIZ) + op->len--; } } else op = (openprom_property *)filp->private_data; - if (!count || !op->len) return 0; - if (op->flag & OPP_STRING) + if (!count || !(op->len || (op->flag & OPP_ASCIIZ))) + return 0; + if (op->flag & OPP_STRINGLIST) { + for (k = 0, p = op->value; p < op->value + op->len; p++) + if (!*p) + k++; + i = op->len + 4 * k + 3; + } else if (op->flag & OPP_STRING) { i = op->len + 3; - else - i = (op->len * 9)>>2; + } else if (op->flag & OPP_BINARY) { + i = (op->len * 9) >> 2; + } else { + i = (op->len << 1) + 1; + } k = filp->f_pos; if (k >= i) return 0; if (count > i - k) count = i - k; @@ -160,20 +186,48 @@ static ssize_t property_read(struct file *filp, char *buf, k++; count--; } + if (k + count >= i - 2) j = i - 2 - k; else j = count; + if (j >= 0) { copy_to_user(buf + k - filp->f_pos, op->value + k - 1, j); count -= j; k += j; } + if (count) __put_user('\'', &buf [k++ - filp->f_pos]); if (count > 1) __put_user('\n', &buf [k++ - filp->f_pos]); + + } else if (op->flag & OPP_STRINGLIST) { + char *tmp; + + tmp = kmalloc (i, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + s = tmp; + *s++ = '\''; + for (p = op->value; p < op->value + op->len; p++) { + if (!*p) { + strcpy(s, "' + '"); + s += 5; + continue; + } + *s++ = *p; + } + strcpy(s, "'\n"); + + copy_to_user(buf, tmp + k, count); + + kfree(tmp); + k += count; + } else if (op->flag & OPP_BINARY) { char buffer[10]; u32 *first, *last; @@ -205,9 +259,35 @@ static ssize_t property_read(struct file *filp, char *buf, } } } + if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) __put_user('\n', (buf - 1)); + k += count; + + } else if (op->flag & OPP_HEXSTRING) { + char buffer[2]; + + if ((k < i - 1) && (k & 1)) { + sprintf (buffer, "%02x", *(op->value + (k >> 1))); + __put_user(buffer[1], &buf[k++ - filp->f_pos]); + count--; + } + + for (; (count > 1) && (k < i - 1); k += 2) { + sprintf (buffer, "%02x", *(op->value + (k >> 1))); + copy_to_user (buf + k - filp->f_pos, buffer, 2); + count -= 2; + } + + if (count && (k < i - 1)) { + sprintf (buffer, "%02x", *(op->value + (k >> 1))); + __put_user(buffer[0], &buf[k++ - filp->f_pos]); + count--; + } + + if (count) + __put_user('\n', &buf [k++ - filp->f_pos]); } count = k - filp->f_pos; filp->f_pos = k; @@ -742,6 +822,10 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry) inode->i_rdev = d->rdev; break; } + + inode->i_gid = 0; + inode->i_uid = 0; + d_add(dentry, inode); return 0; } diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index c53a440fe..cd4aca324 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -42,7 +42,7 @@ static int property_read_proc(char *page, char **start, off_t off, */ static int devtree_readlink(struct dentry *, char *, int); -static struct dentry *devtree_follow_link(struct dentry *, struct dentry *); +static struct dentry *devtree_follow_link(struct dentry *, struct dentry *, unsigned int); struct inode_operations devtree_symlink_inode_operations = { NULL, /* no file-operations */ @@ -66,7 +66,8 @@ struct inode_operations devtree_symlink_inode_operations = { }; static struct dentry *devtree_follow_link(struct dentry *dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct inode *inode = dentry->d_inode; struct proc_dir_entry * de; @@ -74,7 +75,7 @@ static struct dentry *devtree_follow_link(struct dentry *dentry, de = (struct proc_dir_entry *) inode->u.generic_ip; link = (char *) de->data; - return lookup_dentry(link, base, 1); + return lookup_dentry(link, base, follow); } static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen) diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index 6a6ef687e..5537a35e6 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -13,7 +13,9 @@ extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, extern struct inode_operations proc_scsi_inode_operations; extern struct proc_dir_entry proc_sys_root; +#ifdef CONFIG_SYSCTL EXPORT_SYMBOL(proc_sys_root); +#endif EXPORT_SYMBOL(proc_register); EXPORT_SYMBOL(proc_unregister); EXPORT_SYMBOL(create_proc_entry); diff --git a/fs/proc/root.c b/fs/proc/root.c index e2889d123..5f3044f1f 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -55,9 +55,6 @@ static struct file_operations proc_dir_operations = { NULL /* can't fsync */ }; -int proc_readlink(struct dentry * dentry, char * buffer, int buflen); -struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base); - /* * proc directories can do almost nothing.. */ @@ -388,12 +385,13 @@ static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen) } static struct dentry * proc_self_follow_link(struct dentry *dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { char tmp[30]; sprintf(tmp, "%d", current->pid); - return lookup_dentry(tmp, base, 1); + return lookup_dentry(tmp, base, follow); } int proc_readlink(struct dentry * dentry, char * buffer, int buflen) @@ -420,7 +418,7 @@ int proc_readlink(struct dentry * dentry, char * buffer, int buflen) return len; } -struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base) +struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) { struct inode *inode = dentry->d_inode; struct proc_dir_entry * de; @@ -435,7 +433,7 @@ struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base) if (de->readlink_proc) len = de->readlink_proc(de, page); - d = lookup_dentry(page, base, 1); + d = lookup_dentry(page, base, follow); free_page((unsigned long) page); return d; } @@ -742,7 +740,7 @@ proc_delete_dentry(struct dentry * dentry) d_drop(dentry); } -static struct dentry_operations proc_dentry_operations = +struct dentry_operations proc_dentry_operations = { NULL, /* revalidate */ NULL, /* d_hash */ @@ -839,6 +837,7 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry) inode = proc_get_inode(dir->i_sb, ino, &proc_pid); if (!inode) return -EINVAL; + inode->i_flags|=S_IMMUTABLE; } dentry->d_op = &proc_dentry_operations; @@ -955,6 +954,7 @@ static int proc_root_readdir(struct file * filp, for (i = 0; i < nr_pids; i++) { int pid = pid_array[i]; + ino_t ino = (pid << 16) + PROC_PID_INO; unsigned long j = PROC_NUMBUF; do { @@ -963,7 +963,7 @@ static int proc_root_readdir(struct file * filp, pid /= 10; } while (pid); - if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0) + if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) break; filp->f_pos++; } diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index f26733d9a..499980126 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -180,11 +180,7 @@ static struct file_operations qnx4_file_operations = struct inode_operations qnx4_file_inode_operations = { &qnx4_file_operations, /* default file operations */ -#ifdef CONFIG_QNX4FS_RW - qnx4_create, /* create */ -#else - NULL, -#endif + NULL, /* create? It's not a directory */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 99cbf05eb..9c6617f50 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -199,7 +199,7 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry) goto end_rmdir; } #endif - if (dentry->d_count > 1) { + if (!list_empty(&dentry->d_hash)) { retval = -EBUSY; goto end_rmdir; } @@ -245,9 +245,6 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry) 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)) { diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c index f590d04a6..36679c52a 100644 --- a/fs/qnx4/symlinks.c +++ b/fs/qnx4/symlinks.c @@ -13,7 +13,6 @@ /* THIS FILE HAS TO BE REWRITTEN */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/fs.h> @@ -32,11 +31,7 @@ static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *); struct inode_operations qnx4_symlink_inode_operations = { NULL, /* no file-operations */ -#ifdef CONFIG_QNX4FS_RW - qnx4_create, /* create */ -#else - NULL, -#endif + NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ diff --git a/fs/read_write.c b/fs/read_write.c index e36a2d1e1..00fa17ed6 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -4,19 +4,12 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/types.h> -#include <linux/errno.h> +#include <linux/malloc.h> #include <linux/stat.h> -#include <linux/kernel.h> -#include <linux/sched.h> #include <linux/fcntl.h> #include <linux/file.h> -#include <linux/mm.h> #include <linux/uio.h> -#include <linux/malloc.h> -#include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/limits.h> #include <asm/uaccess.h> diff --git a/fs/readdir.c b/fs/readdir.c index f517c9a87..305b1cc74 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -6,12 +6,9 @@ #include <linux/sched.h> #include <linux/mm.h> -#include <linux/types.h> #include <linux/errno.h> #include <linux/stat.h> #include <linux/file.h> -#include <linux/kernel.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index aa361336a..211563b1c 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -451,7 +451,8 @@ out: } static struct dentry *romfs_follow_link(struct dentry *dentry, - struct dentry *base) + struct dentry *base, + unsigned int follow) { struct inode *inode = dentry->d_inode; char *link; @@ -470,7 +471,7 @@ static struct dentry *romfs_follow_link(struct dentry *dentry, } else link[len] = 0; - dentry = lookup_dentry(link, base, 1); + dentry = lookup_dentry(link, base, follow); kfree(link); if (0) { diff --git a/fs/select.c b/fs/select.c index 7ab56a4b8..38742c84e 100644 --- a/fs/select.c +++ b/fs/select.c @@ -10,25 +10,12 @@ * parameter to reflect time remaining. */ -#include <linux/types.h> -#include <linux/time.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/personality.h> -#include <linux/mm.h> #include <linux/malloc.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/file.h> #include <asm/uaccess.h> -#include <asm/system.h> #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) @@ -124,28 +111,25 @@ get_max: #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLEX_SET (POLLPRI) -int do_select(int n, fd_set_buffer *fds, unsigned long timeout) +int do_select(int n, fd_set_buffer *fds, long *timeout) { poll_table wait_table, *wait; - int retval; - int i; - - lock_kernel(); + int retval, i; + long __timeout = *timeout; wait = NULL; - current->timeout = timeout; - if (timeout) { - struct poll_table_entry *entry = (struct poll_table_entry *) - __get_free_page(GFP_KERNEL); - if (!entry) { - retval = -ENOMEM; - goto out_nowait; - } + if (__timeout) { + struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL); + if (!entry) + return -ENOMEM; + wait_table.nr = 0; wait_table.entry = entry; wait = &wait_table; } + lock_kernel(); + retval = max_select_fd(n, fds); if (retval < 0) goto out; @@ -190,18 +174,22 @@ int do_select(int n, fd_set_buffer *fds, unsigned long timeout) } } wait = NULL; - if (retval || !current->timeout || signal_pending(current)) + if (retval || !__timeout || signal_pending(current)) break; - schedule(); + __timeout = schedule_timeout(__timeout); } current->state = TASK_RUNNING; out: - if (timeout) { + if (*timeout) { free_wait(&wait_table); free_page((unsigned long) wait_table.entry); } -out_nowait: + + /* + * Up-to-date the caller timeout. + */ + *timeout = __timeout; unlock_kernel(); return retval; } @@ -214,14 +202,17 @@ out_nowait: * Update: ERESTARTSYS breaks at least the xview clock binary, so * I'm trying ERESTARTNOHAND which restart only when you want to. */ +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { fd_set_buffer *fds; - unsigned long timeout; + long timeout; int ret; - timeout = ~0UL; + timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { time_t sec, usec; @@ -230,10 +221,10 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; - timeout = ROUND_UP(usec, 1000000/HZ); - timeout += sec * (unsigned long) HZ; - if (timeout) - timeout += jiffies + 1; + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = ROUND_UP(usec, 1000000/HZ); + timeout += sec * (unsigned long) HZ; + } } ret = -ENOMEM; @@ -253,12 +244,11 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) zero_fd_set(n, fds->res_out); zero_fd_set(n, fds->res_ex); - ret = do_select(n, fds, timeout); + ret = do_select(n, fds, &timeout); if (tvp && !(current->personality & STICKY_TIMEOUTS)) { - unsigned long timeout = current->timeout - jiffies - 1; time_t sec = 0, usec = 0; - if ((long) timeout > 0) { + if (timeout) { sec = timeout / HZ; usec = timeout % HZ; usec *= (1000000/HZ); @@ -266,7 +256,6 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) put_user(sec, &tvp->tv_sec); put_user(usec, &tvp->tv_usec); } - current->timeout = 0; if (ret < 0) goto out; @@ -287,7 +276,8 @@ out_nofds: return ret; } -static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) +static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, + long timeout) { int count = 0; @@ -321,15 +311,15 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait) } wait = NULL; - if (count || !current->timeout || signal_pending(current)) + if (count || !timeout || signal_pending(current)) break; - schedule(); + timeout = schedule_timeout(timeout); } current->state = TASK_RUNNING; return count; } -asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) +asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) { int i, fdcount, err, size; struct pollfd * fds, *fds1; @@ -342,9 +332,9 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) goto out; if (timeout < 0) - timeout = 0x7fffffff; + timeout = MAX_SCHEDULE_TIMEOUT; else if (timeout) - timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1; + timeout = (timeout*HZ+999)/1000+1; err = -ENOMEM; if (timeout) { @@ -366,9 +356,7 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout) if (copy_from_user(fds, ufds, size)) goto out_fds; - current->timeout = timeout; - fdcount = do_poll(nfds, fds, wait); - current->timeout = 0; + fdcount = do_poll(nfds, fds, wait, timeout); /* OK, now copy the revents fields back to user space. */ fds1 = fds; diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 92cd94363..f2ba59c4f 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -479,22 +479,15 @@ smb_rmdir(struct inode *dir, struct dentry *dentry) smb_close(inode); /* - * Prune any child dentries so this dentry can become negative. + * Check that nobody else is using the directory.. */ - if (dentry->d_count > 1) { - shrink_dcache_parent(dentry); - error = -EBUSY; - if (dentry->d_count > 1) - goto out; - } + error = -EBUSY; + if (!list_empty(&dentry->d_hash)) + goto out; smb_invalid_dir_cache(dir); error = smb_proc_rmdir(dentry); - if (!error) - { - smb_renew_times(dentry); - d_delete(dentry); - } + out: return error; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 3c3e87aa6..a6cf24ce1 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -195,40 +195,15 @@ smb_writepage(struct file *file, struct page *page) } static int -smb_updatepage(struct file *file, struct page *page, const char *buffer, - unsigned long offset, unsigned int count, int sync) +smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync) { struct dentry *dentry = file->f_dentry; - unsigned long page_addr = page_address(page); - int result; pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, page->offset+offset, sync); -#ifdef SMBFS_PARANOIA - if (test_bit(PG_locked, &page->flags)) - printk("smb_updatepage: page already locked!\n"); -#endif - set_bit(PG_locked, &page->flags); - atomic_inc(&page->count); - - if (copy_from_user((char *) page_addr + offset, buffer, count)) - goto bad_fault; - result = smb_writepage_sync(dentry, page, offset, count); -out: - free_page(page_addr); - return result; - -bad_fault: -#ifdef SMBFS_PARANOIA -printk("smb_updatepage: fault at addr=%lu, offset=%lu, buffer=%p\n", -page_addr, offset, buffer); -#endif - result = -EFAULT; - clear_bit(PG_uptodate, &page->flags); - smb_unlock_page(page); - goto out; + return smb_writepage_sync(dentry, page, offset, count); } static ssize_t diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index b61bb3277..42981d483 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -520,9 +520,7 @@ server->conn_pid); /* * Wait for the new connection. */ - current->timeout = jiffies + 5*HZ; - interruptible_sleep_on(&server->wait); - current->timeout = 0; + interruptible_sleep_on_timeout(&server->wait, 5*HZ); if (signal_pending(current)) printk("smb_retry: caught signal\n"); @@ -1602,10 +1600,8 @@ ff_dir_handle, ff_resume_key, ff_lastname, mask); /* Windows 95 is not able to deliver answers * to FIND_NEXT fast enough, so sleep 0.2 sec */ - current->timeout = jiffies + HZ / 5; current->state = TASK_INTERRUPTIBLE; - schedule(); - current->timeout = 0; + schedule_timeout(HZ/5); } } @@ -4,15 +4,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/sched.h> #include <linux/mm.h> #include <linux/errno.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/fs.h> #include <linux/file.h> -#include <linux/kernel.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> diff --git a/fs/super.c b/fs/super.c index 843464421..2784d6462 100644 --- a/fs/super.c +++ b/fs/super.c @@ -18,25 +18,15 @@ */ #include <linux/config.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mount.h> #include <linux/malloc.h> -#include <linux/major.h> -#include <linux/stat.h> -#include <linux/errno.h> -#include <linux/string.h> #include <linux/locks.h> -#include <linux/mm.h> -#include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/fd.h> #include <linux/init.h> #include <linux/quotaops.h> +#include <linux/acct.h> -#include <asm/system.h> #include <asm/uaccess.h> -#include <asm/bitops.h> #include <linux/nfs_fs.h> #include <linux/nfs_fs_sb.h> @@ -54,10 +44,6 @@ */ static struct semaphore mount_sem = MUTEX; -#ifdef CONFIG_BSD_PROCESS_ACCT -extern void acct_auto_close(kdev_t); -#endif - extern void wait_for_keypress(void); extern struct file_operations * get_blkfops(unsigned int major); @@ -667,10 +653,7 @@ static int do_umount(kdev_t dev, int unmount_root, int flags) * are no quotas running any more. Just turn them on again. */ DQUOT_OFF(dev); - -#ifdef CONFIG_BSD_PROCESS_ACCT - (void) acct_auto_close(dev); -#endif + acct_auto_close(dev); /* * If we may have to abort operations to get out of this @@ -868,17 +851,19 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha struct vfsmount *vfsmnt; int error; - down(&mount_sem); error = -EACCES; if (!(flags & MS_RDONLY) && dev && is_read_only(dev)) goto out; - /*flags |= MS_RDONLY;*/ + /* + * Do the lookup first to force automounting. + */ dir_d = namei(dir_name); error = PTR_ERR(dir_d); if (IS_ERR(dir_d)) goto out; + down(&mount_sem); error = -ENOTDIR; if (!S_ISDIR(dir_d->d_inode->i_mode)) goto dput_and_out; @@ -906,18 +891,16 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha error = -ENOMEM; vfsmnt = add_vfsmnt(sb, dev_name, dir_name); - if (!vfsmnt) - goto dput_and_out; - d_mount(dir_d, sb->s_root); - error = 0; /* we don't dput(dir_d) - see umount */ - -out: - up(&mount_sem); - return error; + if (vfsmnt) { + d_mount(dget(dir_d), sb->s_root); + error = 0; + } dput_and_out: dput(dir_d); - goto out; + up(&mount_sem); +out: + return error; } @@ -975,6 +958,8 @@ static int do_remount(const char *dir,int flags,char *data) */ shrink_dcache_sb(sb); fsync_dev(sb->s_dev); + if (flags & MS_RDONLY) + acct_auto_close(sb->s_dev); retval = do_remount_sb(sb, flags, data); } dput(dentry); @@ -1226,9 +1211,7 @@ void __init mount_root(void) #ifdef CONFIG_BLK_DEV_INITRD -extern int initmem_freed; - -static int __init do_change_root(kdev_t new_root_dev,const char *put_old) +int __init change_root(kdev_t new_root_dev,const char *put_old) { kdev_t old_root_dev; struct vfsmount *vfsmnt; @@ -1248,7 +1231,7 @@ static int __init do_change_root(kdev_t new_root_dev,const char *put_old) dput(old_pwd); #if 1 shrink_dcache(); - printk("do_change_root: old root has d_count=%d\n", old_root->d_count); + printk("change_root: old root has d_count=%d\n", old_root->d_count); #endif /* * Get the new mount directory @@ -1293,13 +1276,4 @@ static int __init do_change_root(kdev_t new_root_dev,const char *put_old) return -ENOMEM; } -int change_root(kdev_t new_root_dev,const char *put_old) -{ - if (initmem_freed) { - printk (KERN_CRIT "Initmem has been already freed. Staying in initrd\n"); - return -EBUSY; - } - return do_change_root(new_root_dev, put_old); -} - #endif diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c index 5fa82929b..375ff6f75 100644 --- a/fs/sysv/balloc.c +++ b/fs/sysv/balloc.c @@ -52,8 +52,8 @@ void sysv_free_block(struct super_block * sb, unsigned int block) * into this block being freed: */ if (*sb->sv_sb_flc_count == sb->sv_flc_size) { - unsigned short * flc_count; - unsigned long * flc_blocks; + u16 * flc_count; + u32 * flc_blocks; bh = sv_getblk(sb, sb->s_dev, block); if (!bh) { @@ -154,8 +154,8 @@ int sysv_new_block(struct super_block * sb) return 0; } if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */ - unsigned short * flc_count; - unsigned long * flc_blocks; + u16 * flc_count; + u32 * flc_blocks; if (!(bh = sv_bread(sb, sb->s_dev, block))) { printk("sysv_new_block: cannot read free-list block\n"); @@ -247,8 +247,8 @@ unsigned long sysv_count_free_blocks(struct super_block * sb) } /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */ while (1) { - unsigned short * flc_count; - unsigned long * flc_blocks; + u16 * flc_count; + u32 * flc_blocks; if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) { printk("sysv_count_free_blocks: new block %d is not in data zone\n",block); diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index ee99164fb..9a1acc19e 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -62,7 +62,7 @@ struct inode_operations sysv_dir_inode_operations = { NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ - sysv_truncate, /* truncate */ + NULL, /* truncate */ NULL /* permission */ }; diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index 96b0649a4..ddffd7de2 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -26,10 +26,10 @@ /* Sync one block. The block number is * from_coh_ulong(*blockp) if convert=1, *blockp if convert=0. */ -static int sync_block (struct inode * inode, unsigned long * blockp, int convert, int wait) +static int sync_block (struct inode * inode, u32 *blockp, int convert, int wait) { struct buffer_head * bh; - unsigned long tmp, block; + u32 tmp, block; struct super_block * sb; block = tmp = *blockp; @@ -59,11 +59,11 @@ static int sync_block (struct inode * inode, unsigned long * blockp, int convert } /* Sync one block full of indirect pointers and read it because we'll need it. */ -static int sync_iblock (struct inode * inode, unsigned long * iblockp, int convert, +static int sync_iblock (struct inode * inode, u32 * iblockp, int convert, struct buffer_head * *bh, int wait) { int rc; - unsigned long tmp, block; + u32 tmp, block; *bh = NULL; block = tmp = *iblockp; @@ -101,7 +101,7 @@ static int sync_direct(struct inode *inode, int wait) return err; } -static int sync_indirect(struct inode *inode, unsigned long *iblockp, int convert, int wait) +static int sync_indirect(struct inode *inode, u32 *iblockp, int convert, int wait) { int i; struct buffer_head * ind_bh; @@ -115,7 +115,7 @@ static int sync_indirect(struct inode *inode, unsigned long *iblockp, int conver sb = inode->i_sb; for (i = 0; i < sb->sv_ind_per_block; i++) { rc = sync_block (inode, - ((unsigned long *) ind_bh->b_data) + i, sb->sv_convert, + ((u32 *) ind_bh->b_data) + i, sb->sv_convert, wait); if (rc > 0) break; @@ -126,7 +126,7 @@ static int sync_indirect(struct inode *inode, unsigned long *iblockp, int conver return err; } -static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int convert, +static int sync_dindirect(struct inode *inode, u32 *diblockp, int convert, int wait) { int i; @@ -141,7 +141,7 @@ static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int conv sb = inode->i_sb; for (i = 0; i < sb->sv_ind_per_block; i++) { rc = sync_indirect (inode, - ((unsigned long *) dind_bh->b_data) + i, sb->sv_convert, + ((u32 *) dind_bh->b_data) + i, sb->sv_convert, wait); if (rc > 0) break; @@ -152,7 +152,7 @@ static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int conv return err; } -static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int convert, +static int sync_tindirect(struct inode *inode, u32 *tiblockp, int convert, int wait) { int i; @@ -167,7 +167,7 @@ static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int conv sb = inode->i_sb; for (i = 0; i < sb->sv_ind_per_block; i++) { rc = sync_dindirect (inode, - ((unsigned long *) tind_bh->b_data) + i, sb->sv_convert, + ((u32 *) tind_bh->b_data) + i, sb->sv_convert, wait); if (rc > 0) break; diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index 120c71ad1..8718b8b5a 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -112,7 +112,7 @@ struct inode * sysv_new_inode(const struct inode * dir) return NULL; sb = dir->i_sb; inode->i_sb = sb; - inode->i_flags = inode->i_sb->s_flags; + inode->i_flags = 0; lock_super(sb); /* protect against task switches */ if ((*sb->sv_sb_fic_count == 0) || (*sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)-1) == 0) /* Applies only to SystemV2 FS */ diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 583b94c25..f2cf52087 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -30,7 +30,7 @@ #include <linux/string.h> #include <linux/locks.h> #include <linux/init.h> - +#include <asm/byteorder.h> #include <asm/uaccess.h> #if 0 @@ -532,7 +532,7 @@ void sysv_write_super (struct super_block *sb) void sysv_put_super(struct super_block *sb) { - /* we can assume sysv_write_super() has already been called, and + /* we can assume sysv_write_super() has already been called, and that the superblock is locked */ brelse(sb->sv_bh1); if (sb->sv_bh1 != sb->sv_bh2) brelse(sb->sv_bh2); @@ -648,8 +648,8 @@ int sysv_bmap(struct inode * inode,int block_nr) static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create) { struct super_block *sb; - unsigned long tmp; - unsigned long *p; + u32 tmp; + u32 *p; struct buffer_head * result; sb = inode->i_sb; @@ -684,7 +684,7 @@ static struct buffer_head * block_getblk(struct inode * inode, struct buffer_head * bh, int nr, int create) { struct super_block *sb; - unsigned long tmp, block; + u32 tmp, block; sysv_zone_t *p; struct buffer_head * result; @@ -782,26 +782,43 @@ struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create return NULL; } +#ifdef __BIG_ENDIAN + +static inline unsigned long read3byte (unsigned char * p) +{ + return (p[2] | (p[1]<<8) | (p[0]<<16)); +} + +static inline void write3byte (unsigned char *p , unsigned long val) +{ + p[2]=val&0xFF; + p[1]=(val>>8)&0xFF; + p[0]=(val>>16)&0xFF; +} + +#else -static inline unsigned long read3byte (char * p) +static inline unsigned long read3byte (unsigned char * p) { return (unsigned long)(*(unsigned short *)p) | (unsigned long)(*(unsigned char *)(p+2)) << 16; } -static inline void write3byte (char * p, unsigned long val) +static inline void write3byte (unsigned char * p, unsigned long val) { *(unsigned short *)p = (unsigned short) val; *(unsigned char *)(p+2) = val >> 16; } -static inline unsigned long coh_read3byte (char * p) +#endif + +static inline unsigned long coh_read3byte (unsigned char * p) { return (unsigned long)(*(unsigned char *)p) << 16 | (unsigned long)(*(unsigned short *)(p+1)); } -static inline void coh_write3byte (char * p, unsigned long val) +static inline void coh_write3byte (unsigned char * p, unsigned long val) { *(unsigned char *)p = val >> 16; *(unsigned short *)(p+1) = (unsigned short) val; diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 626cc08a9..fdae5c38c 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -476,8 +476,6 @@ repeat: inode = dentry->d_inode; retval = -EPERM; - if (S_ISDIR(inode->i_mode)) - goto end_unlink; if (de->inode != inode->i_ino) { brelse(bh); current->counter = 0; diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c index 2ce56e390..5ebaec7cc 100644 --- a/fs/sysv/symlink.c +++ b/fs/sysv/symlink.c @@ -21,7 +21,7 @@ #include <asm/uaccess.h> static int sysv_readlink(struct dentry *, char *, int); -static struct dentry *sysv_follow_link(struct dentry *, struct dentry *); +static struct dentry *sysv_follow_link(struct dentry *, struct dentry *, unsigned int); /* * symlinks can't do much... @@ -47,7 +47,8 @@ struct inode_operations sysv_symlink_inode_operations = { }; static struct dentry *sysv_follow_link(struct dentry * dentry, - struct dentry * base) + struct dentry * base, + unsigned int follow) { struct inode *inode = dentry->d_inode; struct buffer_head * bh; @@ -58,7 +59,7 @@ static struct dentry *sysv_follow_link(struct dentry * dentry, return ERR_PTR(-EIO); } UPDATE_ATIME(inode); - base = lookup_dentry(bh->b_data, base, 1); + base = lookup_dentry(bh->b_data, base, follow); brelse(bh); return base; } diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index 7c919caff..c318648a9 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -41,8 +41,8 @@ static int trunc_direct(struct inode * inode) { struct super_block * sb; unsigned int i; - unsigned long * p; - unsigned long block; + u32 * p; + u32 block; struct buffer_head * bh; int retry = 0; @@ -71,7 +71,7 @@ repeat: return retry; } -static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt) +static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt) { unsigned long indtmp, indblock; struct super_block * sb; @@ -140,14 +140,14 @@ done: return retry; } -static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt) +static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt) { - unsigned long indtmp, indblock; + u32 indtmp, indblock; struct super_block * sb; struct buffer_head * indbh; unsigned int i; sysv_zone_t * ind; - unsigned long tmp, block; + u32 tmp, block; int retry = 0; indblock = indtmp = *p; @@ -197,14 +197,14 @@ done: return retry; } -static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt) +static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt) { - unsigned long indtmp, indblock; + u32 indtmp, indblock; struct super_block * sb; struct buffer_head * indbh; unsigned int i; sysv_zone_t * ind; - unsigned long tmp, block; + u32 tmp, block; int retry = 0; indblock = indtmp = *p; diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 6567c515b..ed71ec179 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -20,7 +20,6 @@ #include "util.h" #undef UFS_BALLOC_DEBUG -#undef UFS_BALLOC_DEBUG_MORE #ifdef UFS_BALLOC_DEBUG #define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; @@ -28,27 +27,12 @@ #define UFSD(x) #endif -#ifdef UFS_BALLOC_DEBUG_MORE -#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)); \ -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), \ -SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \ -printk("ndir: total %u, fs %u, cg %u\n\n", SWAB32(usb1->fs_cstotal.cs_ndir), \ -SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir)); -#else -#define UFSDM -#endif - - unsigned ufs_add_fragments (struct inode *, unsigned, unsigned, unsigned, int *); unsigned ufs_alloc_fragments (struct inode *, unsigned, unsigned, unsigned, int *); unsigned ufs_alloccg_block (struct inode *, struct ufs_cg_private_info *, unsigned, int *); unsigned ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, unsigned, unsigned); static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[]; +void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int); /* * Free 'count' fragments from fragment number 'fragment' @@ -90,8 +74,6 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count goto failed; } - UFSDM - end_bit = bit + count; bbase = ufs_blknum (bit); blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase); @@ -118,6 +100,8 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count SUB_SWAB32(ucg->cg_cs.cs_nffree, uspi->s_fpb); SUB_SWAB32(usb1->fs_cstotal.cs_nffree, uspi->s_fpb); SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, uspi->s_fpb); + if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD) + ufs_clusteracct (sb, ucpi, blkno, 1); INC_SWAB32(ucg->cg_cs.cs_nbfree); INC_SWAB32(usb1->fs_cstotal.cs_nbfree); INC_SWAB32(sb->fs_cs(cgno).cs_nbfree); @@ -126,8 +110,6 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count INC_SWAB32(ubh_cg_blktot (ucpi, cylno)); } - UFSDM - ubh_mark_buffer_dirty (USPI_UBH, 1); ubh_mark_buffer_dirty (UCPI_UBH, 1); if (sb->s_flags & MS_SYNCHRONOUS) { @@ -166,7 +148,8 @@ void ufs_free_blocks (struct inode * inode, unsigned fragment, unsigned count) { UFSD(("ENTER, fragment %u, count %u\n", fragment, count)) if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) { - ufs_error (sb, "ufs_free_blocks", "internal error"); + ufs_error (sb, "ufs_free_blocks", "internal error, " + "fragment %u, count %u\n", fragment, count); goto failed; } @@ -196,14 +179,14 @@ do_more: goto failed; } - UFSDM - for (i = bit; i < end_bit; i += uspi->s_fpb) { blkno = ufs_fragstoblks(i); if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno); + if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD) + ufs_clusteracct (sb, ucpi, blkno, 1); DQUOT_FREE_BLOCK(sb, inode, uspi->s_fpb); INC_SWAB32(ucg->cg_cs.cs_nbfree); INC_SWAB32(usb1->fs_cstotal.cs_nbfree); @@ -213,8 +196,6 @@ do_more: INC_SWAB32(ubh_cg_blktot(ucpi, cylno)); } - UFSDM - ubh_mark_buffer_dirty (USPI_UBH, 1); ubh_mark_buffer_dirty (UCPI_UBH, 1); if (sb->s_flags & MS_SYNCHRONOUS) { @@ -294,7 +275,6 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, } if (fragment < inode->u.ufs_i.i_lastfrag) { UFSD(("EXIT (ALREADY ALLOCATED)\n")) - printk("hlaska 2\n"); unlock_super (sb); return 0; } @@ -302,7 +282,6 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, else { if (tmp) { UFSD(("EXIT (ALREADY ALLOCATED)\n")) - printk("hlaska 3, fragment %u, tmp %u, oldcount %u\n", fragment, tmp, oldcount); unlock_super(sb); return 0; } @@ -442,8 +421,6 @@ unsigned ufs_add_fragments (struct inode * inode, unsigned fragment, return 0; } - UFSDM - fragno = ufs_dtogd (fragment); fragoff = ufs_fragnum (fragno); for (i = oldcount; i < newcount; i++) @@ -472,9 +449,6 @@ unsigned ufs_add_fragments (struct inode * inode, unsigned fragment, SUB_SWAB32(ucg->cg_cs.cs_nffree, count); SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, count); SUB_SWAB32(usb1->fs_cstotal.cs_nffree, count); - usb1->fs_fmod = SWAB32(1); - - UFSDM ubh_mark_buffer_dirty (USPI_UBH, 1); ubh_mark_buffer_dirty (UCPI_UBH, 1); @@ -556,8 +530,6 @@ cg_found: "internal error, bad magic number on cg %u", cgno); ucg->cg_time = SWAB32(CURRENT_TIME); - UFSDM - if (count == uspi->s_fpb) { result = ufs_alloccg_block (inode, ucpi, goal, err); if (result == (unsigned)-1) @@ -602,10 +574,6 @@ cg_found: INC_SWAB32(ucg->cg_frsum[allocsize - count]); succed: - usb1->fs_fmod = SWAB32(1); - - UFSDM - ubh_mark_buffer_dirty (USPI_UBH, 1); ubh_mark_buffer_dirty (UCPI_UBH, 1); if (sb->s_flags & MS_SYNCHRONOUS) { @@ -652,8 +620,6 @@ unsigned ufs_alloccg_block (struct inode * inode, goto gotit; } - /*** This function should be optimized later ***/ - norot: result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb); if (result == (unsigned)-1) @@ -661,7 +627,9 @@ norot: ucpi->c_rotor = result; gotit: blkno = ufs_fragstoblks(result); - ubh_clrblock(UCPI_UBH, ucpi->c_freeoff, blkno); + ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno); + if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD) + ufs_clusteracct (sb, ucpi, blkno, -1); if(DQUOT_ALLOC_BLOCK(sb, inode, uspi->s_fpb)) { *err = -EDQUOT; return (unsigned)-1; @@ -672,7 +640,6 @@ gotit: cylno = ufs_cbtocylno(result); DEC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(result))); DEC_SWAB32(ubh_cg_blktot(ucpi, cylno)); - usb1->fs_fmod = 1; UFSD(("EXIT, result %u\n", result)) @@ -685,7 +652,7 @@ unsigned ufs_bitmap_search (struct super_block * sb, struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; struct ufs_cylinder_group * ucg; - unsigned start, length, length2, location, result; + unsigned start, length, location, result; unsigned possition, fragsize, blockmap, mask; unsigned swab; @@ -706,8 +673,8 @@ unsigned ufs_bitmap_search (struct super_block * sb, (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, 1 << (count - 1 + (uspi->s_fpb & 7))); if (location == 0) { - length2 = start + 1; - location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length2, + length = start + 1; + location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length, (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, 1 << (count - 1 + (uspi->s_fpb & 7))); if (location == 0) { @@ -717,7 +684,6 @@ unsigned ufs_bitmap_search (struct super_block * sb, return (unsigned)-1; } start = 0; - length = length2; } result = (start + length - location) << 3; ucpi->c_frotor = result; @@ -753,6 +719,64 @@ unsigned ufs_bitmap_search (struct super_block * sb, return (unsigned)-1; } +void ufs_clusteracct(struct super_block * sb, + struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt) +{ + struct ufs_sb_private_info * uspi; + int i, start, end, forw, back; + unsigned swab; + + + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + + if (uspi->s_contigsumsize <= 0) + return; + + if (cnt > 0) + ubh_setbit(UCPI_UBH, ucpi->c_clusteroff, blkno); + else + ubh_clrbit(UCPI_UBH, ucpi->c_clusteroff, blkno); + + /* + * Find the size of the cluster going forward. + */ + start = blkno + 1; + end = start + uspi->s_contigsumsize; + if ( end >= ucpi->c_nclusterblks) + end = ucpi->c_nclusterblks; + i = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_clusteroff, end, start); + if (i > end) + i = end; + forw = i - start; + + /* + * Find the size of the cluster going backward. + */ + start = blkno - 1; + end = start - uspi->s_contigsumsize; + if (end < 0 ) + end = -1; + i = ubh_find_last_zero_bit (UCPI_UBH, ucpi->c_clusteroff, start, end); + if ( i < end) + i = end; + back = start - i; + + /* + * Account for old cluster and the possibly new forward and + * back clusters. + */ + i = back + forw + 1; + if (i > uspi->s_contigsumsize) + i = uspi->s_contigsumsize; + ADD_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (i << 2))), cnt); + if (back > 0) + SUB_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (back << 2))), cnt); + if (forw > 0) + SUB_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (forw << 2))), cnt); +} + + static unsigned char ufs_fragtable_8fpb[] = { 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10, diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index a822438b6..295332d17 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -22,7 +22,6 @@ #include "util.h" #undef UFS_CYLINDER_DEBUG -#undef UFS_CYLINDER_DEBUG_MORE #ifdef UFS_CYLINDER_DEBUG #define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; @@ -50,10 +49,6 @@ static void ufs_read_cylinder (struct super_block * sb, ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr]; ucg = (struct ufs_cylinder_group *)sb->u.ufs_sb.s_ucg[cgno]->b_data; -#ifdef UFS_CYLINDER_DEBUG_MORE - ufs_print_cylinder_stuff (ucg, swab); -#endif - UCPI_UBH->fragment = ufs_cgcmin(cgno); UCPI_UBH->count = uspi->s_cgsize >> sb->s_blocksize_bits; /* @@ -77,7 +72,9 @@ static void ufs_read_cylinder (struct super_block * sb, ucpi->c_iusedoff = SWAB32(ucg->cg_iusedoff); ucpi->c_freeoff = SWAB32(ucg->cg_freeoff); ucpi->c_nextfreeoff = SWAB32(ucg->cg_nextfreeoff); - + ucpi->c_clustersumoff = SWAB32(ucg->cg_u.cg_44.cg_clustersumoff); + ucpi->c_clusteroff = SWAB32(ucg->cg_u.cg_44.cg_clusteroff); + ucpi->c_nclusterblks = SWAB32(ucg->cg_u.cg_44.cg_nclusterblks); UFSD(("EXIT\n")) return; @@ -201,10 +198,12 @@ struct ufs_cg_private_info * ufs_load_cylinder ( sb->u.ufs_sb.s_cg_loaded++; else ufs_put_cylinder (sb, UFS_MAX_GROUP_LOADED-1); + ucpi = sb->u.ufs_sb.s_ucpi[sb->u.ufs_sb.s_cg_loaded - 1]; for (j = sb->u.ufs_sb.s_cg_loaded - 1; j > 0; j--) { sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1]; sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1]; } + sb->u.ufs_sb.s_ucpi[0] = ucpi; ufs_read_cylinder (sb, cgno, 0); } UFSD(("EXIT\n")) diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 470847dca..6fc224512 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -11,8 +11,6 @@ * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen <nkbj@image.dk> partially based * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>. - * - * write support by Daniel Pirkl <daniel.pirkl@email.cz> 1998 */ #include <linux/fs.h> @@ -104,11 +102,11 @@ revalidate: && 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)) { + if ((de->d_reclen == 0) || (ufs_get_de_namlen(de) == 0)) { /* SWAB16() was unneeded -- compare to 0 */ filp->f_pos = (filp->f_pos & - (sb->s_blocksize - 1)) + - sb->s_blocksize; + (sb->s_blocksize - 1)) + + sb->s_blocksize; brelse(bh); return stored; } @@ -119,7 +117,7 @@ revalidate: /* On error, skip the f_pos to the next block. */ filp->f_pos = (filp->f_pos & - (sb->s_blocksize - 1)) + + (sb->s_blocksize - 1)) + sb->s_blocksize; brelse (bh); return stored; @@ -137,8 +135,8 @@ revalidate: unsigned long version = inode->i_version; 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), + UFSD(("namlen %u\n", ufs_get_de_namlen(de))) + error = filldir(dirent, de->d_name, ufs_get_de_namlen(de), filp->f_pos, SWAB32(de->d_ino)); if (error) break; @@ -172,7 +170,7 @@ int ufs_check_dir_entry (const char * function, struct inode * dir, error_msg = "reclen is smaller than minimal"; else if (SWAB16(de->d_reclen) % 4 != 0) error_msg = "reclen % 4 != 0"; - else if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(ufs_namlen(de))) + else if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(ufs_get_de_namlen(de))) error_msg = "reclen is too small for namlen"; else if (dir && ((char *) de - bh->b_data) + SWAB16(de->d_reclen) > dir->i_sb->s_blocksize) @@ -185,7 +183,7 @@ int ufs_check_dir_entry (const char * function, struct inode * dir, "offset=%lu, inode=%lu, reclen=%d, namlen=%d", dir->i_ino, dir->i_size, error_msg, offset, (unsigned long) SWAB32(de->d_ino), - SWAB16(de->d_reclen), ufs_namlen(de)); + SWAB16(de->d_reclen), ufs_get_de_namlen(de)); return (error_msg == NULL ? 1 : 0); } @@ -222,8 +220,8 @@ struct inode_operations ufs_dir_inode_operations = { NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ - ufs_bmap, /* bmap */ - ufs_truncate, /* truncate */ + NULL, /* bmap */ + NULL, /* truncate */ ufs_permission, /* permission */ NULL, /* smap */ }; diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c index 6da43f20a..c592009bc 100644 --- a/fs/ufs/ialloc.c +++ b/fs/ufs/ialloc.c @@ -34,7 +34,6 @@ #include "util.h" #undef UFS_IALLOC_DEBUG -#undef UFS_IALLOC_DEBUG_MORE #ifdef UFS_IALLOC_DEBUG #define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; @@ -42,18 +41,6 @@ #define UFSD(x) #endif -#ifdef UFS_IALLOC_DEBUG_MORE -#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)); \ -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), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \ -printk("ndir: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_ndir), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir)); -#else -#define UFSDM -#endif - - /* * NOTE! When we get the inode, we're the only people * that have access to it, and as such there are no @@ -120,8 +107,6 @@ void ufs_free_inode (struct inode * inode) if (!ufs_cg_chkmagic(ucg)) ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number"); - UFSDM - ucg->cg_time = SWAB32(CURRENT_TIME); is_directory = S_ISDIR(inode->i_mode); @@ -153,8 +138,6 @@ void ufs_free_inode (struct inode * inode) ubh_wait_on_buffer (UCPI_UBH); } - UFSDM - sb->s_dirt = 1; unlock_super (sb); UFSD(("EXIT\n")) @@ -199,7 +182,7 @@ struct inode * ufs_new_inode (const struct inode * dir, int mode, int * err ) usb1 = ubh_get_usb_first(USPI_UBH); inode->i_sb = sb; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; lock_super (sb); @@ -251,8 +234,6 @@ cg_found: if (!ufs_cg_chkmagic(ucg)) ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number"); - UFSDM - start = ucpi->c_irotor; bit = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_iusedoff, uspi->s_ipg, start); if (!(bit < uspi->s_ipg)) { @@ -316,8 +297,6 @@ cg_found: insert_inode_hash(inode); mark_inode_dirty(inode); - UFSDM - unlock_super (sb); if(DQUOT_ALLOC_INODE(sb, inode)) { diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index afea7180a..699d4d2c9 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -53,10 +53,11 @@ static void ufs_print_inode(struct inode * inode) { unsigned swab = inode->i_sb->u.ufs_sb.s_swab; - printk("ino %lu mode 0%6.6o nlink %d uid %d gid %d" - " size %lu blocks %lu\n", + printk("ino %lu mode 0%6.6o nlink %d uid %d uid32 %u" + " gid %d gid32 %u size %lu blocks %lu\n", inode->i_ino, inode->i_mode, inode->i_nlink, - inode->i_uid,inode->i_gid, inode->i_size, inode->i_blocks); + inode->i_uid, inode->u.ufs_i.i_uid, inode->i_gid, + inode->u.ufs_i.i_gid, inode->i_size, inode->i_blocks); printk(" db <%u %u %u %u %u %u %u %u %u %u %u %u>\n", SWAB32(inode->u.ufs_i.i_u1.i_data[0]), SWAB32(inode->u.ufs_i.i_u1.i_data[1]), @@ -117,7 +118,7 @@ int ufs_bmap (struct inode * inode, int fragment) * direct fragment */ if (fragment < UFS_NDIR_FRAGMENT) - return ufs_inode_bmap (inode, fragment); + return (uspi->s_sbbase + ufs_inode_bmap (inode, fragment)); /* * indirect fragment @@ -128,8 +129,9 @@ int ufs_bmap (struct inode * inode, int fragment) UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift)); if (!tmp) return 0; - return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab); + return (uspi->s_sbbase + + ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab)); } /* @@ -141,12 +143,13 @@ int ufs_bmap (struct inode * inode, int fragment) UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift)); if (!tmp) return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); if (!tmp) return 0; - return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab); + return (uspi->s_sbbase + + ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab)); } /* @@ -157,16 +160,17 @@ int ufs_bmap (struct inode * inode, int fragment) UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift)); if (!tmp) return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, uspi, swab); if (!tmp) return 0; - tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), + tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), (fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab); if (!tmp) return 0; - return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize), - fragment & uspi->s_apbmask, uspi, swab); + return (uspi->s_sbbase + + ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize), + fragment & uspi->s_apbmask, uspi, swab)); } static struct buffer_head * ufs_inode_getfrag (struct inode * inode, @@ -197,7 +201,7 @@ repeat: tmp = SWAB32(*p); lastfrag = inode->u.ufs_i.i_lastfrag; if (tmp && fragment < lastfrag) { - result = getblk (sb->s_dev, tmp + blockoff, sb->s_blocksize); + result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); if (tmp == SWAB32(*p)) { UFSD(("EXIT, result %u\n", tmp + blockoff)) return result; @@ -308,7 +312,7 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, repeat: tmp = SWAB32(*p); if (tmp) { - result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); if (tmp == SWAB32(*p)) { brelse (bh); UFSD(("EXIT, result %u\n", tmp + blockoff)) @@ -329,7 +333,6 @@ repeat: tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err); if (!tmp) { if (SWAB32(*p)) { - printk("REPEAT\n"); goto repeat; } else { @@ -464,7 +467,7 @@ void ufs_read_inode (struct inode * inode) return; } - bh = bread (sb->s_dev, ufs_inotofsba(inode->i_ino), sb->s_blocksize); + bh = bread (sb->s_dev, uspi->s_sbbase + ufs_inotofsba(inode->i_ino), sb->s_blocksize); if (!bh) { ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino); return; @@ -483,11 +486,12 @@ void ufs_read_inode (struct inode * inode) * Linux has only 16-bit uid and gid, so we can't support EFT. * Files are dynamically chown()ed to root. */ - inode->i_uid = ufs_uid(ufs_inode); - if (inode->i_uid == UFS_USEEFT) { + inode->i_uid = inode->u.ufs_i.i_uid = ufs_get_inode_uid(ufs_inode); + inode->i_gid = inode->u.ufs_i.i_gid = ufs_get_inode_gid(ufs_inode); + if (inode->u.ufs_i.i_uid >= UFS_USEEFT) { inode->i_uid = 0; } - if (inode->i_gid == UFS_USEEFT) { + if (inode->u.ufs_i.i_gid >= UFS_USEEFT) { inode->i_gid = 0; } @@ -510,8 +514,6 @@ void ufs_read_inode (struct inode * inode) inode->u.ufs_i.i_flags = SWAB32(ufs_inode->ui_flags); inode->u.ufs_i.i_gen = SWAB32(ufs_inode->ui_gen); inode->u.ufs_i.i_shadow = SWAB32(ufs_inode->ui_u3.ui_sun.ui_shadow); - inode->u.ufs_i.i_uid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_uid); - inode->u.ufs_i.i_gid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_gid); inode->u.ufs_i.i_oeftflag = SWAB32(ufs_inode->ui_u3.ui_sun.ui_oeftflag); inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize); @@ -540,8 +542,6 @@ void ufs_read_inode (struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISSOCK(inode->i_mode)) - ; /* nothing */ else if (S_ISFIFO(inode->i_mode)) init_fifo(inode); @@ -558,12 +558,13 @@ static int ufs_update_inode(struct inode * inode, int do_sync) struct buffer_head * bh; struct ufs_inode * ufs_inode; unsigned i; - unsigned swab; + unsigned flags, swab; UFSD(("ENTER, ino %lu\n", inode->i_ino)) sb = inode->i_sb; uspi = sb->u.ufs_sb.s_uspi; + flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; if (inode->i_ino < UFS_ROOTINO || @@ -582,22 +583,15 @@ static int ufs_update_inode(struct inode * inode, int do_sync) ufs_inode->ui_mode = SWAB16(inode->i_mode); ufs_inode->ui_nlink = SWAB16(inode->i_nlink); - if (inode->i_uid == 0 && inode->u.ufs_i.i_uid >= UFS_USEEFT) { - ufs_inode->ui_u3.ui_sun.ui_uid = SWAB32(inode->u.ufs_i.i_uid); - ufs_inode->ui_u1.oldids.ui_suid = (__u16)ufs_inode->ui_u3.ui_sun.ui_uid; - } - else { - ufs_inode->ui_u1.oldids.ui_suid = SWAB16(inode->i_uid); - ufs_inode->ui_u3.ui_sun.ui_uid = (__u32) ufs_inode->ui_u1.oldids.ui_suid; - } - if (inode->i_gid == 0 && inode->u.ufs_i.i_gid >= UFS_USEEFT) { - ufs_inode->ui_u3.ui_sun.ui_gid = SWAB32(inode->u.ufs_i.i_gid); - ufs_inode->ui_u1.oldids.ui_sgid = (__u16)ufs_inode->ui_u3.ui_sun.ui_gid; - } - else { - ufs_inode->ui_u1.oldids.ui_sgid = SWAB16(inode->i_gid); - ufs_inode->ui_u3.ui_sun.ui_gid = (__u32) ufs_inode->ui_u1.oldids.ui_sgid; - } + if (inode->i_uid == 0 && inode->u.ufs_i.i_uid >= UFS_USEEFT) + ufs_set_inode_uid (ufs_inode, inode->u.ufs_i.i_uid); + else + ufs_set_inode_uid (ufs_inode, inode->i_uid); + + if (inode->i_gid == 0 && inode->u.ufs_i.i_gid >= UFS_USEEFT) + ufs_set_inode_gid (ufs_inode, inode->u.ufs_i.i_gid); + else + ufs_set_inode_gid (ufs_inode, inode->i_gid); ufs_inode->ui_size = SWAB64((u64)inode->i_size); ufs_inode->ui_atime.tv_sec = SWAB32(inode->i_atime); @@ -607,11 +601,13 @@ static int ufs_update_inode(struct inode * inode, int do_sync) ufs_inode->ui_mtime.tv_sec = SWAB32(inode->i_mtime); ufs_inode->ui_mtime.tv_usec = SWAB32(0); ufs_inode->ui_blocks = SWAB32(inode->i_blocks); - ufs_inode->ui_flags = SWAB32(inode->u.ufs_i.i_flags); ufs_inode->ui_gen = SWAB32(inode->u.ufs_i.i_gen); - ufs_inode->ui_u3.ui_sun.ui_shadow = SWAB32(inode->u.ufs_i.i_shadow); - ufs_inode->ui_u3.ui_sun.ui_oeftflag = SWAB32(inode->u.ufs_i.i_oeftflag); + + if ((flags & UFS_UID_MASK) == UFS_UID_EFT) { + ufs_inode->ui_u3.ui_sun.ui_shadow = SWAB32(inode->u.ufs_i.i_shadow); + ufs_inode->ui_u3.ui_sun.ui_oeftflag = SWAB32(inode->u.ufs_i.i_oeftflag); + } if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) ufs_inode->ui_u2.ui_addr.ui_db[0] = SWAB32(kdev_t_to_nr(inode->i_rdev)); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 9542d2770..130d53bb5 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -65,10 +65,10 @@ static int ufs_match (int len, const char * const name, /* * "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && ufs_namlen(de) == 1 && (de->d_name[0] == '.') && + if (!len && ufs_get_de_namlen(de) == 1 && (de->d_name[0] == '.') && (de->d_name[1] == '\0')) return 1; - if (len != ufs_namlen(de)) + if (len != ufs_get_de_namlen(de)) return 0; return !memcmp(name, de->d_name, len); } @@ -286,7 +286,7 @@ 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(UFS_SECTOR_SIZE); - de->d_u.d_namlen = SWAB16(0); + ufs_set_de_namlen(de,0); dir->i_size = offset + UFS_SECTOR_SIZE; mark_inode_dirty(dir); } else { @@ -304,18 +304,18 @@ static struct buffer_head * ufs_add_entry (struct inode * dir, return NULL; } if ((SWAB32(de->d_ino) == 0 && SWAB16(de->d_reclen) >= rec_len) || - (SWAB16(de->d_reclen) >= UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)) + rec_len)) { + (SWAB16(de->d_reclen) >= UFS_DIR_REC_LEN(ufs_get_de_namlen(de)) + rec_len)) { offset += SWAB16(de->d_reclen); if (SWAB32(de->d_ino)) { de1 = (struct ufs_dir_entry *) ((char *) de + - UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); + UFS_DIR_REC_LEN(ufs_get_de_namlen(de))); de1->d_reclen = SWAB16(SWAB16(de->d_reclen) - - UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); - de->d_reclen = SWAB16(UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen))); + UFS_DIR_REC_LEN(ufs_get_de_namlen(de))); + de->d_reclen = SWAB16(UFS_DIR_REC_LEN(ufs_get_de_namlen(de))); de = de1; } de->d_ino = SWAB32(0); - de->d_u.d_namlen = SWAB16(namelen); + ufs_set_de_namlen(de, namelen); memcpy (de->d_name, name, namelen + 1); /* * XXX shouldn't update any times until successful @@ -369,7 +369,7 @@ static int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir, de = (struct ufs_dir_entry *) bh->b_data; UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(de->d_ino), - SWAB16(de->d_reclen), ufs_namlen(de), de->d_name)) + SWAB16(de->d_reclen), ufs_get_de_namlen(de), de->d_name)) while (i < bh->b_size) { if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) @@ -410,11 +410,11 @@ int ufs_create (struct inode * dir, struct dentry * dentry, int mode) struct buffer_head * bh; struct ufs_dir_entry * de; int err = -EIO; - unsigned swab; + unsigned flags, swab; sb = dir->i_sb; swab = sb->u.ufs_sb.s_swab; - + flags = sb->u.ufs_sb.s_flags; /* * N.B. Several error exits in ufs_new_inode don't set err. */ @@ -434,6 +434,7 @@ int ufs_create (struct inode * dir, struct dentry * dentry, int mode) return err; } de->d_ino = SWAB32(inode->i_ino); + ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -455,9 +456,10 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) struct buffer_head * bh; struct ufs_dir_entry * de; int err = -EIO; - unsigned swab; + unsigned flags, swab; sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; err = -ENAMETOOLONG; @@ -493,6 +495,7 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) if (!bh) goto out_no_entry; de->d_ino = SWAB32(inode->i_ino); + ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -519,9 +522,10 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct buffer_head * bh, * dir_block; struct ufs_dir_entry * de; int err; - unsigned swab; + unsigned flags, swab; sb = dir->i_sb; + flags = sb->u.ufs_sb.s_flags; swab = sb->u.ufs_sb.s_swab; err = -ENAMETOOLONG; @@ -548,13 +552,15 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) 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); + ufs_set_de_type (de, inode->i_mode); + ufs_set_de_namlen(de,1); de->d_reclen = SWAB16(UFS_DIR_REC_LEN(1)); strcpy (de->d_name, "."); de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen)); de->d_ino = SWAB32(dir->i_ino); + ufs_set_de_type (de, dir->i_mode); de->d_reclen = SWAB16(UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1)); - de->d_u.d_namlen = SWAB16(2); + ufs_set_de_namlen(de,2); strcpy (de->d_name, ".."); inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); @@ -567,6 +573,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) if (!bh) goto out_no_entry; de->d_ino = SWAB32(inode->i_ino); + ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -605,7 +612,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 +621,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 +632,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); @@ -694,7 +701,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry) if (SWAB32(de->d_ino) != inode->i_ino) goto end_rmdir; - down(&inode->i_sem); /* * Prune any child dentries so that this dentry becomes negative. */ @@ -720,7 +726,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry) retval = ufs_delete_entry (dir, de, bh); dir->i_version = ++event; } - up(&inode->i_sem); if (retval) goto end_rmdir; mark_buffer_dirty(bh, 1); @@ -768,7 +773,7 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry) retval = -ENOENT; bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); UFSD(("de: ino %u, reclen %u, namelen %u, name %s\n", SWAB32(de->d_ino), - SWAB16(de->d_reclen), ufs_namlen(de), de->d_name)) + SWAB16(de->d_reclen), ufs_get_de_namlen(de), de->d_name)) if (!bh) goto end_unlink; @@ -777,8 +782,6 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry) inode->i_sb->dq_op->initialize (inode, -1); retval = -EPERM; - if (S_ISDIR(inode->i_mode)) - goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; if ((dir->i_mode & S_ISVTX) && @@ -984,7 +987,7 @@ static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry, UFSD(("name %s, len %u\n", old_dentry->d_name.name, old_dentry->d_name.len)) old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(old_de->d_ino), - SWAB16(old_de->d_reclen), ufs_namlen(old_de), old_de->d_name)) + SWAB16(old_de->d_reclen), ufs_get_de_namlen(old_de), old_de->d_name)) retval = -ENOENT; if (!old_bh) diff --git a/fs/ufs/super.c b/fs/ufs/super.c index d4426ad5d..dd4815469 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1,6 +1,35 @@ /* * linux/fs/ufs/super.c * + * Copyright (C) 1998 + * Daniel Pirkl <daniel.pirkl@email.cz> + * Charles University, Faculty of Mathematics and Physics + */ + +/* Derivated from + * + * linux/fs/ext2/super.c + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 + */ + +/* + * Inspirated by + * + * linux/fs/ufs/super.c + * * Copyright (C) 1996 * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu) * Laboratory for Computer Science Research Computing Facility @@ -28,20 +57,29 @@ * */ + #include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> +#include <stdarg.h> + +#include <asm/bitops.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <linux/errno.h> #include <linux/fs.h> #include <linux/ufs_fs.h> -#include <linux/locks.h> -#include <asm/uaccess.h> #include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <linux/locks.h> +#include <linux/blkdev.h> +#include <linux/init.h> #include "swab.h" #include "util.h" - #undef UFS_SUPER_DEBUG #undef UFS_SUPER_DEBUG_MORE @@ -59,39 +97,41 @@ void ufs_print_super_stuff(struct ufs_super_block_first * usb1, struct ufs_super_block_second * usb2, struct ufs_super_block_third * usb3, unsigned swab) { - printk("\nufs_print_super_stuff\n"); - printk("size of usb: %lu\n", sizeof(struct ufs_super_block)); - printk(" magic: 0x%x\n", SWAB32(usb3->fs_magic)); - printk(" sblkno: %u\n", SWAB32(usb1->fs_sblkno)); - printk(" cblkno: %u\n", SWAB32(usb1->fs_cblkno)); - printk(" iblkno: %u\n", SWAB32(usb1->fs_iblkno)); - printk(" dblkno: %u\n", SWAB32(usb1->fs_dblkno)); - printk(" cgoffset: %u\n", SWAB32(usb1->fs_cgoffset)); - printk(" ~cgmask: 0x%x\n", ~SWAB32(usb1->fs_cgmask)); - printk(" size: %u\n", SWAB32(usb1->fs_size)); - printk(" dsize: %u\n", SWAB32(usb1->fs_dsize)); - printk(" ncg: %u\n", SWAB32(usb1->fs_ncg)); - printk(" bsize: %u\n", SWAB32(usb1->fs_bsize)); - printk(" fsize: %u\n", SWAB32(usb1->fs_fsize)); - printk(" frag: %u\n", SWAB32(usb1->fs_frag)); - printk(" fragshift: %u\n", SWAB32(usb1->fs_fragshift)); - printk(" ~fmask: %u\n", ~SWAB32(usb1->fs_fmask)); - printk(" fshift: %u\n", SWAB32(usb1->fs_fshift)); - printk(" sbsize: %u\n", SWAB32(usb1->fs_sbsize)); - printk(" spc: %u\n", SWAB32(usb1->fs_spc)); - printk(" cpg: %u\n", SWAB32(usb1->fs_cpg)); - printk(" ipg: %u\n", SWAB32(usb1->fs_ipg)); - printk(" fpg: %u\n", SWAB32(usb1->fs_fpg)); - printk(" csaddr: %u\n", SWAB32(usb1->fs_csaddr)); - printk(" cssize: %u\n", SWAB32(usb1->fs_cssize)); - printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize)); - printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb)); - printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat)); - printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos)); - printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir)); - printk(" nifree %u\n", SWAB32(usb1->fs_cstotal.cs_nifree)); - printk(" nbfree %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree)); - printk(" nffree %u\n", SWAB32(usb1->fs_cstotal.cs_nffree)); + printk("ufs_print_super_stuff\n"); + printk("size of usb: %u\n", sizeof(struct ufs_super_block)); + printk(" magic: 0x%x\n", SWAB32(usb3->fs_magic)); + printk(" sblkno: %u\n", SWAB32(usb1->fs_sblkno)); + printk(" cblkno: %u\n", SWAB32(usb1->fs_cblkno)); + printk(" iblkno: %u\n", SWAB32(usb1->fs_iblkno)); + printk(" dblkno: %u\n", SWAB32(usb1->fs_dblkno)); + printk(" cgoffset: %u\n", SWAB32(usb1->fs_cgoffset)); + printk(" ~cgmask: 0x%x\n", ~SWAB32(usb1->fs_cgmask)); + printk(" size: %u\n", SWAB32(usb1->fs_size)); + printk(" dsize: %u\n", SWAB32(usb1->fs_dsize)); + printk(" ncg: %u\n", SWAB32(usb1->fs_ncg)); + printk(" bsize: %u\n", SWAB32(usb1->fs_bsize)); + printk(" fsize: %u\n", SWAB32(usb1->fs_fsize)); + printk(" frag: %u\n", SWAB32(usb1->fs_frag)); + printk(" fragshift: %u\n", SWAB32(usb1->fs_fragshift)); + printk(" ~fmask: %u\n", ~SWAB32(usb1->fs_fmask)); + printk(" fshift: %u\n", SWAB32(usb1->fs_fshift)); + printk(" sbsize: %u\n", SWAB32(usb1->fs_sbsize)); + printk(" spc: %u\n", SWAB32(usb1->fs_spc)); + printk(" cpg: %u\n", SWAB32(usb1->fs_cpg)); + printk(" ipg: %u\n", SWAB32(usb1->fs_ipg)); + printk(" fpg: %u\n", SWAB32(usb1->fs_fpg)); + printk(" csaddr: %u\n", SWAB32(usb1->fs_csaddr)); + printk(" cssize: %u\n", SWAB32(usb1->fs_cssize)); + printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize)); + printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb)); + printk(" contigsumsize: %d\n", SWAB32(usb3->fs_u.fs_44.fs_contigsumsize)); + printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat)); + printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos)); + printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir)); + printk(" nifree %u\n", SWAB32(usb1->fs_cstotal.cs_nifree)); + printk(" nbfree %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree)); + printk(" nffree %u\n", SWAB32(usb1->fs_cstotal.cs_nffree)); + printk("\n"); } @@ -101,107 +141,411 @@ void ufs_print_super_stuff(struct ufs_super_block_first * usb1, void ufs_print_cylinder_stuff(struct ufs_cylinder_group *cg, unsigned swab) { printk("\nufs_print_cylinder_stuff\n"); - printk("size of ucg: %lu\n", sizeof(struct ufs_cylinder_group)); - printk(" magic: %x\n", SWAB32(cg->cg_magic)); - printk(" time: %u\n", SWAB32(cg->cg_time)); - printk(" cgx: %u\n", SWAB32(cg->cg_cgx)); - printk(" ncyl: %u\n", SWAB16(cg->cg_ncyl)); - printk(" niblk: %u\n", SWAB16(cg->cg_niblk)); - printk(" ndblk: %u\n", SWAB32(cg->cg_ndblk)); - printk(" cs_ndir: %u\n", SWAB32(cg->cg_cs.cs_ndir)); - printk(" cs_nbfree: %u\n", SWAB32(cg->cg_cs.cs_nbfree)); - printk(" cs_nifree: %u\n", SWAB32(cg->cg_cs.cs_nifree)); - printk(" cs_nffree: %u\n", SWAB32(cg->cg_cs.cs_nffree)); - printk(" rotor: %u\n", SWAB32(cg->cg_rotor)); - printk(" frotor: %u\n", SWAB32(cg->cg_frotor)); - printk(" irotor: %u\n", SWAB32(cg->cg_irotor)); - printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n", + printk("size of ucg: %u\n", sizeof(struct ufs_cylinder_group)); + printk(" magic: %x\n", SWAB32(cg->cg_magic)); + printk(" time: %u\n", SWAB32(cg->cg_time)); + printk(" cgx: %u\n", SWAB32(cg->cg_cgx)); + printk(" ncyl: %u\n", SWAB16(cg->cg_ncyl)); + printk(" niblk: %u\n", SWAB16(cg->cg_niblk)); + printk(" ndblk: %u\n", SWAB32(cg->cg_ndblk)); + printk(" cs_ndir: %u\n", SWAB32(cg->cg_cs.cs_ndir)); + printk(" cs_nbfree: %u\n", SWAB32(cg->cg_cs.cs_nbfree)); + printk(" cs_nifree: %u\n", SWAB32(cg->cg_cs.cs_nifree)); + printk(" cs_nffree: %u\n", SWAB32(cg->cg_cs.cs_nffree)); + printk(" rotor: %u\n", SWAB32(cg->cg_rotor)); + printk(" frotor: %u\n", SWAB32(cg->cg_frotor)); + printk(" irotor: %u\n", SWAB32(cg->cg_irotor)); + printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n", SWAB32(cg->cg_frsum[0]), SWAB32(cg->cg_frsum[1]), SWAB32(cg->cg_frsum[2]), SWAB32(cg->cg_frsum[3]), SWAB32(cg->cg_frsum[4]), SWAB32(cg->cg_frsum[5]), SWAB32(cg->cg_frsum[6]), SWAB32(cg->cg_frsum[7])); - printk(" btotoff: %u\n", SWAB32(cg->cg_btotoff)); - printk(" boff: %u\n", SWAB32(cg->cg_boff)); - printk(" iuseoff: %u\n", SWAB32(cg->cg_iusedoff)); - printk(" freeoff: %u\n", SWAB32(cg->cg_freeoff)); - printk(" nextfreeoff: %u\n", SWAB32(cg->cg_nextfreeoff)); + printk(" btotoff: %u\n", SWAB32(cg->cg_btotoff)); + printk(" boff: %u\n", SWAB32(cg->cg_boff)); + printk(" iuseoff: %u\n", SWAB32(cg->cg_iusedoff)); + printk(" freeoff: %u\n", SWAB32(cg->cg_freeoff)); + printk(" nextfreeoff: %u\n", SWAB32(cg->cg_nextfreeoff)); + printk(" clustersumoff %u\n", SWAB32(cg->cg_u.cg_44.cg_clustersumoff)); + printk(" clusteroff %u\n", SWAB32(cg->cg_u.cg_44.cg_clusteroff)); + printk(" nclusterblks %u\n", SWAB32(cg->cg_u.cg_44.cg_nclusterblks)); + printk("\n"); } #endif /* UFS_SUPER_DEBUG_MORE */ +static char error_buf[1024]; + +void ufs_error (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + va_list args; + + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + if (!(sb->s_flags & MS_RDONLY)) { + usb1->fs_clean = UFS_FSBAD; + ubh_mark_buffer_dirty(USPI_UBH, 1); + sb->s_dirt = 1; + sb->s_flags |= MS_RDONLY; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_ONERROR) { + case UFS_MOUNT_ONERROR_PANIC: + panic ("UFS-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); + + case UFS_MOUNT_ONERROR_LOCK: + case UFS_MOUNT_ONERROR_UMOUNT: + case UFS_MOUNT_ONERROR_REPAIR: + printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); + } +} + +void ufs_panic (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + struct ufs_sb_private_info * uspi; + struct ufs_super_block_first * usb1; + va_list args; + + uspi = sb->u.ufs_sb.s_uspi; + usb1 = ubh_get_usb_first(USPI_UBH); + + if (!(sb->s_flags & MS_RDONLY)) { + usb1->fs_clean = UFS_FSBAD; + ubh_mark_buffer_dirty(USPI_UBH, 1); + sb->s_dirt = 1; + } + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + /* this is to prevent panic from syncing this filesystem */ + if (sb->s_lock) + sb->s_lock = 0; + sb->s_flags |= MS_RDONLY; + printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +/*** + panic ("UFS-fs panic (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +***/ +} + +void ufs_warning (struct super_block * sb, const char * function, + const char * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vsprintf (error_buf, fmt, args); + va_end (args); + printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n", + kdevname(sb->s_dev), function, error_buf); +} + +static int ufs_parse_options (char * options, unsigned * mount_options) +{ + char * this_char; + char * value; + + UFSD(("ENTER\n")) + + if (!options) + return 1; + + for (this_char = strtok (options, ","); + this_char != NULL; + this_char = strtok (NULL, ",")) { + + if ((value = strchr (this_char, '=')) != NULL) + *value++ = 0; + if (!strcmp (this_char, "ufstype")) { + ufs_clear_opt (*mount_options, UFSTYPE); + if (!strcmp (value, "old")) + ufs_set_opt (*mount_options, UFSTYPE_OLD); + else if (!strcmp (value, "sun")) + ufs_set_opt (*mount_options, UFSTYPE_SUN); + else if (!strcmp (value, "44bsd")) + ufs_set_opt (*mount_options, UFSTYPE_44BSD); + else if (!strcmp (value, "next")) + ufs_set_opt (*mount_options, UFSTYPE_NEXT); + else { + printk ("UFS-fs: Invalid type option: %s\n", value); + return 0; + } + } + else if (!strcmp (this_char, "onerror")) { + ufs_clear_opt (*mount_options, ONERROR); + if (!strcmp (value, "panic")) + ufs_set_opt (*mount_options, ONERROR_PANIC); + else if (!strcmp (value, "lock")) + ufs_set_opt (*mount_options, ONERROR_LOCK); + else if (!strcmp (value, "umount")) + ufs_set_opt (*mount_options, ONERROR_UMOUNT); + else if (!strcmp (value, "repair")) { + printk("UFS-fs: Unable to do repair on error, " + "will lock lock instead \n"); + ufs_set_opt (*mount_options, ONERROR_REPAIR); + } + else { + printk ("UFS-fs: Invalid action onerror: %s\n", value); + return 0; + } + } + else { + printk("UFS-fs: Invalid option: %s\n", this_char); + return 0; + } + } + return 1; +} + +/* + * Read on-disk structures asscoiated with cylinder groups + */ +int ufs_read_cylinder_structures (struct super_block * sb) { + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * ubh; + unsigned char * base, * space; + unsigned size, blks, i; + unsigned swab; + + UFSD(("ENTER\n")) + + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; + + /* + * Read cs structures from (usually) first data block + * on the device. + */ + size = uspi->s_cssize; + blks = howmany(size, uspi->s_fsize); + base = space = kmalloc(size, GFP_KERNEL); + if (!base) + goto failed; + for (i = 0; i < blks; i += uspi->s_fpb) { + size = uspi->s_bsize; + if (i + uspi->s_fpb > blks) + size = (blks - i) * uspi->s_fsize; + ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size); + if (!ubh) + goto failed; + ubh_ubhcpymem (space, ubh, size); + sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space; + space += size; + ubh_brelse (ubh); + ubh = NULL; + } + + /* + * Read cylinder group (we read only first fragment from block + * at this time) and prepare internal data structures for cg caching. + */ + if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL))) + goto failed; + for (i = 0; i < uspi->s_ncg; i++) + sb->u.ufs_sb.s_ucg[i] = NULL; + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { + sb->u.ufs_sb.s_ucpi[i] = NULL; + sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; + } + for (i = 0; i < uspi->s_ncg; i++) { + UFSD(("read cg %u\n", i)) + if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize))) + goto failed; + if (!ufs_cg_chkmagic ((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data)) + goto failed; +#ifdef UFS_SUPER_DEBUG_MORE + ufs_print_cylinder_stuff((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data, swab); +#endif + } + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { + if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL))) + goto failed; + sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; + } + sb->u.ufs_sb.s_cg_loaded = 0; + UFSD(("EXIT\n")) + return 1; + +failed: + if (base) kfree (base); + if (sb->u.ufs_sb.s_ucg) { + for (i = 0; i < uspi->s_ncg; i++) + if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]); + kfree (sb->u.ufs_sb.s_ucg); + for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) + if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]); + } + UFSD(("EXIT (FAILED)\n")) + return 0; +} + /* - * Called while file system is mounted, read super block - * and create important internal structures. + * Put on-disk structures associated with cylidner groups and + * write them back to disk */ -struct super_block * ufs_read_super ( - struct super_block * sb, - void * data, +void ufs_put_cylinder_structures (struct super_block * sb) { + struct ufs_sb_private_info * uspi; + struct ufs_buffer_head * ubh; + unsigned char * base, * space; + unsigned blks, size, i; + + UFSD(("ENTER\n")) + + uspi = sb->u.ufs_sb.s_uspi; + + size = uspi->s_cssize; + blks = howmany(size, uspi->s_fsize); + base = space = (char*) sb->u.ufs_sb.s_csp[0]; + for (i = 0; i < blks; i += uspi->s_fpb) { + size = uspi->s_bsize; + if (i + uspi->s_fpb > blks) + size = (blks - i) * uspi->s_fsize; + ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size); + ubh_memcpyubh (ubh, space, size); + space += size; + ubh_mark_buffer_uptodate (ubh, 1); + ubh_mark_buffer_dirty (ubh, 0); + ubh_brelse (ubh); + } + for (i = 0; i < sb->u.ufs_sb.s_cg_loaded; i++) { + ufs_put_cylinder (sb, i); + kfree (sb->u.ufs_sb.s_ucpi[i]); + } + for (; i < UFS_MAX_GROUP_LOADED; i++) + kfree (sb->u.ufs_sb.s_ucpi[i]); + for (i = 0; i < uspi->s_ncg; i++) + brelse (sb->u.ufs_sb.s_ucg[i]); + kfree (sb->u.ufs_sb.s_ucg); + kfree (base); + UFSD(("EXIT\n")) +} + +struct super_block * ufs_read_super (struct super_block * sb, void * data, int silent) { struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; struct ufs_super_block_second * usb2; struct ufs_super_block_third * usb3; - struct ufs_buffer_head * ubh; - unsigned char * base, * space; - unsigned size, blks, i; + struct ufs_buffer_head * ubh; unsigned block_size, super_block_size; unsigned flags, swab; - s64 tmp; - static unsigned offsets[] = {0, 96, 160}; /* different superblock locations */ - - UFSD(("ENTER\n")) - + uspi = NULL; ubh = NULL; - 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. - */ - + UFSD(("ENTER\n")) + MOD_INC_USE_COUNT; lock_super (sb); - sb->u.ufs_sb.s_uspi = uspi = + /* + * Set default mount options + * Parse mount options + */ + sb->u.ufs_sb.s_mount_opt = 0; + ufs_set_opt (sb->u.ufs_sb.s_mount_opt, ONERROR_LOCK); + if (!ufs_parse_options ((char *) data, &sb->u.ufs_sb.s_mount_opt)) { + printk("wrong mount options\n"); + goto failed; + } + if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) { + printk("You didn't specify type of your ufs file system\n\n" + " mount -t ufs -o ufstype=sun|44bsd|old|next ....\n\n" + "!!! WARNING !!! wrong value may corrupt you file system\n" + "default value is ufstype=old\n"); + ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD); + } + + sb->u.ufs_sb.s_uspi = uspi = kmalloc (sizeof(struct ufs_sb_private_info), GFP_KERNEL); if (!uspi) goto failed; + + switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) { + case UFS_MOUNT_UFSTYPE_44BSD: + UFSD(("44bsd ufstype\n")) + uspi->s_fsize = block_size = 512; + uspi->s_fmask = ~(512 - 1); + uspi->s_fshift = 9; + uspi->s_sbsize = super_block_size = 1536; + uspi->s_sbbase = 0; + flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD; + break; + + case UFS_MOUNT_UFSTYPE_SUN: + UFSD(("sun ufstype\n")) + uspi->s_fsize = block_size = 1024; + uspi->s_fmask = ~(1024 - 1); + uspi->s_fshift = 10; + uspi->s_sbsize = super_block_size = 2048; + uspi->s_sbbase = 0; + flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUN | UFS_CG_SUN; + break; + + case UFS_MOUNT_UFSTYPE_OLD: + UFSD(("old ufstype\n")) + uspi->s_fsize = block_size = 1024; + uspi->s_fmask = ~(1024 - 1); + uspi->s_fshift = 10; + uspi->s_sbsize = super_block_size = 2048; + uspi->s_sbbase = 0; + flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD; + if (!(sb->s_flags & MS_RDONLY)) { + printk("old type of ufs is supported read-only\n"); + goto failed; + } + break; - block_size = BLOCK_SIZE; - super_block_size = BLOCK_SIZE * 2; + case UFS_MOUNT_UFSTYPE_NEXT: + UFSD(("next ufstype\n")) + uspi->s_fsize = block_size = 1024; + uspi->s_fmask = ~(1024 - 1); + uspi->s_fshift = 10; + uspi->s_sbsize = super_block_size = 2048; + uspi->s_sbbase = 0; + flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD; + if (!(sb->s_flags & MS_RDONLY)) { + printk("nextstep type of ufs is supported read-only\n"); + goto failed; + } + break; - uspi->s_fsize = block_size; - uspi->s_fmask = ~(BLOCK_SIZE - 1); - uspi->s_fshift = BLOCK_SIZE_BITS; - uspi->s_sbsize = super_block_size; - i = 0; - uspi->s_sbbase = offsets[i]; -again: + default: + printk("this fs type of ufs is not supported\n"); + goto failed; + } + + if (!(sb->s_flags & MS_RDONLY)) + printk("!!! warning !!! write support of ufs is still in experimental state\n"); + +again: set_blocksize (sb->s_dev, block_size); /* * read ufs super block from device */ - ubh = ubh_bread2 (sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size); + ubh = ubh_bread_uspi (uspi, sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size); if (!ubh) goto failed; usb1 = ubh_get_usb_first(USPI_UBH); usb2 = ubh_get_usb_second(USPI_UBH); usb3 = ubh_get_usb_third(USPI_UBH); - + /* * 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 defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */ switch (usb3->fs_magic) { @@ -222,108 +566,68 @@ again: goto magic_found; } #endif - /* - * Magic number not found -- try another super block location - */ - if (++i < sizeof(offsets)/sizeof(unsigned)) { - ubh_brelse2(ubh); + + if ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == + UFS_MOUNT_UFSTYPE_NEXT && uspi->s_sbbase < 256) { + ubh_brelse_uspi(uspi); ubh = NULL; - uspi->s_sbbase = offsets[i]; + uspi->s_sbbase += 8; goto again; - } else { - printk("ufs_read_super: " - "super block location not in " - "{ 0, 96, 160} " - "or bad magic number\n"); - goto failed; } - magic_found: + printk("ufs_read_super: bad magic number\n"); + goto failed; +magic_found: /* * Check block and fragment sizes */ uspi->s_bsize = SWAB32(usb1->fs_bsize); uspi->s_fsize = SWAB32(usb1->fs_fsize); uspi->s_sbsize = SWAB32(usb1->fs_sbsize); + uspi->s_fmask = SWAB32(usb1->fs_fmask); + uspi->s_fshift = SWAB32(usb1->fs_fshift); 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); + printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize); goto failed; } - - /* - * 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_brelse_uspi(uspi); 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; + UFSD(("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size)) goto again; } #ifdef UFS_SUPER_DEBUG_MORE ufs_print_super_stuff (usb1, usb2, usb3, swab); #endif - /* - * 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(("Flavor: 44BSD\n")) - flags |= UFS_44BSD; - sb->s_flags |= MS_RDONLY; - } else { - UFSD(("Flavor: OLD\n")) - sb->s_flags |= UFS_OLD; /* 4.2BSD */ - } - } else if (uspi->s_sbbase > 0) { - UFSD(("Flavor: NEXT\n")) - flags |= UFS_NEXT; - sb->s_flags |= MS_RDONLY; - } else { - UFSD(("Flavor: SUN\n")) - flags |= UFS_SUN; - } /* - * Check whether file system was correctly unmounted. + * Check, if 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)) { + (((flags & UFS_ST_MASK) == UFS_ST_SUN) && + (ufs_get_fs_state(usb3) == (UFS_FSOK - SWAB32(usb1->fs_time))))) { switch(usb1->fs_clean) { - case UFS_FSACTIVE: /* 0x00 */ - printk("ufs_read_super: fs is active\n"); - sb->s_flags |= MS_RDONLY; - break; - case UFS_FSCLEAN: /* 0x01 */ - UFSD(("ufs_read_super: fs is clean\n")) + case UFS_FSCLEAN: + UFSD(("fs is clean\n")) break; case UFS_FSSTABLE: - UFSD(("ufs_read_super: fs is stable\n")) + UFSD(("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"); + case UFS_FSACTIVE: + printk("ufs_read_super: fs is active\n"); + sb->s_flags |= MS_RDONLY; break; - case UFS_FSBAD: /* 0xFF */ + case UFS_FSBAD: printk("ufs_read_super: fs is bad\n"); sb->s_flags |= MS_RDONLY; break; @@ -333,19 +637,19 @@ again: sb->s_flags |= MS_RDONLY; break; } - } else { + } + else { printk("ufs_read_super: fs needs fsck\n"); sb->s_flags |= MS_RDONLY; } - sb->s_flags &= ~MS_RDONLY; /* * Read ufs_super_block into internal data structures */ sb->s_blocksize = SWAB32(usb1->fs_fsize); sb->s_blocksize_bits = SWAB32(usb1->fs_fshift); sb->s_op = &ufs_super_ops; - sb->dq_op = 0; /* XXX */ + sb->dq_op = NULL; /***/ sb->s_magic = SWAB32(usb3->fs_magic); uspi->s_sblkno = SWAB32(usb1->fs_sblkno); @@ -385,12 +689,9 @@ 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]; - 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]; - uspi->s_qfmask = SWAB64(tmp); + uspi->s_contigsumsize = SWAB32(usb3->fs_u.fs_44.fs_contigsumsize); + uspi->s_qbmask = ufs_get_fs_qbmask(usb3); + uspi->s_qfmask = ufs_get_fs_qfmask(usb3); uspi->s_postblformat = SWAB32(usb3->fs_postblformat); uspi->s_nrpos = SWAB32(usb3->fs_nrpos); uspi->s_postbloff = SWAB32(usb3->fs_postbloff); @@ -410,86 +711,32 @@ again: 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 */ + uspi->s_bpf = uspi->s_fsize << 3; + uspi->s_bpfshift = uspi->s_fshift + 3; + uspi->s_bpfmask = uspi->s_bpf - 1; + 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); - /* - * Read cs structures from (usually) first data block - * on the device. - */ - size = uspi->s_cssize; - blks = howmany(size, uspi->s_fsize); - base = space = kmalloc(size, GFP_KERNEL); - if (!base) - goto failed; - for (i = 0; i < blks; i += uspi->s_fpb) { - size = uspi->s_bsize; - if (i + uspi->s_fpb > blks) - size = (blks - i) * uspi->s_fsize; - ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size); - if (!ubh) - goto failed; - ubh_ubhcpymem (space, ubh, size); - sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space; - space += size; - ubh_brelse (ubh); - ubh = NULL; - } /* - * 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! + * Read cylinder group structures */ - if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL))) - goto failed; - for (i = 0; i < uspi->s_ncg; i++) - sb->u.ufs_sb.s_ucg[i] = NULL; - for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { - sb->u.ufs_sb.s_ucpi[i] = NULL; - sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; - } - for (i = 0; i < uspi->s_ncg; i++) { - UFSD(("read cg %u\n", i)) - if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize))) - goto failed; - if (!ufs_cg_chkmagic ((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data)) - goto failed; -#ifdef UFS_SUPER_DEBUG_MORE - ufs_print_cylinder_stuff((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data, swab); -#endif - } - for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { - if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL))) + if (!(sb->s_flags & MS_RDONLY)) + if (!ufs_read_cylinder_structures(sb)) goto failed; - sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY; - } - sb->u.ufs_sb.s_cg_loaded = 0; unlock_super(sb); UFSD(("EXIT\n")) return(sb); failed: - if (ubh) ubh_brelse2 (ubh); + if (ubh) ubh_brelse_uspi (uspi); if (uspi) kfree (uspi); - if (base) kfree (base); - - if (sb->u.ufs_sb.s_ucg) { - for (i = 0; i < uspi->s_ncg; i++) - if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]); - kfree (sb->u.ufs_sb.s_ucg); - for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) - if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]); - } sb->s_dev = 0; unlock_super (sb); MOD_DEC_USE_COUNT; @@ -497,187 +744,132 @@ failed: return(NULL); } -/* - * Put super block, release internal structures - */ -void ufs_put_super (struct super_block * sb) -{ - struct ufs_sb_private_info * uspi; - struct ufs_buffer_head * ubh; - unsigned char * base, * space; - unsigned size, blks, i; - - UFSD(("ENTER\n")) - - uspi = sb->u.ufs_sb.s_uspi; - size = uspi->s_cssize; - blks = howmany(size, uspi->s_fsize); - base = space = (char*) sb->u.ufs_sb.s_csp[0]; - for (i = 0; i < blks; i += uspi->s_fpb) { - size = uspi->s_bsize; - if (i + uspi->s_fpb > blks) - size = (blks - i) * uspi->s_fsize; - ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size); - if (!ubh) - goto go_on; - ubh_memcpyubh (ubh, space, size); - space += size; - ubh_mark_buffer_uptodate (ubh, 1); - ubh_mark_buffer_dirty (ubh, 0); - ubh_brelse (ubh); - } - -go_on: - for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { - ufs_put_cylinder (sb, i); - kfree (sb->u.ufs_sb.s_ucpi[i]); - } - for (i = 0; i < uspi->s_ncg; i++) - brelse (sb->u.ufs_sb.s_ucg[i]); - kfree (sb->u.ufs_sb.s_ucg); - kfree (base); - ubh_brelse2 (USPI_UBH); - kfree (sb->u.ufs_sb.s_uspi); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return; -} - -/* - * Write super block to device - */ void ufs_write_super (struct super_block * sb) { struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; struct ufs_super_block_third * usb3; - unsigned swab; - + unsigned flags, swab; + UFSD(("ENTER\n")) swab = sb->u.ufs_sb.s_swab; + flags = sb->u.ufs_sb.s_flags; uspi = sb->u.ufs_sb.s_uspi; usb1 = ubh_get_usb_first(USPI_UBH); usb3 = ubh_get_usb_third(USPI_UBH); - + if (!(sb->s_flags & MS_RDONLY)) { - if (SWAB16(usb3->fs_u.fs_sun.fs_state) & UFS_FSOK) - usb3->fs_u.fs_sun.fs_state = SWAB16(SWAB16(usb3->fs_u.fs_sun.fs_state) & ~UFS_FSOK); usb1->fs_time = SWAB32(CURRENT_TIME); - usb3->fs_u.fs_sun.fs_state = SWAB32(UFS_FSOK - SWAB32(usb1->fs_time)); + if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN) + ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time)); ubh_mark_buffer_dirty (USPI_UBH, 1); } sb->s_dirt = 0; UFSD(("EXIT\n")) } -/* - * Copy some info about file system to user - */ -int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz) +void ufs_put_super (struct super_block * sb) { struct ufs_sb_private_info * uspi; - struct ufs_super_block_first * usb1; - struct statfs tmp; - struct statfs *sp = &tmp; - unsigned long used, avail; unsigned swab; - + UFSD(("ENTER\n")) - - swab = sb->u.ufs_sb.s_swab; - uspi = sb->u.ufs_sb.s_uspi; - usb1 = ubh_get_usb_first (USPI_UBH); - sp->f_type = UFS_MAGIC; - sp->f_bsize = sb->s_blocksize; - sp->f_blocks = uspi->s_dsize; - sp->f_bfree = (SWAB32(usb1->fs_cstotal.cs_nbfree) << uspi->s_fpbshift )+ - SWAB32(usb1->fs_cstotal.cs_nffree); + uspi = sb->u.ufs_sb.s_uspi; + swab = sb->u.ufs_sb.s_swab; - avail = sp->f_blocks - (sp->f_blocks / 100) * uspi->s_minfree; - used = sp->f_blocks - sp->f_bfree; - if (avail > used) - sp->f_bavail = avail - used; - else - sp->f_bavail = 0; - sp->f_files = uspi->s_ncg * uspi->s_ipg; - sp->f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree); - sp->f_fsid.val[0] = SWAB32(usb1->fs_id[0]); - sp->f_fsid.val[1] = SWAB32(usb1->fs_id[1]); - sp->f_namelen = UFS_MAXNAMLEN; + if (!(sb->s_flags & MS_RDONLY)) + ufs_put_cylinder_structures (sb); - UFSD(("EXIT\n")) - - return copy_to_user(buf, sp, bufsiz) ? -EFAULT : 0; + ubh_brelse_uspi (uspi); + kfree (sb->u.ufs_sb.s_uspi); + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return; } -static char error_buf[1024]; - -void ufs_warning (struct super_block * sb, const char * function, - const char * fmt, ...) -{ - va_list args; - - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); -} - -void ufs_error (struct super_block * sb, const char * function, - const char * fmt, ...) +int ufs_remount (struct super_block * sb, int * mount_flags, char * data) { struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; - va_list args; - + struct ufs_super_block_third * usb3; + unsigned new_mount_opt, ufstype; + unsigned flags, swab; + uspi = sb->u.ufs_sb.s_uspi; + flags = sb->u.ufs_sb.s_flags; + swab = sb->u.ufs_sb.s_swab; usb1 = ubh_get_usb_first(USPI_UBH); + usb3 = ubh_get_usb_third(USPI_UBH); - if (!(sb->s_flags & MS_RDONLY)) { - usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH, 1); - sb->s_dirt = 1; + /* + * Allow the "check" option to be passed as a remount option. + * It is not possible to change ufstype option during remount + */ + ufstype = sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE; + new_mount_opt = 0; + ufs_set_opt (new_mount_opt, ONERROR_LOCK); + if (!ufs_parse_options (data, &new_mount_opt)) + return -EINVAL; + if (!(new_mount_opt & UFS_MOUNT_UFSTYPE)) { + new_mount_opt |= ufstype; + } + else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) { + printk("ufstype can't be changed during remount\n"); + return -EINVAL; + } + sb->u.ufs_sb.s_mount_opt = new_mount_opt; + + if ((*mount_flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + return 0; + if (*mount_flags & MS_RDONLY) { + ufs_put_cylinder_structures(sb); + usb1->fs_time = SWAB32(CURRENT_TIME); + if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN) + ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time)); + ubh_mark_buffer_dirty (USPI_UBH, 1); + sb->s_dirt = 0; sb->s_flags |= MS_RDONLY; } - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); + else { + if (ufstype != UFS_MOUNT_UFSTYPE_SUN && + ufstype != UFS_MOUNT_UFSTYPE_44BSD) { + printk("this ufstype is read-only supported\n"); + return 0; + } + if (!ufs_read_cylinder_structures (sb)) { + printk("failed during remounting\n"); + return 0; + } + sb->s_flags &= ~MS_RDONLY; + } + return 0; } -void ufs_panic (struct super_block * sb, const char * function, - const char * fmt, ...) +int ufs_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; - va_list args; - + struct statfs tmp; + unsigned swab; + + swab = sb->u.ufs_sb.s_swab; uspi = sb->u.ufs_sb.s_uspi; - usb1 = ubh_get_usb_first(USPI_UBH); + usb1 = ubh_get_usb_first (USPI_UBH); - if (!(sb->s_flags & MS_RDONLY)) { - usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH, 1); - sb->s_dirt = 1; - } - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - /* this is to prevent panic from syncing this filesystem */ - if (sb->s_lock) - sb->s_lock = 0; - sb->s_flags |= MS_RDONLY; - printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); -/* panic ("UFS-fs panic (device %s): %s: %s\n", - kdevname(sb->s_dev), function, error_buf); -*/ + tmp.f_type = UFS_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = uspi->s_dsize; + tmp.f_bfree = ufs_blkstofrags(SWAB32(usb1->fs_cstotal.cs_nbfree)) + + SWAB32(usb1->fs_cstotal.cs_nffree); + tmp.f_bavail = (tmp.f_bfree > ((tmp.f_blocks / 100) * uspi->s_minfree)) + ? (tmp.f_bfree - ((tmp.f_blocks / 100) * uspi->s_minfree)) : 0; + tmp.f_files = uspi->s_ncg * uspi->s_ipg; + tmp.f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree); + tmp.f_namelen = UFS_MAXNAMLEN; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } - static struct super_operations ufs_super_ops = { ufs_read_inode, ufs_write_inode, @@ -687,20 +879,19 @@ static struct super_operations ufs_super_ops = { ufs_put_super, ufs_write_super, ufs_statfs, - NULL, /* XXX - ufs_remount() */ + ufs_remount }; static struct file_system_type ufs_fs_type = { - "ufs", + "ufs", FS_REQUIRES_DEV, ufs_read_super, NULL }; - __initfunc(int init_ufs_fs(void)) { - return(register_filesystem(&ufs_fs_type)); + return register_filesystem(&ufs_fs_type); } #ifdef MODULE @@ -715,5 +906,5 @@ void cleanup_module(void) { unregister_filesystem(&ufs_fs_type); } -#endif +#endif diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index f1004733a..bdc38e9fe 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -43,7 +43,7 @@ static struct dentry * ufs_follow_link(struct dentry * dentry, - struct dentry * base) + struct dentry * base, unsigned int follow) { struct inode * inode; struct buffer_head * bh; @@ -67,7 +67,7 @@ static struct dentry * ufs_follow_link(struct dentry * dentry, link = (char *) inode->u.ufs_i.i_u1.i_symlink; } UPDATE_ATIME(inode); - base = lookup_dentry(link, base, 1); + base = lookup_dentry(link, base, follow); if (bh) brelse(bh); UFSD(("EXIT\n")) diff --git a/fs/ufs/util.c b/fs/ufs/util.c index 527ba02aa..23f5052bb 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -49,7 +49,7 @@ failed: return NULL; } -struct ufs_buffer_head * _ubh_bread2_ (struct ufs_sb_private_info * uspi, +struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi, kdev_t dev, unsigned fragment, unsigned size) { unsigned i, j, count; @@ -82,14 +82,14 @@ void ubh_brelse (struct ufs_buffer_head * ubh) kfree (ubh); } -void ubh_brelse2 (struct ufs_buffer_head * ubh) +void ubh_brelse_uspi (struct ufs_sb_private_info * uspi) { unsigned i; - if (!ubh) + if (!USPI_UBH) return; - for ( i = 0; i < ubh->count; i++ ) { - brelse (ubh->bh[i]); - ubh->bh[i] = NULL; + for ( i = 0; i < USPI_UBH->count; i++ ) { + brelse (USPI_UBH->bh[i]); + USPI_UBH->bh[i] = NULL; } } @@ -138,8 +138,6 @@ unsigned ubh_max_bcount (struct ufs_buffer_head * ubh) for ( i = 0; i < ubh->count; i++ ) if ( ubh->bh[i]->b_count > max ) max = ubh->bh[i]->b_count; - if (max == 0) - printk("Je cosi shnileho v kralovstvi Danskem!\n"); return max; } diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 7443f9fc9..02e6fe98a 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -19,73 +19,166 @@ #define max(x,y) ((x)>(y)?(x):(y)) + /* - * current filesystem state; method depends on flags + * macros used for retyping */ -#define ufs_state(usb3) \ - (((flags & UFS_ST_MASK) == UFS_ST_OLD) \ - ? (usb3)->fs_u.fs_sun.fs_state /* old normal way */ \ - : (usb3)->fs_u.fs_44.fs_state /* 4.4BSD way */) +#define UCPI_UBH ((struct ufs_buffer_head *)ucpi) +#define USPI_UBH ((struct ufs_buffer_head *)uspi) + + /* - * namlen, its format depends of flags + * macros used for accesing structures */ -#define ufs_namlen(de) _ufs_namlen_(de,flags,swab) -static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsigned swab) { - if ((flags & UFS_DE_MASK) == UFS_DE_OLD) { - return SWAB16(de->d_u.d_namlen); - } else /* UFS_DE_44BSD */ { - return de->d_u.d_44.d_namlen; +#define ufs_get_fs_state(usb3) _ufs_get_fs_state_(usb3,flags,swab) +static inline __s32 _ufs_get_fs_state_(struct ufs_super_block_third * usb3, + unsigned flags, unsigned swab) +{ + if ((flags & UFS_ST_MASK) == UFS_ST_SUN) + return SWAB32((usb3)->fs_u.fs_sun.fs_state); + else + return SWAB32((usb3)->fs_u.fs_44.fs_state); +} + +#define ufs_set_fs_state(usb3,value) _ufs_set_fs_state_(usb3,value,flags,swab) +static inline void _ufs_set_fs_state_(struct ufs_super_block_third * usb3, + __s32 value, unsigned flags, unsigned swab) +{ + if ((flags & UFS_ST_MASK) == UFS_ST_SUN) + (usb3)->fs_u.fs_sun.fs_state = SWAB32(value); + else + (usb3)->fs_u.fs_44.fs_state = SWAB32(value); +} + +#define ufs_get_fs_qbmask(usb3) _ufs_get_fs_qbmask_(usb3,flags,swab) +static inline __u64 _ufs_get_fs_qbmask_(struct ufs_super_block_third * usb3, + unsigned flags, unsigned swab) +{ + __u64 tmp; + if ((flags & UFS_ST_MASK) == UFS_ST_SUN) { + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1]; + } + else { + ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qbmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qbmask[1]; } + return SWAB64(tmp); } -/* - * 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 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) { +#define ufs_get_fs_qfmask(usb3) _ufs_get_fs_qfmask_(usb3,flags,swab) +static inline __u64 _ufs_get_fs_qfmask_(struct ufs_super_block_third * usb3, + unsigned flags, unsigned swab) +{ + __u64 tmp; + if ((flags & UFS_ST_MASK) == UFS_ST_SUN) { + ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1]; + } + else { + ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qfmask[0]; + ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qfmask[1]; + } + return SWAB64(tmp); +} + +#define ufs_get_de_namlen(de) \ + (((flags & UFS_DE_MASK) == UFS_DE_OLD) \ + ? SWAB16(de->d_u.d_namlen) \ + : de->d_u.d_44.d_namlen) + +#define ufs_set_de_namlen(de,value) \ + (((flags & UFS_DE_MASK) == UFS_DE_OLD) \ + ? (de->d_u.d_namlen = SWAB16(value)) \ + : (de->d_u.d_44.d_namlen = value)) + +#define ufs_set_de_type(de,mode) _ufs_set_de_type_(de,mode,flags,swab) +static inline void _ufs_set_de_type_(struct ufs_dir_entry * de, int mode, + unsigned flags, unsigned swab) +{ + if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) { + switch (mode & S_IFMT) { + case S_IFSOCK: de->d_u.d_44.d_type = DT_SOCK; break; + case S_IFLNK: de->d_u.d_44.d_type = DT_LNK; break; + case S_IFREG: de->d_u.d_44.d_type = DT_REG; break; + case S_IFBLK: de->d_u.d_44.d_type = DT_BLK; break; + case S_IFDIR: de->d_u.d_44.d_type = DT_DIR; break; + case S_IFCHR: de->d_u.d_44.d_type = DT_CHR; break; + case S_IFIFO: de->d_u.d_44.d_type = DT_FIFO; break; + default: de->d_u.d_44.d_type = DT_UNKNOWN; + } + } +} + +#define ufs_get_inode_uid(inode) _ufs_get_inode_uid_(inode,flags,swab) +static inline __u32 _ufs_get_inode_uid_(struct ufs_inode * inode, + unsigned flags, unsigned swab) +{ switch (flags & UFS_UID_MASK) { case UFS_UID_EFT: return SWAB32(inode->ui_u3.ui_sun.ui_uid); case UFS_UID_44BSD: return SWAB32(inode->ui_u3.ui_44.ui_uid); - case UFS_UID_OLD: default: return SWAB16(inode->ui_u1.oldids.ui_suid); } } -#define ufs_gid(inode) _ufs_gid_(inode,flags,swab) -static inline __u32 _ufs_gid_(struct ufs_inode * inode, unsigned flags, unsigned swab) { +#define ufs_set_inode_uid(inode,value) _ufs_set_inode_uid_(inode,value,flags,swab) +static inline void _ufs_set_inode_uid_(struct ufs_inode * inode, __u32 value, + unsigned flags, unsigned swab) +{ + inode->ui_u1.oldids.ui_suid = SWAB16(value); + switch (flags & UFS_UID_MASK) { + case UFS_UID_EFT: + inode->ui_u3.ui_sun.ui_uid = SWAB32(value); + break; + case UFS_UID_44BSD: + inode->ui_u3.ui_44.ui_uid = SWAB32(value); + break; + } +} + +#define ufs_get_inode_gid(inode) _ufs_get_inode_gid_(inode,flags,swab) +static inline __u32 _ufs_get_inode_gid_(struct ufs_inode * inode, + unsigned flags, unsigned swab) +{ switch (flags & UFS_UID_MASK) { case UFS_UID_EFT: return SWAB32(inode->ui_u3.ui_sun.ui_gid); case UFS_UID_44BSD: return SWAB32(inode->ui_u3.ui_44.ui_gid); - case UFS_UID_OLD: default: return SWAB16(inode->ui_u1.oldids.ui_sgid); } } -/* - * macros used to avoid needless retyping - */ -#define UCPI_UBH ((struct ufs_buffer_head *)ucpi) -#define USPI_UBH ((struct ufs_buffer_head *)uspi) +#define ufs_set_inode_gid(inode,value) _ufs_set_inode_gid_(inode,value,flags,swab) +static inline void _ufs_set_inode_gid_(struct ufs_inode * inode, __u32 value, + unsigned flags, unsigned swab) +{ + inode->ui_u1.oldids.ui_sgid = SWAB16(value); + switch (flags & UFS_UID_MASK) { + case UFS_UID_EFT: + inode->ui_u3.ui_sun.ui_gid = SWAB32(value); + break; + case UFS_UID_44BSD: + inode->ui_u3.ui_44.ui_gid = SWAB32(value); + break; + } +} + + /* * 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); -#define ubh_bread2(dev,fragment,size) _ubh_bread2_(uspi,dev,fragment,size) -extern struct ufs_buffer_head * _ubh_bread2_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); +extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); extern void ubh_brelse (struct ufs_buffer_head *); -extern void ubh_brelse2 (struct ufs_buffer_head *); +extern void ubh_brelse_uspi (struct ufs_sb_private_info *); extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *, int); extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int); extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **); @@ -98,6 +191,8 @@ extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struc #define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size) extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned); + + /* * macros to get important structures from ufs_buffer_head */ @@ -115,25 +210,29 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head #define ubh_get_ucg(ubh) \ ((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data)) + /* * Extract byte from ufs_buffer_head * Extract the bits for a block from a map inside ufs_buffer_head */ #define ubh_get_addr8(ubh,begin) \ - ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + ((begin) & ~uspi->s_fmask)) + ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + \ + ((begin) & ~uspi->s_fmask)) #define ubh_get_addr16(ubh,begin) \ - (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + ((begin) & (uspi->fsize>>1) - 1))) + (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + \ + ((begin) & (uspi->fsize>>1) - 1))) #define ubh_get_addr32(ubh,begin) \ - (((u32*)((ubh)->bh[(begin) >> (BLOCK_SIZE_BITS-2)]->b_data)) + \ - ((begin) & ((BLOCK_SIZE>>2) - 1))) + (((u32*)((ubh)->bh[(begin) >> (uspi->s_fshift-2)]->b_data)) + \ + ((begin) & ((uspi->s_fsize>>2) - 1))) #define ubh_get_addr ubh_get_addr8 #define ubh_blkmap(ubh,begin,bit) \ ((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb))) + /* * Macros for access to superblock array structures */ @@ -188,45 +287,90 @@ 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) { - unsigned base, rest; + unsigned base, count, pos; + size -= offset; begin <<= 3; - size += begin; offset += begin; - base = offset >> (uspi->s_fshift + 3); - offset &= ((uspi->s_fsize << 3) - 1); + base = offset >> uspi->s_bpfshift; + offset &= uspi->s_bpfmask; for (;;) { - rest = min (size, uspi->s_fsize << 3); - size -= rest; - offset = ext2_find_next_zero_bit (ubh->bh[base]->b_data, rest, offset); - if (offset < rest || !size) + count = min (size + offset, uspi->s_bpf); + size -= count - offset; + pos = ext2_find_next_zero_bit (ubh->bh[base]->b_data, count, offset); + if (pos < count || !size) break; base++; offset = 0; } - return (base << (uspi->s_fshift + 3)) + offset - begin; + return (base << uspi->s_bpfshift) + pos - begin; +} + +static inline unsigned find_last_zero_bit (unsigned char * bitmap, + unsigned size, unsigned offset) +{ + unsigned bit, i; + unsigned char * mapp; + unsigned char map; + + mapp = bitmap + (size >> 3); + map = *mapp--; + bit = 1 << (size & 7); + for (i = size; i > offset; i--) { + if ((map & bit) == 0) + break; + if ((i & 7) != 0) { + bit >>= 1; + } else { + map = *mapp--; + bit = 1 << 7; + } + } + return i; +} + +#define ubh_find_last_zero_bit(ubh,begin,size,offset) _ubh_find_last_zero_bit_(uspi,ubh,begin,size,offset) +static inline unsigned _ubh_find_last_zero_bit_( + struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, + unsigned begin, unsigned start, unsigned end) +{ + unsigned base, count, pos, size; + + size = start - end; + begin <<= 3; + start += begin; + base = start >> uspi->s_bpfshift; + start &= uspi->s_bpfmask; + for (;;) { + count = min (size + (uspi->s_bpf - start), uspi->s_bpf) + - (uspi->s_bpf - start); + size -= count; + pos = find_last_zero_bit (ubh->bh[base]->b_data, + start, start - count); + if (pos > start - count || !size) + break; + base--; + start = uspi->s_bpf; + } + return (base << uspi->s_bpfshift) + pos - 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: @@ -243,8 +387,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; @@ -263,8 +407,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; @@ -301,8 +445,7 @@ 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/README-WIP.txt b/fs/umsdos/README-WIP.txt index f63a1144f..4535ec6ab 100644 --- a/fs/umsdos/README-WIP.txt +++ b/fs/umsdos/README-WIP.txt @@ -1,21 +1,29 @@ Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing (started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>) +(Final conversion to dentries Bill Hawes <whawes@star.net>) ----------- 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 ----------- +There is no warning any more. +Both read-only and read-write stuff is fixed, both in +msdos-compatibile mode, and in umsdos EMD mode, and it seems stable. +There are still few hardlink nuisances, but those are not fatal. -Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7: +I'd call it pre-release, and ask for as many people as possible to +come and test it! See notes below for some more information, or if +you are trying to use UMSDOS as root partition. + +Legend: those lines marked with '+' on the beggining of line indicates it +passed all of my tests, and performed perfect in all of them. + +Current status (981129) - UMSDOS dentry-pre 0.84: (1) pure MSDOS (no --linux-.--- EMD file): +READ: + readdir - works + lookup - works + read file - works +WRITE: + creat file - works + delete file - works + write file - works @@ -24,145 +32,95 @@ Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7: + 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) ++ rmdir - works (2) umsdos (with --linux-.--- EMD file): +READ: + 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!) ++ switching MSDOS/UMSDOS - works ++ switching UMSDOS/MSDOS - works +- pseudo root things - works mostly. See notes below. + 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 ++ hard links - works ++ special files (block/char devices, FIFOs, sockets...) - works ++ various umsdos ioctls - works + + +WRITE: ++ create symlink - works +- create hardlink - works, but see portability WARNING below ++ create file - works ++ create special file - works ++ write to file - works ++ rename file (same dir) - works ++ rename file (dif. dir) - works +- rename hardlink (same dir) - +- rename hardlink (dif. dir) - ++ rename symlink (same dir) - works ++ rename symlink (dif. dir) - works ++ rename dir (same dir) - works ++ rename dir (dif. dir) - works ++ delete file - works ++ notify_change (chown,perms) - works ++ delete hardlink - works ++ mkdir - works ++ rmdir - works ++ umssyncing (many ioctls) - works -- create symlink - seems to work both on short & long names now ! -- create hardlink - WARNING: NOT FIXED YET! -- create file - seems to work both on short & long names now ! -- create special file - seems to work both on short & long names now ! -- write to file - seems to work both on short & long names now ! -- rename file (same dir) - seems to work, but with i_count PROBLEMS -- rename file (dif. dir) - seems to work, but with i_count PROBLEMS -- rename dir (same dir) - seems to work, but with i_count PROBLEMS -- rename dir (dif. dir) - seems to work, but with i_count PROBLEMS -- delete file - seems to work fully now! -- notify_change (chown,perms) - seems to work! -- delete hardlink - WARNING: NOT FIXED YET! -- mkdir - seems to work both on short & long names now ! -- 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 + +- 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). +Some current notes: -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. +Note: creating and using pseudo-hardlinks is always non-perfect, especially +in filesystems that might be externally modified like umsdos. There is +example is specs file about it. Specifically, moving directory which +contains hardlinks will break them. -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. +Note: (about pseudoroot) If you are currently trying to use UMSDOS as root +partition (with linux installed in c:\linux) it will boot, but there are +some problems. Volunteers ready to test pseudoroot are needed (preferably +ones with working backups or unimportant data). There are problems with +different interpretation of hard links in normal in pseudo-root modes, +resulting is 'silent delete' of them sometimes. Also, '/DOS' pseudo +directory is only partially re-implemented and buggy. It works most of the +time, though. Update: should work ok in 0.84, although it still does not +work correctly in combination with initrd featere. Working on this! -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. +Warning: (about creating hardlinks in pseudoroot mode) - hardlinks created in +pseudoroot mode are not compatibile with 'normal' hardlinks, and vice versa. +That is because harlink which is /foo in pseudoroot mode, becomes +/linux/foo in normal mode. I'm thinking about this one. However, since most +people either always use pseudoroot, or always use normal umsdos filesystem, +this is no showstopper. -Note 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. +Warning: (about hardlinks) - modifying hardlinks (esp. if there are in +different directories) are currently somewhat broken, I'm working on it. ------------------------------------------------------------------------------ Some general notes: -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 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 -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 occurs every few cycles. - -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? - -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. - - ------------------------------------------------------------------------------- -some of my notes for myself /mn/: - -+ hardlinks/symlinks. test with files in not_the_same_dir -- also test not_the_same_dir for other file operations like rename etc. -- iput: device 00:00 inode 318 still has aliases! problem. Check in iput() - for device 0,0. Probably null pointer passed around when it shouldn't be ? -- dput/iput problem... -- 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 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). - -- SECURITY WARNING: short dentries should be invalidated, or they could be - accessed instead of proper long names. - -- I've put many check_dentry_path(), check_inode() calls to trace down - problems. those should be removed in final version. - -- 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. +Good idea when running development kernels is to have SysRq support compiled +in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like +not being able to umount(2) umsdosfs, and because of it root partition also, +or panics which force you to reboot etc.) -+ as for iput(): (my only pointer so far. anyone else?) +I'm unfortunately somewhat out of time to read linux-kernel@vger, 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-umsdos@voyager.hr; however mnalis@jagor.srce.hr is preferable. ->development I only know about iput. All functions that get an inode as ->argument and don't return it have to call iput on it before exit, i.e. when ->it is no longer needed and the code returns to vfs layer. The rest is quite ->new to me, but it might be similar for dput. Typical side effect of a ->missing iput was a memory runout (but no crash). You also couldn't unmount ->the filesystem later though no process was using it. On the other hand, one ->iput too much lead to serious pointer corruption and crashed the system ->very soon. I used to look at the FAT filesystem and copy those pieces of -> -> Frank diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index a97f1cb91..41740fd42 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -13,6 +13,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <linux/umsdos_fs.h> #include <asm/system.h> @@ -65,49 +66,52 @@ void check_page_tables (void) 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))); + printk (" (has %c_sb=%d, %d)", + c, MAJOR (sb->s_dev), MINOR (sb->s_dev)); } else { - Printk ((" (%c_sb is NULL)", c)); + printk (" (%c_sb is NULL)", c); } } /* * check an inode */ +extern struct inode_operations umsdos_rdir_inode_operations; void check_inode (struct inode *inode) { if (inode) { - Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)", - inode->i_ino, inode->i_count)); + 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)")); + printk (" (has i_dentry)"); } else { - Printk ((" (NO i_dentry)")); + printk (" (NO i_dentry)"); } + printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched); + if (inode->i_op == NULL) { - Printk ((" (i_op is NULL)\n")); + printk (" (i_op is NULL)\n"); } else if (inode->i_op == &umsdos_dir_inode_operations) { - Printk ((" (i_op is umsdos_dir_inode_operations)\n")); + 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")); + 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")); + 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")); + 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")); + 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")); + printk (" (i_op is umsdos_symlink_inode_operations)\n"); } else { - Printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op)); + printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op)); } } else { - Printk ((KERN_DEBUG "* inode is NULL\n")); + printk (KERN_DEBUG "* inode is NULL\n"); } } @@ -125,40 +129,40 @@ void checkd_inode (struct inode *inode) return; } - Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino)); + 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")); + 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")); + 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")); + 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 (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")); + printk (KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"); return; #endif } - Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n")); + printk (KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"); return; } @@ -170,19 +174,19 @@ void checkd_inode (struct inode *inode) 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)); + 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)); + 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)); + 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")); + printk (" (d_op is NULL)\n"); } else { - Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op)); + printk (" (d_op is UNKNOWN: %p)\n", dentry->d_op); } } @@ -194,35 +198,35 @@ void check_dent_int (struct dentry *dentry, int parent) void check_dentry_path (struct dentry *dentry, const char *desc) { int count=0; - Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc)); + printk (KERN_DEBUG "*** check_dentry_path: %.60s\n", desc); if (!dentry) { - Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n")); + 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))); + 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")); + 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")); + printk (KERN_ERR + "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"); } if (!dentry) { - Printk ((KERN_ERR - "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n")); + printk (KERN_ERR + "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"); } } #else diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 5a7bcd854..6ab276ac4 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -20,46 +20,40 @@ #include <asm/uaccess.h> #define UMSDOS_SPECIAL_DIRFPOS 3 +extern struct dentry *saved_root; extern struct inode *pseudo_root; - +/* #define UMSDOS_DEBUG_VERBOSE 1 */ /* - * This needs to have the parent dentry passed to it. - * N.B. Try to get rid of this soon! + * Dentry operations routines */ -int compat_msdos_create (struct inode *dir, const char *name, int len, - int mode, struct inode **inode) + +/* nothing for now ... */ +static int umsdos_dentry_validate(struct dentry *dentry) { - int ret; - struct dentry *dentry, *d_dir; + return 1; +} - check_inode (dir); - 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; +/* for now, drop everything to force lookups ... */ +static void umsdos_dentry_dput(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + if (inode) { + d_drop(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); -out: - return ret; } +struct dentry_operations umsdos_dentry_operations = +{ + umsdos_dentry_validate, /* d_validate(struct dentry *) */ + NULL, /* d_hash */ + NULL, /* d_compare */ + umsdos_dentry_dput, /* d_delete(struct dentry *) */ + NULL, + NULL, +}; + /* * So grep * doesn't complain in the presence of directories. @@ -109,9 +103,9 @@ static int umsdos_dir_once ( void *buf, * Return a negative value from linux/errno.h. * 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. + * This function is used by the normal readdir VFS entry point, + * and in order to get the directory entry from a file's dentry. + * See umsdos_dentry_to_entry() below. */ static int umsdos_readdir_x (struct inode *dir, struct file *filp, @@ -129,7 +123,6 @@ static int umsdos_readdir_x (struct inode *dir, struct file *filp, 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 @@ -140,6 +133,8 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n")); * linux root), it simulate a directory /DOS which points to * the real root of the file system. */ + + Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n")); if (filldir (dirbuf, "DOS", 3, UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) { filp->f_pos++; @@ -174,9 +169,13 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n")); ret = PTR_ERR(demd); if (IS_ERR(demd)) goto out_end; - ret = 0; - if (!demd->d_inode) + ret = -EIO; + if (!demd->d_inode) { + printk(KERN_WARNING + "umsdos_readir_x: EMD file %s/%s not found\n", + demd->d_parent->d_name.name, demd->d_name.name); goto out_dput; + } /* set up our private filp ... */ fill_new_filp(&new_filp, demd); @@ -189,35 +188,63 @@ 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 inode *inode; struct umsdos_dirent entry; + struct umsdos_info info; ret = -EIO; if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0) break; - if (entry.name_len == 0) - goto remove_name; + continue; +#ifdef UMSDOS_DEBUG_VERBOSE +if (entry.flags & UMSDOS_HLINK) +printk("umsdos_readdir_x: %s/%s is hardlink\n", +filp->f_dentry->d_name.name, entry.name); +#endif umsdos_parse (entry.name, entry.name_len, &info); info.f_pos = cur_f_pos; umsdos_manglename (&info); + /* + * Do a real lookup on the short name. + */ dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname, - info.fake.len); + info.fake.len, 1); 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) { + /* + * If the file wasn't found, remove it from the EMD. + */ + inode = dret->d_inode; + if (!inode) + goto remove_name; +#ifdef UMSDOS_DEBUG_VERBOSE +if (inode->u.umsdos_i.i_is_hlink) +printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n", +dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino); +#endif + +Printk (("Found %s/%s, ino=%ld, flags=%x\n", +dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino, +entry.flags)); + /* check whether to resolve a hard-link */ + if ((entry.flags & UMSDOS_HLINK) && follow_hlink && + !inode->u.umsdos_i.i_is_hlink) { dret = umsdos_solve_hlink (dret); ret = PTR_ERR(dret); if (IS_ERR(dret)) break; + inode = dret->d_inode; + if (!inode) { +printk("umsdos_readdir_x: %s/%s negative after link\n", +dret->d_parent->d_name.name, dret->d_name.name); + goto clean_up; + } } - + /* #Specification: pseudo root / reading real root * The pseudo root (/linux) is logically * erased from the real root. This means that @@ -225,20 +252,21 @@ dret->d_parent->d_name.name, info.fake.fname, entry.flags)); * infinite recursion (/DOS/linux/DOS/linux/...) while * walking the file system. */ - if (dret->d_inode != pseudo_root && + 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, dret->d_inode->i_ino) < 0) { + cur_f_pos, inode->i_ino) < 0) { new_filp.f_pos = cur_f_pos; } -Printk (("Found %s/%s(%ld)\n", -dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino)); +Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n", +dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino)); if (u_entry != NULL) *u_entry = entry; dput(dret); + ret = 0; break; } + clean_up: dput(dret); continue; @@ -248,11 +276,17 @@ dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino)); * 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); +#ifdef UMSDOS_PARANOIA +printk("umsdos_readdir_x: %s/%s out of sync, erasing\n", +filp->f_dentry->d_name.name, info.entry.name); +#endif + ret = umsdos_delentry(filp->f_dentry, &info, + S_ISDIR(info.entry.mode)); if (ret) - break; - continue; + printk(KERN_WARNING + "umsdos_readdir_x: delentry %s, err=%d\n", + info.entry.name, ret); + goto clean_up; } /* * If the fillbuf has failed, f_pos is back to 0. @@ -296,8 +330,6 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir) 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); if (bufk.count == 0) @@ -340,19 +372,17 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir) * 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_new(struct dentry *dentry, struct umsdos_info *info) { - 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; + struct inode *inode = dentry->d_inode; + struct umsdos_dirent *entry = &info->entry; + /* + * This part of the initialization depends only on i_patched. + */ + if (inode->u.umsdos_i.i_patched) + goto out; + inode->u.umsdos_i.i_patched = 1; if (S_ISREG (entry->mode)) entry->mtime = inode->i_mtime; inode->i_mode = entry->mode; @@ -380,218 +410,18 @@ void umsdos_lookup_patch (struct inode *dir, struct inode *inode, "UMSDOS: lookup_patch entry->nlink < 1 ???\n"); } } - umsdos_patch_inode (inode, dir, emd_pos); + /* + * The mode may have changed, so patch the inode again. + */ + umsdos_patch_dentry_inode(dentry, info->f_pos); + umsdos_set_dirinfo_new(dentry, info->f_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 */ - ino_t ino; -}; - - -/* - * Just to record the offset of one entry. - */ - -static int umsdos_filldir_k ( void *buf, - const char *name, - int len, - off_t offset, - ino_t ino) -{ - struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf; - - d->f_pos = offset; - d->ino = ino; - return 0; -} - -struct UMSDOS_DIR_SEARCH { - struct umsdos_dirent *entry; - int found; - ino_t search_ino; -}; - -static int umsdos_dir_search ( void *buf, - const char *name, - int len, - off_t offset, - ino_t ino) -{ - int ret = 0; - struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf; - - if (d->search_ino == ino) { - d->found = 1; - memcpy (d->entry->name, name, len); - d->entry->name[len] = '\0'; - d->entry->name_len = len; - ret = 1; /* So fat_readdir will terminate */ - } - return ret; -} - - - -/* - * 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 - * in the file system if not. - */ - -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) { - /* - * 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; - } - - /* 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; -} - -/* - * Deprecated. Try to get rid of this soon! - */ -int umsdos_inode2entry (struct inode *dir, struct inode *inode, - struct umsdos_dirent *entry) -{ - int ret = -ENOENT; - struct inode *emddir; - struct dentry *i2e; - struct file filp; - struct UMSDOS_DIR_SEARCH bufsrch; - struct UMSDOS_DIRENT_K bufk; - - 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; - } - - 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; - } - - /* 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 (bufk.ino == inode->i_ino) { - ret = 0; - umsdos_lookup_patch (dir, inode, entry, bufk.f_pos); - break; - } - } - iput (emddir); -out: - return ret; -} - - -/* * Return != 0 if an entry is the pseudo DOS entry in the pseudo root. */ @@ -631,71 +461,79 @@ int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry) 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; - umsdos_startlookup (dir); - /* this shouldn't happen ... */ - if (len == 1 && name[0] == '.') { - 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; - } +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_lookup_x: looking for %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + umsdos_startlookup (dir); 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. */ - inode = iget(dir->i_sb, UMSDOS_ROOT_INO); - if (inode) - goto out_add; - ret = -ENOMEM; - goto out; + Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n")); + inode = saved_root->d_inode; + goto out_add; } ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - if (ret) + if (ret) { +printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); goto out; + } + ret = umsdos_findentry (dentry->d_parent, &info, 0); + if (ret) { +if (ret != -ENOENT) +printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); + goto out; + } 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; - + /* do a real lookup to get the short name ... */ dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len); + info.fake.len, 1); ret = PTR_ERR(dret); - if (IS_ERR(dret)) + if (IS_ERR(dret)) { +printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); goto out; - if (!dret->d_inode) + } + inode = dret->d_inode; + if (!inode) goto out_remove; - - umsdos_lookup_patch_new(dret, &info.entry, info.f_pos); + umsdos_lookup_patch_new(dret, &info); +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", +dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino); +#endif /* 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)); + if ((info.entry.flags & UMSDOS_HLINK) && + !inode->u.umsdos_i.i_is_hlink) { dret = umsdos_solve_hlink (dret); ret = PTR_ERR(dret); if (IS_ERR(dret)) goto out; + ret = -ENOENT; + inode = dret->d_inode; + if (!inode) { +printk("umsdos_lookup_x: %s/%s negative after link\n", +dret->d_parent->d_name.name, dret->d_name.name); + goto out_dput; + } } - /* N.B. can dentry be negative after resolving hlinks? */ - if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) { + if (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. @@ -705,23 +543,23 @@ dret->d_inode->i_ino, info.entry.flags)); * which are recorded independently of the pseudo-root * mode. */ - Printk (("umsdos_lookup_x: untested Pseudo_root\n")); +printk("umsdos_lookup_x: skipping DOS/linux\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. + * We've found it OK. Now hash the dentry with the inode. */ out_add: inode->i_count++; d_add (dentry, inode); + dentry->d_op = &umsdos_dentry_operations; ret = 0; out_dput: + if (dret && dret != dentry) + d_drop(dret); dput(dret); out: umsdos_endlookup (dir); @@ -729,10 +567,10 @@ out: out_remove: printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n", - dentry->d_name.name, info.fake.fname); + dentry->d_parent->d_name.name, dentry->d_name.name); umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode)); ret = -ENOENT; - goto out; + goto out_dput; } @@ -740,9 +578,7 @@ out_remove: * 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. - * + * Called by VFS; should fill dentry->d_inode via d_add. */ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry) @@ -756,6 +592,7 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry) Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative\n")); d_add (dentry, NULL); + dentry->d_op = &umsdos_dentry_operations; ret = 0; } return ret; @@ -768,7 +605,8 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry) * 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 *umsdos_lookup_dentry(struct dentry *parent, char *name, int len, + int real) { struct dentry *result, *dentry; int error; @@ -783,7 +621,9 @@ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len) dentry = d_alloc(parent, &qstr); if (dentry) { result = dentry; - error = umsdos_real_lookup(parent->d_inode, result); + error = real ? + UMSDOS_rlookup(parent->d_inode, result) : + UMSDOS_lookup(parent->d_inode, result); if (error) goto out_fail; } @@ -797,111 +637,134 @@ out_fail: goto out; } +/* + * Return a path relative to our root. + */ +char * umsdos_d_path(struct dentry *dentry, char * buffer, int len) +{ + struct dentry * old_root = current->fs->root; + char * path; + + /* N.B. not safe -- fix this soon! */ + current->fs->root = dentry->d_sb->s_root; + path = d_path(dentry, buffer, len); + current->fs->root = old_root; + return path; +} + /* - * gets dentry which points to pseudo-hardlink + * Return the dentry which points to a 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. + * if file is found, return new dentry/inode + * The resolved inode will have i_is_hlink set. * + * Note: the original dentry is always dput(), even if an error occurs. */ struct dentry *umsdos_solve_hlink (struct dentry *hlink) { /* root is our root for resolving pseudo-hardlink */ struct dentry *base = hlink->d_sb->s_root; - struct dentry *final, *dir, *dentry_dst; + struct dentry *dentry_dst; char *path, *pt; - unsigned long len; - int ret = -EIO; + int len; struct file filp; - check_dentry_path (hlink, "HLINK BEGIN hlink"); +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_solve_hlink: following %s/%s\n", +hlink->d_parent->d_name.name, hlink->d_name.name); +#endif - final = ERR_PTR (-ENOMEM); + dentry_dst = ERR_PTR (-ENOMEM); path = (char *) kmalloc (PATH_MAX, GFP_KERNEL); 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; +#ifdef UMSDOS_DEBUG_VERBOSE +printk ("umsdos_solve_hlink: %s/%s is path %s\n", +hlink->d_parent->d_name.name, hlink->d_name.name, path); +#endif /* start at root dentry */ - dir = dget(base); - path[hlink->d_inode->i_size] = '\0'; - pt = path; + dentry_dst = dget(base); + path[len] = '\0'; + pt = path + 1; /* skip leading '/' */ while (1) { + struct dentry *dir = dentry_dst, *demd; char *start = pt; - int len; + int real; while (*pt != '\0' && *pt != '/') pt++; len = (int) (pt - start); if (*pt == '/') *pt++ = '\0'; - dentry_dst = umsdos_lookup_dentry(dir, start, len); + real = (dir->d_inode->u.umsdos_i.i_emd_dir == 0); + /* + * Hack alert! inode->u.umsdos_i.i_emd_dir isn't reliable, + * so just check whether there's an EMD file ... + */ + real = 1; + demd = umsdos_get_emd_dentry(dir); + if (!IS_ERR(demd)) { + if (demd->d_inode) + real = 0; + dput(demd); + } + +#ifdef UMSDOS_DEBUG_VERBOSE +printk ("umsdos_solve_hlink: dir %s/%s, name=%s, emd_dir=%ld, real=%d\n", +dir->d_parent->d_name.name, dir->d_name.name, start, +dir->d_inode->u.umsdos_i.i_emd_dir, real); +#endif + dentry_dst = umsdos_lookup_dentry(dir, start, len, real); + if (real) + d_drop(dir); + dput (dir); 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 { - 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! */ + /* not found? stop search ... */ + if (!dentry_dst->d_inode) { break; } - if (*pt == '\0') { /* we're finished! */ - final = umsdos_lookup_dentry(hlink->d_parent, - (char *) hlink->d_name.name, - hlink->d_name.len); + if (*pt == '\0') /* we're finished! */ 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++; + + if (!IS_ERR(dentry_dst)) { + struct inode *inode = dentry_dst->d_inode; + if (inode) { + inode->u.umsdos_i.i_is_hlink = 1; +#ifdef UMSDOS_DEBUG_VERBOSE +printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n", +dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino); +#endif } else { - printk ("umsdos_solve_hlink: %s/%s already exists\n", - final->d_parent->d_name.name, - final->d_name.name); +#ifdef UMSDOS_DEBUG_VERBOSE +printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n", +dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name); +#endif } -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); - } - dput(dir); + } else + printk(KERN_WARNING + "umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst)); 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; + return dentry_dst; out_noread: - printk("umsdos_solve_hlink: failed reading pseudolink!\n"); + printk(KERN_WARNING "umsdos_solve_hlink: failed reading pseudolink!\n"); goto out_free; } @@ -943,5 +806,4 @@ struct inode_operations umsdos_dir_inode_operations = NULL, /* smap */ NULL, /* updatepage */ NULL, /* revalidate */ - }; diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index 2f56b0404..6a4e99c04 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -30,46 +30,12 @@ ssize_t umsdos_file_read_kmem ( struct file *filp, char *buf, size_t count) { - int ret; - + ssize_t ret; mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); - - 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 " f_pos=%Lu\n", filp->f_pos)); - PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); - PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary)); - PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags)); - PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid)); - PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version)); - PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); - MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; - 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) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); - 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)); - PRINTK ((KERN_DEBUG " (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags)); - PRINTK ((KERN_DEBUG " (ret) f_owner=%d\n", filp->f_owner.uid)); - PRINTK ((KERN_DEBUG " (ret) f_version=%ld\n", filp->f_version)); - PRINTK ((KERN_DEBUG " (ret) 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)); - -#if 0 - { - 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)); - } -#endif - set_fs (old_fs); return ret; } @@ -85,34 +51,30 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp, const char *buf, 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\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 " f_pos=%Lu\n", filp->f_pos)); - PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); - PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary)); - PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags)); - PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid)); - PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version)); - PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); + ssize_t ret; /* note: i_binary=2 is for CVF-FAT. We put it here, instead of - * umsdos_file_write_kmem, since it is also wise not to compress symlinks - * (in the unlikely event that they are > 512 bytes and can be compressed - * FIXME: should we set it when reading symlinks too? */ + * umsdos_file_write_kmem, since it is also wise not to compress + * symlinks (in the unlikely event that they are > 512 bytes and + * can be compressed. + * FIXME: should we set it when reading symlinks too? + */ MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; + set_fs (KERNEL_DS); ret = fat_file_write (filp, buf, count, &filp->f_pos); - Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret)); - set_fs (old_fs); + if (ret < 0) { + printk(KERN_WARNING "umsdos_file_write: ret=%d\n", ret); + goto out; + } +#ifdef UMSDOS_PARANOIA +if (ret != count) +printk(KERN_WARNING "umsdos_file_write: count=%u, ret=%u\n", count, ret); +#endif +out: return ret; } @@ -125,9 +87,8 @@ ssize_t umsdos_file_write_kmem (struct file *filp, const char *buf, size_t count) { - int ret; + ssize_t ret; - Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n")); ret = umsdos_file_write_kmem_real (filp, buf, count); return ret; } @@ -166,7 +127,6 @@ ssize_t umsdos_emd_dir_write ( struct file *filp, 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")); #ifdef __BIG_ENDIAN d->nlink = le16_to_cpu (d->nlink); @@ -179,13 +139,13 @@ filp, buf, count, filp->f_pos)); d->mode = le16_to_cpu (d->mode); #endif -#if UMS_DEBUG +#ifdef UMSDOS_PARANOIA if (written != count) printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count); #endif - return written != count ? -EIO : 0; + return (written != count) ? -EIO : 0; } @@ -199,9 +159,7 @@ written, count); ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count) { - long int ret = 0; - int sizeread; - + ssize_t sizeread, ret = 0; #ifdef __BIG_ENDIAN struct umsdos_dirent *d = (struct umsdos_dirent *) buf; @@ -211,8 +169,9 @@ ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count) filp->f_flags = 0; 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 (KERN_WARNING + "UMSDOS: EMD problem, pos=%Ld, count=%d, read=%d\n", + filp->f_pos, count, sizeread); ret = -EIO; } #ifdef __BIG_ENDIAN @@ -226,24 +185,27 @@ filp->f_pos, sizeread, count); d->mode = le16_to_cpu (d->mode); #endif return ret; - } /* - * Create the EMD dentry for a directory. + * Lookup the EMD dentry for a directory. + * + * Note: the caller must hold a lock on the parent directory. */ struct dentry *umsdos_get_emd_dentry(struct dentry *parent) { struct dentry *demd; - demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE, - UMSDOS_EMD_NAMELEN); + demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE, + UMSDOS_EMD_NAMELEN, 1); return demd; } /* * Check whether a directory has an EMD file. + * + * Note: the caller must hold a lock on the parent directory. */ int umsdos_have_emd(struct dentry *dir) { @@ -260,37 +222,38 @@ int umsdos_have_emd(struct dentry *dir) /* * Create the EMD file for a directory if it doesn't - * already exist. Returns 0 or and error code. + * already exist. Returns 0 or an error code. + * + * Note: the caller must hold a lock on the parent directory. */ 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)) + if (IS_ERR(demd)) { + printk("umsdos_make_emd: can't get dentry in %s, err=%d\n", + parent->d_name.name, err); 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; - } + err = 0; + if (demd->d_inode) + goto out_set; -printk("umsdos_make_emd: creating %s/%s\n", -parent->d_name.name, demd->d_name.name); +Printk(("umsdos_make_emd: creating EMD %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"); + printk (KERN_WARNING + "umsdos_make_emd: create %s/%s failed, err=%d\n", + parent->d_name.name, demd->d_name.name, err); 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_set: + parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino; out_dput: dput(demd); @@ -300,92 +263,6 @@ out: /* - * Locate the EMD file in a directory. - * - * 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_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 (("umsdos_emd_dir_lookup: deja trouve %ld %p\n", - dir->u.umsdos_i.i_emd_dir, ret)); - goto out; - } - - 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 ")); - - 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"); - } - } - 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. @@ -425,6 +302,8 @@ Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n", /* * Write an entry in the EMD file. * Return 0 if OK, -EIO if some error. + * + * Note: the caller must hold a lock on the parent directory. */ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, int free_entry) @@ -443,14 +322,15 @@ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, /* 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); + printk(KERN_WARNING + "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 identified + * Unused entries 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. @@ -506,6 +386,8 @@ struct find_buffer { * Unread bytes are simply moved to the beginning. * * Return -ENOENT if EOF, 0 if OK, a negative error code if any problem. + * + * Note: the caller must hold a lock on the parent directory. */ static int umsdos_fillbuf (struct find_buffer *buf) @@ -556,6 +438,7 @@ static int umsdos_fillbuf (struct find_buffer *buf) * All this to say that umsdos_writeentry must be called after this * function since it relies on the f_pos field of info. * + * Note: the caller must hold a lock on the parent directory. */ /* #Specification: EMD file structure * The EMD file uses a fairly simple layout. It is made of records @@ -662,6 +545,8 @@ demd->d_parent->d_name.name, demd->d_name.name, emd_dir)); } } } +Printk(("umsdos_find: ready to mangle %s, len=%d, pos=%ld\n", +entry->name, entry->name_len, (long)info->f_pos)); umsdos_manglename (info); out_dput: @@ -795,8 +680,8 @@ int umsdos_isempty (struct dentry *dentry) 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); +Printk(("umsdos_isempty: checked %s/%s, empty=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret)); return ret; } @@ -805,11 +690,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret); * Locate an entry in a EMD directory. * Return 0 if OK, error code if not, generally -ENOENT. * - * does not change i_count + * expect argument: + * 0: anything + * 1: file + * 2: directory */ -/* 0: anything */ -/* 1: file */ -/* 2: directory */ int umsdos_findentry (struct dentry *parent, struct umsdos_info *info, int expect) @@ -820,14 +705,16 @@ int umsdos_findentry (struct dentry *parent, struct umsdos_info *info, if (ret) goto out; - if (expect != 0) { - if (S_ISDIR (info->entry.mode)) { - if (expect != 2) - ret = -EISDIR; - } else if (expect == 2) { + switch (expect) { + case 1: + if (S_ISDIR (info->entry.mode)) + ret = -EISDIR; + break; + case 2: + if (!S_ISDIR (info->entry.mode)) ret = -ENOTDIR; - } } + out: Printk (("umsdos_findentry: returning %d\n", ret)); return ret; diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index 5277c9548..339d82801 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -32,15 +32,12 @@ static ssize_t UMSDOS_file_read ( struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; - /* We have to set the access time because msdos don't care */ - /* FIXME */ int ret = fat_file_read (filp, buf, count, ppos); + /* We have to set the access time because msdos don't care */ if (!IS_RDONLY (inode)) { inode->i_atime = CURRENT_TIME; - /* FIXME - * inode->i_dirt = 1; - */ + mark_inode_dirty(inode); } return ret; } @@ -65,10 +62,11 @@ static ssize_t UMSDOS_file_write ( static void UMSDOS_truncate (struct inode *inode) { Printk (("UMSDOS_truncate\n")); - fat_truncate (inode); - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - - /*FIXME inode->i_dirt = 1; */ + if (!IS_RDONLY (inode)) { + fat_truncate (inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); + } } /* Function for normal file system (512 bytes hardware sector size) */ diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index ca20bb8c3..ef6e56dc3 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -19,38 +19,15 @@ #include <linux/umsdos_fs.h> #include <linux/list.h> +extern struct dentry_operations umsdos_dentry_operations; extern struct inode_operations umsdos_rdir_inode_operations; +struct dentry *saved_root = NULL; /* Original root if changed */ struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */ /* directory. See UMSDOS_readdir_x() */ - -/* - * returns inode->i_dentry - * Note: Deprecated; won't work reliably - */ - -struct dentry *geti_dentry (struct inode *inode) -{ - struct dentry *ret; - - if (!inode) { - printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n"); - return NULL; - } - 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); - - 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; -} +static struct dentry *check_pseudo_root(struct super_block *); /* @@ -59,7 +36,7 @@ struct dentry *geti_dentry (struct inode *inode) void fill_new_filp (struct file *filp, struct dentry *dentry) { if (!dentry) - printk("fill_new_filp: NULL dentry!\n"); + printk(KERN_ERR "fill_new_filp: NULL dentry!\n"); memset (filp, 0, sizeof (struct file)); filp->f_reada = 1; @@ -69,61 +46,6 @@ void fill_new_filp (struct file *filp, struct dentry *dentry) } -/* - * 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) -{ -/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */ - - struct dentry *ret; - struct qstr qname; - - if (inode) - Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name)); - else - Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name)); - - qname.name = name; - qname.len = len; - qname.hash = full_name_hash (name, len); - - ret = d_alloc (parent, &qname); /* create new dentry */ - - 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 root! beware !\n")); - } - - - if (inode) { - /* 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); - } - - 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"); - } - - return ret; -} - void UMSDOS_put_inode (struct inode *inode) { @@ -133,11 +55,12 @@ void UMSDOS_put_inode (struct inode *inode) ,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) { + if (inode == pseudo_root) { printk (KERN_ERR "Umsdos: Oops releasing pseudo_root." " Notify jacques@solucorp.qc.ca\n"); } + inode->u.umsdos_i.i_patched = 0; fat_put_inode (inode); } @@ -145,35 +68,23 @@ void UMSDOS_put_inode (struct inode *inode) void UMSDOS_put_super (struct super_block *sb) { Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n")); + if (saved_root) { + shrink_dcache_parent(saved_root); +printk("UMSDOS_put_super: freeing saved root, d_count=%d\n", +saved_root->d_count); + dput(saved_root); + saved_root = NULL; + pseudo_root = NULL; + } msdos_put_super (sb); MOD_DEC_USE_COUNT; } /* - * Call msdos_lookup, but set back the original msdos function table. - * 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) -{ - int ret; - - 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)); - - return ret; -} - -/* - * 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 + * Complete the setup of a directory dentry based on its + * EMD/non-EMD status. If it has an EMD, then plug the * umsdos function table. If not, use the msdos one. - * - * {i,d}_counts are untouched by this function. */ void umsdos_setup_dir(struct dentry *dir) { @@ -192,89 +103,32 @@ dir->d_parent->d_name.name, dir->d_name.name)); } } -/* - * 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; - - inode->u.umsdos_i.i_emd_dir = 0; - - 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); - } -} - /* * 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_new (struct dentry *dentry, off_t f_pos) { - struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1); + struct inode *inode = dentry->d_inode; + struct dentry *demd; - 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; + inode->u.umsdos_i.i_emd_owner = 0; inode->u.umsdos_i.pos = f_pos; - iput (emd_owner); -out: + + /* now check the EMD file */ + demd = umsdos_get_emd_dentry(dentry->d_parent); + if (!IS_ERR(demd)) { + if (demd->d_inode) + inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino; + dput(demd); + } return; } -/* - * Tells if an Umsdos inode has been "patched" once. - * Return != 0 if so. - */ -int umsdos_isinit (struct inode *inode) -{ - return inode->u.umsdos_i.i_emd_owner != 0; -} - /* * 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), @@ -282,88 +136,45 @@ int umsdos_isinit (struct inode *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, off_t f_pos) +void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos) { - Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", - inode->i_ino)); + struct inode *inode = dentry->d_inode; - if (umsdos_isinit (inode)) - goto already_init; +PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino)); + + /* + * Classify the inode based on EMD/non-EMD status. + */ +PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n", +dentry, f_pos)); + umsdos_set_dirinfo_new(dentry, f_pos); 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 { -Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n")); inode->i_op = &umsdos_file_inode_operations_no_bmap; } } else { if (inode->i_op->bmap != NULL) { -Printk ((KERN_DEBUG "umsdos_patch_inode: 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_ISDIR (inode->i_mode)) { - if (dir != NULL) { - umsdos_setup_dir_inode (inode); - } + umsdos_setup_dir(dentry); } 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. - */ - 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); - } - iput (emd_owner); - out: - return; - } -} - -/* - * Patch the inode in a dentry. - */ -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); } @@ -373,127 +184,142 @@ void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos) /* #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 + * not available the first time the inode is accessed, so we use * a value in the inode to tell if it has been finally initialised. * - * 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. + * New inodes are obtained by the lookup and create routines, and + * each of these must ensure that the inode gets patched. */ void UMSDOS_read_inode (struct inode *inode) { - PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", + 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)); - if (S_ISDIR (inode->i_mode) - && (inode->u.umsdos_i.u.dir_info.creating != 0 - || inode->u.umsdos_i.u.dir_info.looking != 0 - || waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) { - Printk (("read inode %d %d %p\n" - ,inode->u.umsdos_i.u.dir_info.creating - ,inode->u.umsdos_i.u.dir_info.looking - ,inode->u.umsdos_i.u.dir_info.p)); - } - /* N.B. Defer this until we have a dentry ... */ - umsdos_patch_inode (inode, NULL, 0); + /* inode needs patching */ + inode->u.umsdos_i.i_patched = 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. +int umsdos_notify_change_locked(struct dentry *, struct iattr *); +/* + * lock the parent dir before starting ... */ -static int internal_notify_change (struct inode *inode, struct iattr *attr) +int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr) { - unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner; + struct inode *dir = dentry->d_parent->d_inode; int ret; - Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n")); + down(&dir->i_sem); + ret = umsdos_notify_change_locked(dentry, attr); + up(&dir->i_sem); + return ret; +} - if ((ret = inode_change_ok (inode, attr)) != 0) +/* + * Must be called with the parent lock held. + */ +int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct dentry *demd; + int ret; + struct file filp; + struct umsdos_dirent entry; + +Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n", +dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched)); + + ret = inode_change_ok (inode, attr); + if (ret) { +printk("UMSDOS_notify_change: %s/%s change not OK, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); goto out; + } if (inode->i_nlink == 0) - goto out_nolink; - + goto out; 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 - */ - 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. - */ - } - iput(emd_owner); + goto out; + + /* get the EMD file dentry */ + demd = umsdos_get_emd_dentry(dentry->d_parent); + ret = PTR_ERR(demd); + if (IS_ERR(demd)) + goto out; + ret = -EPERM; + if (!demd->d_inode) { + printk(KERN_WARNING + "UMSDOS_notify_change: no EMD file %s/%s\n", + demd->d_parent->d_name.name, demd->d_name.name); + goto out_dput; + } + + ret = 0; + /* don't do anything if this is the EMD itself */ + if (inode == demd->d_inode) + goto out_dput; + + /* This inode is not a EMD file nor an inode used internally + * by MSDOS, so we can update its status. + * See emd.c + */ + + fill_new_filp (&filp, demd); + filp.f_pos = inode->u.umsdos_i.pos; +Printk(("UMSDOS_notify_change: %s/%s reading at %d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, (int) 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) { + printk(KERN_WARNING + "umsdos_notify_change: %s/%s EMD read error, ret=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name,ret); + goto out_dput; } -out_nolink: + 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); + if (ret) + printk(KERN_WARNING + "umsdos_notify_change: %s/%s EMD write error, ret=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name,ret); + + 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. + */ +#ifdef UMSDOS_DEBUG_VERBOSE +if (entry.flags & UMSDOS_HIDDEN) +printk("umsdos_notify_change: %s/%s hidden, nlink=%d, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret); +#endif + +out_dput: + dput(demd); +out: if (ret == 0) inode_setattr (inode, attr); -out: return ret; } -int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr) -{ - return internal_notify_change (dentry->d_inode, attr); -} - /* * Update the disk with the inode content */ @@ -514,7 +340,8 @@ void UMSDOS_write_inode (struct inode *inode) * But it has the side effect to re"dirt" the inode. */ /* - * internal_notify_change (inode, &newattrs); + * UMSDOS_notify_change (inode, &newattrs); + * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */ } @@ -539,7 +366,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, int silent) { struct super_block *res; - struct inode *pseudo = NULL; + struct dentry *new_root; MOD_INC_USE_COUNT; MSDOS_SB(sb)->options.isvfat = 0; @@ -551,28 +378,38 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, if (!res) goto out_fail; - printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 " + printk (KERN_INFO "UMSDOS dentry-pre 0.84 " "(compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); sb->s_op = &umsdos_sops; MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */ - /* FIXME:?? clear d_op on root so it will not be inherited */ - sb->s_root->d_op = NULL; + /* install our dentry operations ... */ + sb->s_root->d_op = &umsdos_dentry_operations; + umsdos_patch_dentry_inode(sb->s_root, 0); + + /* Check whether to change to the /linux root */ + new_root = check_pseudo_root(sb); - pseudo = sb->s_root->d_inode; - umsdos_setup_dir(sb->s_root); + if (new_root) { + /* sanity check */ + if (new_root->d_op != &umsdos_dentry_operations) + printk("umsdos_read_super: pseudo-root wrong ops!\n"); -#if 0 - if (pseudo) { - pseudo_root_stuff(); + pseudo_root = new_root->d_inode; + + saved_root = sb->s_root; + sb->s_root = new_root; + printk(KERN_INFO "UMSDOS: changed to alternate root\n"); } -#endif /* if d_count is not 1, mount will fail with -EBUSY! */ if (sb->s_root->d_count > 1) { shrink_dcache_sb(sb); + if (sb->s_root->d_count > 1) { + printk(KERN_ERR "UMSDOS: root count %d > 1 !", sb->s_root->d_count); + } } return sb; @@ -584,78 +421,50 @@ out_fail: } /* - * FIXME URGENT: - * disable pseudo root-for the moment of testing. - * re-enable this before release ! + * Check for an alternate root if we're the root device. */ -#if 0 -void pseudo_root_stuff(void) +static struct dentry *check_pseudo_root(struct super_block *sb) { - struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL; - - root = creat_dentry (UMSDOS_PSDROOT_NAME, - strlen (UMSDOS_PSDROOT_NAME), - NULL, res->s_root); - sbin = creat_dentry ("sbin", 4, NULL, root); + struct dentry *root, *init; - Printk ((KERN_DEBUG "Mounting root\n")); - if (umsdos_real_lookup (pseudo, root) == 0 - && (root->d_inode != NULL) - && S_ISDIR (root->d_inode->i_mode)) { - - int pseudo_ok = 0; - -Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME)); - etc = creat_dentry ("etc", 3, NULL, root); - - - 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; - } - - /* 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 (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; - } - /* FIXME - * - * iput (sbin); - * iput (etc); - */ + /* + * Check whether we're mounted as the root device. + * If so, this should be the only superblock. + */ + if (sb->s_list.next->next != &sb->s_list) + goto out_noroot; +printk("check_pseudo_root: mounted as root\n"); + + root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0); + if (IS_ERR(root)) + goto out_noroot; + if (!root->d_inode) + goto out_dput; +printk("check_pseudo_root: found %s/%s\n", +root->d_parent->d_name.name, root->d_name.name); + + /* look for /sbin/init */ + init = lookup_dentry("sbin/init", dget(root), 0); + if (!IS_ERR(init)) { + if (init->d_inode) + goto root_ok; + dput(init); } + /* check for other files? */ + goto out_dput; + +root_ok: +printk("check_pseudo_root: found %s/%s, enabling pseudo-root\n", +init->d_parent->d_name.name, init->d_name.name); + dput(init); + return root; + + /* Alternate root not found ... */ +out_dput: + dput(root); +out_noroot: + return NULL; } -#endif static struct file_system_type umsdos_fs_type = diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 84e1f5034..f26d19ba8 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -78,6 +78,9 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, struct file new_filp; struct umsdos_ioctl data; +Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n", +dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr)); + /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ if (cmd != UMSDOS_GETVERSION && cmd != UMSDOS_READDIR_DOS @@ -164,20 +167,19 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, ret = PTR_ERR(demd); if (IS_ERR(demd)) goto out; + ret = 0; + if (!demd->d_inode) + goto read_dput; + fill_new_filp(&new_filp, demd); new_filp.f_pos = filp->f_pos; - - while (1) { + while (new_filp.f_pos < demd->d_inode->i_size) { off_t f_pos = new_filp.f_pos; struct umsdos_dirent entry; struct umsdos_info info; - ret = 0; - if (new_filp.f_pos >= demd->d_inode->i_size) - break; - ret = umsdos_emd_dir_readentry (&new_filp, &entry); - if (ret < 0) + if (ret) break; if (entry.name_len <= 0) continue; @@ -198,6 +200,7 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, } /* update the original f_pos */ filp->f_pos = new_filp.f_pos; + read_dput: d_drop(demd); dput(demd); goto out; @@ -217,6 +220,8 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, extern struct inode_operations umsdos_rdir_inode_operations; ret = umsdos_make_emd(dentry); +Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret)); dir->i_op = (ret == 0) ? &umsdos_dir_inode_operations : &umsdos_rdir_inode_operations; @@ -257,18 +262,18 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, * 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 + * This ioctl allows umssync to rename a mangled 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); + data.dos_dirent.d_reclen ,1); 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); + data.umsdos_dirent.name_len, 1); ret = PTR_ERR(new_dentry); if (!IS_ERR(new_dentry)) { printk("umsdos_ioctl: renaming %s/%s to %s/%s\n", @@ -277,7 +282,6 @@ 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; } @@ -301,6 +305,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); data.umsdos_dirent.name_len, &info); ret = umsdos_delentry (dentry, &info, S_ISDIR (data.umsdos_dirent.mode)); + if (ret) { + printk(KERN_WARNING + "umsdos_ioctl: delentry %s/%s failed, ret=%d\n", + dentry->d_name.name, info.entry.name, ret); + } goto out; } else if (cmd == UMSDOS_UNLINK_DOS) { @@ -314,14 +323,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); * Return 0 if success. */ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, - data.dos_dirent.d_reclen); + data.dos_dirent.d_reclen, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out; ret = -ENOENT; - if (temp->d_inode) - ret = msdos_unlink (dir, temp); - d_drop(temp); + if (temp->d_inode) { + ret = -EISDIR; + if (!S_ISDIR(temp->d_inode->i_mode)) + ret = msdos_unlink (dir, temp); + } dput (temp); goto out; } @@ -336,14 +347,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); * Return 0 if success. */ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, - data.dos_dirent.d_reclen); + data.dos_dirent.d_reclen, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out; ret = -ENOENT; - if (temp->d_inode) - ret = msdos_rmdir (dir, temp); - d_drop(temp); + if (temp->d_inode) { + ret = -ENOTDIR; + if (S_ISDIR(temp->d_inode->i_mode)) + ret = msdos_rmdir (dir, temp); + } dput (temp); goto out; @@ -362,7 +375,7 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); struct inode *inode; dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, - data.dos_dirent.d_reclen); + data.dos_dirent.d_reclen, 1); ret = PTR_ERR(dret); if (IS_ERR(dret)) goto out; @@ -380,7 +393,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); sizeof (data.stat))) ret = 0; } - d_drop(dret); dput(dret); goto out; } diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c index c7f12722f..98a5c25ab 100644 --- a/fs/umsdos/mangle.c +++ b/fs/umsdos/mangle.c @@ -411,6 +411,7 @@ int umsdos_parse ( /* Why not use info->fake.len everywhere? Is it longer? */ memcpy (info->entry.name, fname, len); + info->entry.name[len] = '\0'; /* for printk */ info->entry.name_len = len; ret = 0; } diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index b365fbf04..831802dcf 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -177,17 +177,15 @@ void umsdos_endlookup (struct inode *dir) 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); + current->fsuid == dir->i_uid || + capable (CAP_FOWNER)); } 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; if (umsdos_is_pseudodos (dir, dentry)) { @@ -198,20 +196,6 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, * The pseudo sub-directory /DOS can't be removed! * EPERM is returned. */ - ret = -EPERM; - ret = errcod; - } else if (name[0] == '.' - && (len == 1 || (len == 2 && name[1] == '.'))) { - /* #Specification: create / . and .. - * If one try to creates . or .., it always fail and return - * EEXIST. - * - * If one try to delete . or .., it always fail and return - * EPERM. - * - * This should be test at the VFS layer level to avoid - * duplicating this in all file systems. Any comments ? - */ ret = errcod; } return ret; @@ -224,8 +208,8 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry, * * 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 + * #Specification: create / file exists in DOS + * Here is a situation: we are trying to create a file with * UMSDOS. The file is unknown to UMSDOS but already * exists in the DOS directory. * @@ -254,19 +238,9 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry, 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_path (dentry, "umsdos_create_any"); ret = umsdos_nevercreat (dir, dentry, -EEXIST); - if (ret) { -Printk (("%d/\n", ret)); + if (ret) goto out; - } ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); if (ret) @@ -279,79 +253,82 @@ Printk (("%d/\n", ret)); 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; + goto out; - /* create short name dentry */ + /* do a real lookup to get the short name dentry */ fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len); + info.fake.len, 1); ret = PTR_ERR(fake); if (IS_ERR(fake)) - goto out_unlock; + goto out_remove; + + /* keep the short name anonymous ... */ + if (dentry != fake) + d_drop(fake); /* should not exist yet ... */ ret = -EEXIST; if (fake->d_inode) - goto out_remove; + goto out_remove_dput; ret = msdos_create (dir, fake, S_IFREG | 0777); if (ret) - goto out_remove; + goto out_remove_dput; 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"); - /* * 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); } - - 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); + if (inode->i_count > 1) { + printk(KERN_WARNING + "umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + inode->i_ino, inode->i_count); + } + umsdos_lookup_patch_new(dentry, &info); -out_unlock: - umsdos_unlockcreate (dir); out: return ret; + + /* Creation failed ... remove the EMD entry */ +out_remove_dput: + dput(fake); +out_remove: + if (ret == -EEXIST) + printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n", + dentry->d_parent->d_name.name, info.fake.fname); + umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode)); + goto out; +} + +/* + * 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 EMD file. + * + * Return the status of the operation. 0 mean success. + */ +int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode) +{ + return umsdos_create_any (dir, dentry, mode, 0, 0); } + /* * 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) + struct umsdos_info *old_info) { - /* != 0, this is the value of flags */ new_info->entry.mode = old_info->entry.mode; new_info->entry.rdev = old_info->entry.rdev; new_info->entry.uid = old_info->entry.uid; @@ -359,10 +336,11 @@ static void umsdos_ren_init (struct umsdos_info *new_info, new_info->entry.ctime = old_info->entry.ctime; new_info->entry.atime = old_info->entry.atime; new_info->entry.mtime = old_info->entry.mtime; - new_info->entry.flags = flags ? flags : old_info->entry.flags; + new_info->entry.flags = old_info->entry.flags; new_info->entry.nlink = old_info->entry.nlink; } +#ifdef OBSOLETE #define chkstk() \ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\ printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \ @@ -373,6 +351,7 @@ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\ #undef chkstk #define chkstk() do { } while (0); +#endif /* * Rename a file (move) in the file system. @@ -382,124 +361,238 @@ 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 inode *old_inode = old_dentry->d_inode; + struct dentry *old, *new, *old_emd; + int err, ret, rehash = 0; struct umsdos_info old_info; struct umsdos_info new_info; - old_ret = umsdos_parse (old_dentry->d_name.name, + ret = -EPERM; + err = umsdos_parse (old_dentry->d_name.name, old_dentry->d_name.len, &old_info); - if (old_ret) + if (err) goto out; - new_ret = umsdos_parse (new_dentry->d_name.name, + err = umsdos_parse (new_dentry->d_name.name, new_dentry->d_name.len, &new_info); - if (new_ret) + if (err) goto out; - check_dentry_path (old_dentry, "umsdos_rename_f OLD"); - check_dentry_path (new_dentry, "umsdos_rename_f OLD"); + /* Get the EMD dentry for the old parent */ + old_emd = umsdos_get_emd_dentry(old_dentry->d_parent); + ret = PTR_ERR(old_emd); + if (IS_ERR(old_emd)) + goto out; - chkstk (); -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 (); + ret = umsdos_findentry(old_emd->d_parent, &old_info, 0); if (ret) { -Printk (("ret %d ", ret)); + printk(KERN_ERR + "umsdos_rename_f: old entry %s/%s not in EMD, ret=%d\n", + old_dentry->d_parent->d_name.name, old_info.entry.name, + 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 ")); +printk("umsdos_rename_f: %s/%s old sticky bit, fsuid=%d, uid=%d, dir=%d\n", +old_dentry->d_parent->d_name.name, old_info.entry.name, +current->fsuid, old_info.entry.uid, old_dir->i_uid); 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; + /* + * Check whether the new_name already exists, and + * if so whether we're allowed to replace it. + */ + err = umsdos_findentry(new_dentry->d_parent, &new_info, 0); + if (err == 0) { + /* Are we allowed to replace it? */ + if (is_sticky(new_dir, new_info.entry.uid)) { +Printk (("sticky set on new ")); + goto out_unlock; + } + /* check whether it _really_ exists ... */ + ret = -EEXIST; + if (new_dentry->d_inode) + goto out_unlock; + + /* bogus lookup? complain and fix up the EMD ... */ + printk(KERN_WARNING + "umsdos_rename_f: entry %s/%s exists, inode NULL??\n", + new_dentry->d_parent->d_name.name, new_info.entry.name); + err = umsdos_delentry(new_dentry->d_parent, &new_info, + S_ISDIR(new_info.entry.mode)); } - Printk (("new newentry ")); - umsdos_ren_init (&new_info, &old_info, flags); +Printk (("new newentry ")); + /* create the new entry ... */ + umsdos_ren_init (&new_info, &old_info); + if (flags) + new_info.entry.flags = flags; ret = umsdos_newentry (new_dentry->d_parent, &new_info); - chkstk (); if (ret) { -Printk (("ret %d %d ", ret, new_info.fake.len)); + printk(KERN_WARNING + "umsdos_rename_f: newentry %s/%s failed, ret=%d\n", + new_dentry->d_parent->d_name.name, new_info.entry.name, + ret); 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)) + /* If we're moving a hardlink, drop it first */ + if (old_info.entry.flags & UMSDOS_HLINK) { + rehash = !list_empty(&old_dentry->d_hash); + d_drop(old_dentry); +printk("umsdos_rename_f: moving hardlink %s/%s, rehash=%d\n", +old_dentry->d_parent->d_name.name, old_info.entry.name, rehash); + } + + /* Do a real lookup to get the old short name dentry */ + old = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, + old_info.fake.len, 1); + ret = PTR_ERR(old); + if (IS_ERR(old)) { + printk(KERN_WARNING + "umsdos_rename_f: lookup old dentry %s/%s, ret=%d\n", + old_dentry->d_parent->d_name.name, old_info.fake.fname, + ret); 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; + } + /* short and long name dentries match? */ + if (old == old_dentry) + dput(old); + else { + /* make sure it's the same inode! */ + ret = -ENOENT; + if (old->d_inode != old_inode) + goto out_dput; + /* + * A cross-directory move with different short and long + * names has nasty complications: msdos-fs will need to + * change inodes, so we must check whether the original + * dentry is busy, and if the rename succeeds the short + * dentry will come back with a different inode. + * + * To handle this, we drop the dentry and free the inode, + * and then pick up the new inode after the rename. + */ + if (old_dir != new_dir) { + ret = -EBUSY; + if (old_dentry->d_count > 1) { +printk("umsdos_rename_f: old dentry %s/%s busy, d_count=%d\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name,old_dentry->d_count); + goto out_dput; + } + d_drop(old_dentry); + d_delete(old_dentry); +printk("umsdos_rename_f: cross-dir move, %s/%s dropped\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name); + } + } + new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, - new_info.fake.len); + new_info.fake.len, 1); ret = PTR_ERR(new); - if (IS_ERR(new)) + if (IS_ERR(new)) { + printk(KERN_WARNING + "umsdos_rename_f: lookup new dentry %s/%s, ret=%d\n", + new_dentry->d_parent->d_name.name, new_info.fake.fname, + ret); goto out_dput; - - Printk (("msdos_rename ")); - check_dentry_path (old, "umsdos_rename_f OLD2"); - check_dentry_path (new, "umsdos_rename_f NEW2"); + } +#ifdef UMSDOS_PARANOIA +if (new->d_inode != new_dentry->d_inode) +printk("umsdos_rename_f: new %s/%s, inode %p!=%p??\n", +new->d_parent->d_name.name, new->d_name.name, new->d_inode,new_dentry->d_inode); +#endif + /* short and long name dentries match? */ + if (new == new_dentry) + dput(new); + +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_rename_f: msdos_rename %s/%s(%ld) to %s/%s(%ld)\n", +old->d_parent->d_name.name, old->d_name.name, old->d_inode->i_ino, +new->d_parent->d_name.name, new->d_name.name, +new->d_inode ? new->d_inode->i_ino : 0); +#endif + /* Do the msdos-level rename */ ret = msdos_rename (old_dir, old, new_dir, new); - chkstk (); -printk("after m_rename ret %d ", ret); - /* dput(old); */ - dput(new); +Printk(("umsdos_rename_f: now %s/%s, ret=%d\n", +old->d_parent->d_name.name, old->d_name.name, ret)); + + if (new != new_dentry) + dput(new); + /* If the rename failed, remove the new EMD entry */ if (ret != 0) { +Printk(("umsdos_rename_f: rename failed, ret=%d, removing %s/%s\n", +ret, new_dentry->d_parent->d_name.name, new_info.entry.name)); umsdos_delentry (new_dentry->d_parent, &new_info, S_ISDIR (new_info.entry.mode)); - chkstk (); goto out_dput; } - ret = umsdos_delentry (old_dentry->d_parent, &old_info, - S_ISDIR (old_info.entry.mode)); - chkstk (); - if (ret) - goto out_dput; -#if 0 + /* + * Rename successful ... remove the old name from the EMD. + * Note that we use the EMD parent here, as the old dentry + * may have moved to a new parent ... + */ + err = umsdos_delentry (old_emd->d_parent, &old_info, + S_ISDIR (old_info.entry.mode)); + if (err) { + /* Failed? Complain a bit, but don't fail the operation */ + printk(KERN_WARNING + "umsdos_rename_f: delentry %s/%s failed, error=%d\n", + old_emd->d_parent->d_name.name, old_info.entry.name, + err); + } + + /* + * Check whether to update the dcache ... if both + * old and new dentries match, it's already correct. + * If the targets were aliases, the old short-name + * dentry has the original target name. + */ + if (old_dentry != old) { + if (!old_dentry->d_inode) { + struct inode *inode = old->d_inode; + inode->i_count++; + d_instantiate(old_dentry, inode); +printk("umsdos_rename_f: %s/%s gets new ino=%ld\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino); + } + if (new_dentry == new) + new_dentry = old; + goto move_it; + } else if (new_dentry != new) { + move_it: + /* this will rehash the dentry ... */ + d_move(old_dentry, new_dentry); + } + /* Check whether the old inode changed ... */ + if (old_dentry->d_inode != old_inode) { + umsdos_lookup_patch_new(old_dentry, &new_info); + } + /* * 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); + umsdos_set_dirinfo_new(old_dentry, new_info.f_pos); + /* dput() the dentry if we haven't already */ out_dput: - dput(dret); + if (old_dentry != old) + dput(old); out_unlock: - Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n")); + dput(old_emd); 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; } @@ -509,54 +602,51 @@ out: * 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 + * A symbolic link is simply a file which holds 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 + * I see two different ways to do this: One is to place the link data + * in unused entries of the EMD file; the other is to have a separate * file dedicated to hold all symbolic links data. * * Let's go for simplicity... */ +extern struct inode_operations umsdos_symlink_inode_operations; + static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry, const char *symname, int mode, char flags) { int ret, len; struct file filp; +Printk(("umsdos_symlink: %s/%s to %s\n", +dentry->d_parent->d_name.name, dentry->d_name.name, symname)); + ret = umsdos_create_any (dir, dentry, mode, 0, flags); if (ret) { -Printk (("umsdos_symlink ret %d ", ret)); + printk(KERN_WARNING + "umsdos_symlink: create failed, ret=%d\n", ret); goto out; } - len = strlen (symname); - 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)); + len = strlen (symname); 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; - } - } - if (ret != 0) { - UMSDOS_unlink (dir, dentry); - } - + if (ret < 0) + goto out_unlink; + if (ret != len) + goto out_error; + ret = 0; out: - Printk (("\n")); return ret; + +out_error: + ret = -EIO; +out_unlink: + printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n"); + UMSDOS_unlink (dir, dentry); + goto out; } /* @@ -576,13 +666,20 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir, struct dentry *dentry) { struct inode *oldinode = olddentry->d_inode; - struct inode *olddir; - char *path; + struct inode *olddir = olddentry->d_parent->d_inode; struct dentry *temp; + char *path; unsigned long buffer; int ret; - struct umsdos_dirent entry; + struct umsdos_info old_info; + struct umsdos_info hid_info; +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_link: new %s%s -> %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +olddentry->d_parent->d_name.name, olddentry->d_name.name); +#endif + ret = -EPERM; if (S_ISDIR (oldinode->i_mode)) goto out; @@ -596,80 +693,174 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir, if (!buffer) goto out; - olddir = olddentry->d_parent->d_inode; - umsdos_lockcreate2 (dir, olddir); + /* + * Lock the link parent if it's not the same directory. + */ + ret = -EDEADLOCK; + if (olddir != dir) { + if (atomic_read(&olddir->i_sem.count) < 1) + goto out_free; + down(&olddir->i_sem); + } - /* get the entry for the old name */ - ret = umsdos_dentry_to_entry(olddentry, &entry); + /* + * Parse the name and get the visible directory entry. + */ + ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len, + &old_info); 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_findentry (olddentry->d_parent, &old_info, 1); + if (ret) { +printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name, ret); + goto out_unlock; + } - ret = umsdos_newhidden (olddentry->d_parent, &info); - if (ret) + /* + * If the visible dentry is a pseudo-hardlink, the original + * file must be already hidden. + */ + if (!(old_info.entry.flags & UMSDOS_HLINK)) { + int err; + + /* create a hidden link name */ + ret = umsdos_newhidden (olddentry->d_parent, &hid_info); + if (ret) { +printk("umsdos_link: can't make hidden %s/%s, ret=%d\n", +olddentry->d_parent->d_name.name, hid_info.entry.name, 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); + /* + * Make a dentry and rename the original file ... + */ + temp = umsdos_lookup_dentry(olddentry->d_parent, + hid_info.entry.name, + hid_info.entry.name_len, 0); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) { +printk("umsdos_link: lookup %s/%s failed, ret=%d\n", +dentry->d_parent->d_name.name, hid_info.entry.name, ret); + goto cleanup; } + /* rename the link to the hidden location ... */ + ret = umsdos_rename_f (olddir, olddentry, olddir, temp, + UMSDOS_HIDDEN); dput(temp); + if (ret) { +printk("umsdos_link: rename to %s/%s failed, ret=%d\n", +temp->d_parent->d_name.name, temp->d_name.name, ret); + goto cleanup; + } + /* mark the inode as a hardlink */ + oldinode->u.umsdos_i.i_is_hlink = 1; + + /* + * Capture the path to the hidden link. + */ + path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE); +Printk(("umsdos_link: hidden link path=%s\n", path)); + + /* + * Recreate a dentry for the original name and symlink it, + * then symlink the new dentry. Don't give up if one fails, + * or we'll lose the file completely! + * + * Note: this counts as the "original" reference, so we + * don't increment i_nlink for this one. + */ + temp = umsdos_lookup_dentry(olddentry->d_parent, + old_info.entry.name, + old_info.entry.name_len, 0); + ret = PTR_ERR(temp); + if (!IS_ERR(temp)) { + ret = umsdos_symlink_x (olddir, temp, path, + S_IFREG | 0777, UMSDOS_HLINK); + dput(temp); + } + + /* This symlink increments i_nlink (see below.) */ + err = umsdos_symlink_x (dir, dentry, path, + S_IFREG | 0777, UMSDOS_HLINK); + /* fold the two errors */ + if (!ret) + ret = err; + goto out_unlock; + + /* creation failed ... remove the link entry */ + cleanup: +printk("umsdos_link: link failed, ret=%d, removing %s/%s\n", +ret, olddentry->d_parent->d_name.name, hid_info.entry.name); + err = umsdos_delentry(olddentry->d_parent, &hid_info, 0); 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); } +Printk(("UMSDOS_link: %s/%s already hidden\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name)); + /* + * The original file is already hidden, and we need to get + * the dentry for its real name, not the visible name. + * N.B. make sure it's the hidden inode ... + */ + if (!oldinode->u.umsdos_i.i_is_hlink) + printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n", + olddentry->d_parent->d_name.name, + olddentry->d_name.name, oldinode->i_ino); + + /* + * In order to get the correct (real) inode, we just drop + * the original dentry. + */ + d_drop(olddentry); +Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname)); + + /* Do a real lookup to get the short name dentry */ + temp = umsdos_lookup_dentry(olddentry->d_parent, + old_info.fake.fname, + old_info.fake.len, 1); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out_unlock; + + /* now resolve the link ... */ + temp = umsdos_solve_hlink(temp); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out_unlock; + path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE); + dput(temp); +Printk(("umsdos_link: %s/%s already hidden, path=%s\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name, path)); + + /* finally we can symlink it ... */ + ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK); + out_unlock: - umsdos_unlockcreate (olddir); - umsdos_unlockcreate (dir); - free_page(buffer); -out: + /* remain locked for the call to notify_change ... */ if (ret == 0) { struct iattr newattrs; +#ifdef UMSDOS_PARANOIA +if (!oldinode->u.umsdos_i.i_is_hlink) +printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino); +#endif oldinode->i_nlink++; +Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n", +olddentry->d_parent->d_name.name, olddentry->d_name.name, +oldinode->i_ino, oldinode->i_nlink)); newattrs.ia_valid = 0; - ret = UMSDOS_notify_change (olddentry, &newattrs); + ret = umsdos_notify_change_locked(olddentry, &newattrs); } - Printk (("umsdos_link %d\n", ret)); - return ret; -} - + if (olddir != dir) + up(&olddir->i_sem); -/* - * 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 EMD file. - * - * Return the status of the operation. 0 mean success. - */ -int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode) -{ - int ret; - Printk ((KERN_ERR "UMSDOS_create: entering\n")); - check_dentry_path (dentry, "UMSDOS_create START"); - ret = umsdos_create_any (dir, dentry, mode, 0, 0); - check_dentry_path (dentry, "UMSDOS_create END"); +out_free: + free_page(buffer); +out: + Printk (("umsdos_link %d\n", ret)); return ret; } @@ -699,12 +890,9 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode) goto out; ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info); - if (ret) { -Printk (("umsdos_mkdir %d\n", ret)); + if (ret) goto out; - } - umsdos_lockcreate (dir); info.entry.mode = mode | S_IFDIR; info.entry.rdev = 0; info.entry.uid = current->fsuid; @@ -713,72 +901,74 @@ Printk (("umsdos_mkdir %d\n", ret)); info.entry.flags = 0; info.entry.nlink = 1; ret = umsdos_newentry (dentry->d_parent, &info); - if (ret) { -Printk (("newentry %d ", ret)); - goto out_unlock; - } + if (ret) + goto out; /* lookup the short name dentry */ temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len); + info.fake.len, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) - goto out_unlock; + goto out_remove; + + /* Keep the short name dentry anonymous */ + if (temp != dentry) + d_drop(temp); /* 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; + goto out_remove_dput; } ret = msdos_mkdir (dir, temp, mode); if (ret) - goto out_remove; + goto out_remove_dput; + /* + * Lock the inode to protect the EMD creation ... + */ inode = temp->d_inode; - umsdos_lookup_patch_new(temp, &info.entry, info.f_pos); + down(&inode->i_sem); /* * 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"); +if (dentry->d_inode) +printk("umsdos_mkdir: dentry not negative!\n"); + inode->i_count++; + d_instantiate(dentry, inode); } - - /* create the EMD file */ - err = umsdos_make_emd(dentry); + /* N.B. this should have an option to create the EMD ... */ + umsdos_lookup_patch_new(dentry, &info); /* - * set up the dir so it is promoted to EMD, - * with the EMD file invisible inside it. + * Create the EMD file, and set up the dir so it is + * promoted to EMD with the EMD file invisible. + * + * N.B. error return if EMD fails? */ - umsdos_setup_dir(temp); - goto out_dput; - -out_remove: - umsdos_delentry (dentry->d_parent, &info, 1); + err = umsdos_make_emd(dentry); + umsdos_setup_dir(dentry); -out_dput: - /* kill off the short name dentry */ - if (temp != dentry) - d_drop(temp); + up(&inode->i_sem); dput(temp); -out_unlock: - umsdos_unlockcreate (dir); - Printk (("umsdos_mkdir %d\n", ret)); out: + Printk(("umsdos_mkdir: %s/%s, ret=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, ret)); return ret; + + /* an error occurred ... remove EMD entry. */ +out_remove_dput: + dput(temp); +out_remove: + umsdos_delentry (dentry->d_parent, &info, 1); + goto out; } /* @@ -801,11 +991,7 @@ out: int UMSDOS_mknod (struct inode *dir, struct dentry *dentry, int mode, int rdev) { - int ret; - check_dentry_path (dentry, "UMSDOS_mknod START"); - ret = umsdos_create_any (dir, dentry, mode, rdev, 0); - check_dentry_path (dentry, "UMSDOS_mknod END"); - return ret; + return umsdos_create_any (dir, dentry, mode, rdev, 0); } /* @@ -821,72 +1007,62 @@ int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry) 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) + ret = -EBUSY; + if (!list_empty(&dentry->d_hash)) goto out; -#endif - 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", + /* check the 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; - } + goto out; } /* check whether the EMD is empty */ - empty = umsdos_isempty (dentry); ret = -ENOTEMPTY; - if (empty == 0) - goto out_unlock; + empty = umsdos_isempty (dentry); /* 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); + if (!IS_ERR(demd)) { + err = -ENOENT; + if (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; - } +#ifdef UMSDOS_PARANOIA +if (err) +printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n", +demd->d_parent->d_name.name, demd->d_name.name, err); +#endif + dput(demd); + if (!err) + ret = 0; + } + } else if (empty == 2) + ret = 0; + if (ret) + goto out; 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); + info.fake.len, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) - goto out_unlock; + goto out; /* - * If the short name matches the dentry, dput() it now. + * If the short name is an alias, dput() it now; + * otherwise d_drop() it to keep it anonymous. */ - if (temp == dentry) { + if (temp == dentry) dput(temp); -printk("umsdos_rmdir: %s/%s, short matches long\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - } + else + d_drop(temp); /* * Attempt to remove the msdos name. @@ -897,20 +1073,15 @@ dentry->d_parent->d_name.name, dentry->d_name.name); /* OK so far ... remove the name from the EMD */ ret = umsdos_delentry (dentry->d_parent, &info, 1); +#ifdef UMSDOS_PARANOIA +if (ret) +printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret); +#endif -out_dput: /* dput() temp if we didn't do it above */ - if (temp != dentry) { - d_drop(temp); +out_dput: + if (temp != dentry) 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)); @@ -922,8 +1093,8 @@ out: * 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 + * When we delete a file and this file is a link, + * we must subtract 1 from the nlink field of the * hidden link. * * If the count goes to 0, we delete this hidden @@ -931,12 +1102,12 @@ out: */ int UMSDOS_unlink (struct inode *dir, struct dentry *dentry) { - struct dentry *temp; + struct dentry *temp, *link = NULL; struct inode *inode; - int ret; + int ret, rehash = 0; struct umsdos_info info; -Printk (("UMSDOS_unlink: entering %s/%s\n", +Printk(("UMSDOS_unlink: entering %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name)); ret = umsdos_nevercreat (dir, dentry, -EPERM); @@ -950,11 +1121,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name)); umsdos_lockcreate (dir); ret = umsdos_findentry (dentry->d_parent, &info, 1); if (ret) { -printk("UMSDOS_unlink: findentry returned %d\n", ret); +printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, ret); goto out_unlock; } Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); + ret = -EPERM; /* check sticky bit */ if (is_sticky(dir, info.entry.uid)) { @@ -963,65 +1136,54 @@ dentry->d_parent->d_name.name, dentry->d_name.name); goto out_unlock; } - ret = 0; + /* + * Note! If this is a hardlink and the names are aliased, + * the short-name lookup will return the hardlink dentry. + * In order to get the correct (real) inode, we just drop + * the original dentry. + */ 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_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) { - ret = UMSDOS_unlink (dentry->d_parent->d_inode, - dentry); - } - } else { - struct iattr newattrs; - newattrs.ia_valid = 0; - ret = UMSDOS_notify_change (dentry, &newattrs); - } + rehash = !list_empty(&dentry->d_hash); + d_drop(dentry); +Printk(("UMSDOS_unlink: hard link %s/%s, fake=%s, rehash=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash)); } - if (ret) - goto out_unlock; - /* get the short name dentry */ + /* Do a real lookup to get the short name dentry */ temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, - info.fake.len); + info.fake.len, 1); + ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out_unlock; /* - * If the short name matches the long, - * dput() it now so it's not busy. + * Resolve hardlinks now, but defer processing until later. */ - if (temp == dentry) { -printk("UMSDOS_unlink: %s/%s, short matches long\n", -dentry->d_parent->d_name.name, dentry->d_name.name); - dput(temp); + if (info.entry.flags & UMSDOS_HLINK) { + link = umsdos_solve_hlink(dget(temp)); } + /* + * If the short and long names are aliased, + * dput() it now so the dentry isn't busy. + */ + if (temp == dentry) + dput(temp); + + /* Delete the EMD entry */ ret = umsdos_delentry (dentry->d_parent, &info, 0); - if (ret && ret != -ENOENT) + if (ret && ret != -ENOENT) { + printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n", + info.entry.name, ret); 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)); +#ifdef UMSDOS_PARANOIA +if (ret) +printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n", +temp->d_parent->d_name.name, temp->d_name.name, ret); +#endif /* dput() temp if we didn't do it above */ out_dput: @@ -1030,12 +1192,64 @@ out_dput: 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); + + /* + * Now check for deferred handling of a hardlink. + */ + if (!link) + goto out; + + if (IS_ERR(link)) { +printk("umsdos_unlink: failed to resolve %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); + if (!ret) + ret = PTR_ERR(link); + goto out; + } + +Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n", +link->d_parent->d_name.name, link->d_name.name, ret)); + + /* already have an error? */ + if (ret) + goto out_cleanup; + + /* make sure the link exists ... */ + inode = link->d_inode; + if (!inode) { + printk(KERN_WARNING "umsdos_unlink: hard link not found\n"); + goto out_cleanup; + } + + /* + * If this was the last linked reference, delete it now. + * + * N.B. Deadlock problem? We should be holding the lock + * for the hardlink's parent, but another process might + * be holding that lock waiting for us to finish ... + */ + if (inode->i_nlink <= 1) { + ret = UMSDOS_unlink (link->d_parent->d_inode, link); + if (ret) { + printk(KERN_WARNING + "umsdos_unlink: link removal failed, ret=%d\n", + ret); + } + } else { + struct iattr newattrs; + inode->i_nlink--; + newattrs.ia_valid = 0; + ret = umsdos_notify_change_locked(link, &newattrs); + } + +out_cleanup: + d_drop(link); + dput(link); + out: Printk (("umsdos_unlink %d\n", ret)); return ret; @@ -1048,30 +1262,49 @@ out: int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { + struct dentry *new_target; int ret; +#ifdef UMSDOS_DEBUG_VERBOSE +printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, +old_dentry->d_inode->i_ino, +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0); +#endif 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)); + /* + * If the target already exists, delete it first. + */ + if (new_dentry->d_inode) { + if (S_ISDIR(new_dentry->d_inode->i_mode)) + ret = UMSDOS_rmdir (new_dir, new_dentry); + else + ret = UMSDOS_unlink (new_dir, new_dentry); + if (ret) + goto out; } - if (ret) - goto out; - /* this time the rename should work ... */ - ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0); + /* + * If we didn't get a negative dentry, make a copy and hash it. + */ + new_target = new_dentry; + if (new_dentry->d_inode) { +printk("umsdos_rename: %s/%s not negative, hash=%d\n", +new_dentry->d_parent->d_name.name, new_dentry->d_name.name, +!list_empty(&new_dentry->d_hash)); + ret = -ENOMEM; + new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name); + if (!new_target) + goto out; + d_add(new_target, NULL); + } + ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0); + if (new_target != new_dentry) + dput(new_target); out: return ret; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 39605218d..10ff145b7 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -19,7 +19,9 @@ #include <asm/uaccess.h> +extern struct dentry *saved_root; extern struct inode *pseudo_root; +extern struct dentry_operations umsdos_dentry_operations; struct RDIR_FILLDIR { void *dirbuf; @@ -63,9 +65,7 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir) bufk.filldir = filldir; bufk.dirbuf = dirbuf; - bufk.real_root = pseudo_root && - dir->i_ino == UMSDOS_ROOT_INO && - dir->i_sb == pseudo_root->i_sb; + bufk.real_root = pseudo_root && (dir == saved_root->d_inode); return fat_readdir (filp, &bufk, rdir_filldir); } @@ -81,45 +81,40 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir) */ int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo) { - /* so locating "linux" will work */ - 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->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; + if (saved_root && dir == saved_root->d_inode && !nopseudo && + dentry->d_name.len == UMSDOS_PSDROOT_LEN && + memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) { + /* #Specification: pseudo root / DOS/linux + * Even in the real root directory (c:\), the directory + * /linux won't show + */ + + ret = -ENOENT; goto out; } - 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); - } - 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); - } + ret = msdos_lookup (dir, dentry); + if (ret) { + printk(KERN_WARNING + "umsdos_rlookup_x: %s/%s failed, ret=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name,ret); + goto out; + } + if (dentry->d_inode) { + /* We must install the proper function table + * depending on whether this is an MS-DOS or + * a UMSDOS directory + */ +Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name)); + umsdos_patch_dentry_inode(dentry, 0); + } out: - PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret)); + /* always install our dentry ops ... */ + dentry->d_op = &umsdos_dentry_operations; return ret; } @@ -160,54 +155,34 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry) if (umsdos_is_pseudodos (dir, dentry)) goto out; - umsdos_lockcreate (dir); ret = -EBUSY; - if (dentry->d_count > 1) { - shrink_dcache_parent(dentry); - if (dentry->d_count > 1) - goto out_unlock; - } + if (!list_empty(&dentry->d_hash)) + goto out; ret = msdos_rmdir (dir, dentry); if (ret != -ENOTEMPTY) - goto out_check; - -#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 + goto out; empty = umsdos_isempty (dentry); if (empty == 1) { - struct dentry *temp; + struct dentry *demd; /* 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)) { + demd = umsdos_get_emd_dentry(dentry); + ret = PTR_ERR(demd); + if (!IS_ERR(demd)) { ret = 0; - if (temp->d_inode) - ret = msdos_unlink (dentry->d_inode, temp); - dput(temp); + if (demd->d_inode) + ret = msdos_unlink (dentry->d_inode, demd); + dput(demd); } - if (ret) - goto out_unlock; } + if (ret) + goto out; + /* 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; } diff --git a/fs/umsdos/specs b/fs/umsdos/specs index 7b88a78e4..0f7d68c0a 100644 --- a/fs/umsdos/specs +++ b/fs/umsdos/specs @@ -147,6 +147,7 @@ * 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 @@ -180,6 +181,7 @@ * 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 diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index dc639a7a4..4b3678a22 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -32,52 +32,26 @@ int umsdos_readlink_x ( struct dentry *dentry, char *buffer, int bufsiz) { - int ret; + size_t size = dentry->d_inode->i_size; loff_t loffs = 0; + ssize_t ret; struct file filp; - ret = dentry->d_inode->i_size; - - if (!(dentry->d_inode)) { - return -EBADF; - } +Printk((KERN_DEBUG "UMSDOS_read: %s/%s, size=%u\n", +dentry->d_parent->d_name.name, dentry->d_name.name, size)); fill_new_filp (&filp, dentry); - filp.f_reada = 0; filp.f_flags = O_RDONLY; - filp.f_op = &umsdos_symlink_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */ - - if (ret > bufsiz) - ret = bufsiz; - - PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs)); - PRINTK ((KERN_DEBUG " f_op=%p\n", filp.f_op)); - 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 " f_pos=%Lu\n", filp.f_pos)); - PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name)); - PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp.f_dentry->d_inode)->i_binary)); - PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags)); - PRINTK ((KERN_DEBUG " f_owner=%d\n", filp.f_owner.uid)); - PRINTK ((KERN_DEBUG " f_version=%ld\n", filp.f_version)); - PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin)); + filp.f_op = &umsdos_symlink_operations; + if (size > bufsiz) + size = bufsiz; - PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs)); - if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) { + ret = fat_file_read (&filp, buffer, size, &loffs); + if (ret != size) { ret = -EIO; } -#if 0 - { - struct umsdos_dirent *mydirent = buffer; - - 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 - - PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret)); return ret; } @@ -85,49 +59,40 @@ int umsdos_readlink_x ( struct dentry *dentry, 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/? 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; - + return umsdos_readlink_x (dentry, buffer, buflen); } /* this one mostly stolen from romfs :) */ -static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *base) +static struct dentry *UMSDOS_followlink (struct dentry *dentry, + struct dentry *base, + unsigned int follow) { struct inode *inode = dentry->d_inode; - char *symname = NULL; + char *symname; int len, cnt; mm_segment_t old_fs = get_fs (); - Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name)); +Printk((KERN_DEBUG "UMSDOS_followlink /mn/: (%s/%s)\n", +dentry->d_parent->d_name.name, dentry->d_name.name)); len = inode->i_size; if (!(symname = kmalloc (len + 1, GFP_KERNEL))) { - dentry = ERR_PTR (-EAGAIN); /* correct? */ + dentry = ERR_PTR (-ENOMEM); goto outnobuf; } + set_fs (KERNEL_DS); /* we read into kernel space this time */ - PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len)); cnt = umsdos_readlink_x (dentry, symname, len); - PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len)); set_fs (old_fs); - Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt)); if (len != cnt) { dentry = ERR_PTR (-EIO); goto out; - } else - symname[len] = 0; + } - dentry = lookup_dentry (symname, base, 1); + symname[len] = 0; + dentry = lookup_dentry (symname, base, follow); kfree (symname); if (0) { @@ -139,7 +104,7 @@ static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *b return dentry; } - +/* needed to patch the file structure */ static struct file_operations umsdos_symlink_operations = { NULL, /* lseek - default */ @@ -158,7 +123,7 @@ static struct file_operations umsdos_symlink_operations = struct inode_operations umsdos_symlink_inode_operations = { - NULL, /* default file operations */ + NULL, /* default file operations (none) */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ @@ -169,14 +134,14 @@ struct inode_operations umsdos_symlink_inode_operations = NULL, /* mknod */ NULL, /* rename */ UMSDOS_readlink, /* readlink */ - UMSDOS_followlink, /* followlink *//* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */ - generic_readpage, /* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */ + UMSDOS_followlink, /* followlink */ + 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 */ NULL, /* updatepage */ NULL /* revalidate */ - }; + diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index fe1565106..bf558a4f7 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -1422,6 +1422,8 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh, if (dir->i_dev != dentry->d_inode->i_dev || dir == dentry->d_inode) { return -EBUSY; } + if (!list_empty(&dentry->d_hash)) + return -EBUSY; res = vfat_empty(dentry->d_inode); if (res) { @@ -1508,18 +1510,16 @@ static void drop_replace_inodes(struct dentry *dentry, struct inode *inode) tmp = next; next = tmp->next; alias = list_entry(tmp, struct dentry, d_alias); + if (alias == dentry) + continue; + if (inode) { list_del(&alias->d_alias); iput(alias->d_inode); d_instantiate(alias, inode); - /* dentry is already accounted for */ - if (alias != dentry) { - inode->i_count++; - } - } - if (alias != dentry) { - d_drop(alias); + inode->i_count++; } + d_drop(alias); } } } @@ -1551,10 +1551,12 @@ int vfat_rmdir(struct inode *dir,struct dentry* dentry) { int res; PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); - res = vfat_rmdirx(dir, dentry); - if (res >= 0) { - drop_replace_inodes(dentry, NULL); - d_delete(dentry); + + res = -EBUSY; + if (list_empty(&dentry->d_hash)) { + res = vfat_rmdirx(dir, dentry); + if (res >= 0) + drop_replace_inodes(dentry, NULL); } return res; } @@ -1846,7 +1848,7 @@ struct inode_operations vfat_dir_inode_operations = { NULL, /* followlink */ NULL, /* readpage */ NULL, /* writepage */ - fat_bmap, /* bmap */ + NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; |