diff options
Diffstat (limited to 'fs')
64 files changed, 677 insertions, 431 deletions
diff --git a/fs/9p/error.c b/fs/9p/error.c index ae91555c1558..0d7fa4e08812 100644 --- a/fs/9p/error.c +++ b/fs/9p/error.c @@ -83,6 +83,7 @@ int v9fs_errstr2errno(char *errstr, int len) if (errno == 0) { /* TODO: if error isn't found, add it dynamically */ + errstr[len] = 0; printk(KERN_ERR "%s: errstr :%s: not found\n", __FUNCTION__, errstr); errno = 1; diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 27507201f9e7..a9b6301a04fc 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -25,6 +25,7 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/idr.h> +#include <asm/semaphore.h> #include "debug.h" #include "v9fs.h" @@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) new->iounit = 0; new->rdir_pos = 0; new->rdir_fcall = NULL; + init_MUTEX(&new->lock); INIT_LIST_HEAD(&new->list); return new; @@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) } /** - * v9fs_fid_lookup - retrieve the right fid from a particular dentry + * v9fs_fid_lookup - return a locked fid from a dentry * @dentry: dentry to look for fid in - * @type: intent of lookup (operation or traversal) * - * find a fid in the dentry + * find a fid in the dentry, obtain its semaphore and return a reference to it. + * code calling lookup is responsible for releasing lock * * TODO: only match fids that have the same uid as current user * @@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) if (!return_fid) { dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); + return_fid = ERR_PTR(-EBADF); } + if(down_interruptible(&return_fid->lock)) + return ERR_PTR(-EINTR); + return return_fid; } + +/** + * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it + * @dentry: dentry to look for fid in + * + * find a fid in the dentry and then clone to a new private fid + * + * TODO: only match fids that have the same uid as current user + * + */ + +struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) +{ + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); + struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); + struct v9fs_fcall *fcall = NULL; + int fid, err; + + base_fid = v9fs_fid_lookup(dentry); + + if(IS_ERR(base_fid)) + return base_fid; + + if(base_fid) { /* clone fid */ + fid = v9fs_get_idpool(&v9ses->fidpool); + if (fid < 0) { + eprintk(KERN_WARNING, "newfid fails!\n"); + new_fid = ERR_PTR(-ENOSPC); + goto Release_Fid; + } + + err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); + if (err < 0) { + dprintk(DEBUG_ERROR, "clone walk didn't work\n"); + v9fs_put_idpool(fid, &v9ses->fidpool); + new_fid = ERR_PTR(err); + goto Free_Fcall; + } + new_fid = v9fs_fid_create(v9ses, fid); + if (new_fid == NULL) { + dprintk(DEBUG_ERROR, "out of memory\n"); + new_fid = ERR_PTR(-ENOMEM); + } +Free_Fcall: + kfree(fcall); + } + +Release_Fid: + up(&base_fid->lock); + return new_fid; +} + +void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) +{ + v9fs_t_clunk(v9ses, fid->fid); + v9fs_fid_destroy(fid); +} diff --git a/fs/9p/fid.h b/fs/9p/fid.h index aa974d6875c3..48fc170c26c8 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h @@ -30,6 +30,8 @@ struct v9fs_fid { struct list_head list; /* list of fids associated with a dentry */ struct list_head active; /* XXX - debug */ + struct semaphore lock; + u32 fid; unsigned char fidopen; /* set when fid is opened */ unsigned char fidclunked; /* set when fid has already been clunked */ @@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *); void v9fs_fid_destroy(struct v9fs_fid *fid); struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); +struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry); +void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid); + diff --git a/fs/9p/mux.c b/fs/9p/mux.c index 944273c3dbff..147ceef8e537 100644 --- a/fs/9p/mux.c +++ b/fs/9p/mux.c @@ -132,8 +132,10 @@ int v9fs_mux_global_init(void) v9fs_mux_poll_tasks[i].task = NULL; v9fs_mux_wq = create_workqueue("v9fs"); - if (!v9fs_mux_wq) + if (!v9fs_mux_wq) { + printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n"); return -ENOMEM; + } return 0; } diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 0b96fae8b479..d9b561ba5e58 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -457,14 +457,19 @@ static int __init init_v9fs(void) v9fs_error_init(); - printk(KERN_INFO "Installing v9fs 9P2000 file system support\n"); + printk(KERN_INFO "Installing v9fs 9p2000 file system support\n"); ret = v9fs_mux_global_init(); - if (!ret) + if (ret) { + printk(KERN_WARNING "v9fs: starting mux failed\n"); return ret; + } ret = register_filesystem(&v9fs_fs_type); - if (!ret) + if (ret) { + printk(KERN_WARNING "v9fs: registering file system failed\n"); v9fs_mux_global_exit(); + } + return ret; } diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index e86a07151280..9f17b0cacdd0 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file) struct v9fs_fid *vfid; struct v9fs_fcall *fcall = NULL; int omode; - int fid = V9FS_NOFID; int err; dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); - vfid = v9fs_fid_lookup(file->f_path.dentry); - if (!vfid) { - dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); - return -EBADF; - } - - fid = v9fs_get_idpool(&v9ses->fidpool); - if (fid < 0) { - eprintk(KERN_WARNING, "newfid fails!\n"); - return -ENOSPC; - } + vfid = v9fs_fid_clone(file->f_path.dentry); + if (IS_ERR(vfid)) + return PTR_ERR(vfid); - err = v9fs_t_walk(v9ses, vfid->fid, fid, NULL, &fcall); - if (err < 0) { - dprintk(DEBUG_ERROR, "rewalk didn't work\n"); - if (fcall && fcall->id == RWALK) - goto clunk_fid; - else { - v9fs_put_idpool(fid, &v9ses->fidpool); - goto free_fcall; - } - } - kfree(fcall); - - /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */ - /* translate open mode appropriately */ omode = v9fs_uflags2omode(file->f_flags); - err = v9fs_t_open(v9ses, fid, omode, &fcall); + err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall); if (err < 0) { PRINT_FCALL_ERROR("open failed", fcall); - goto clunk_fid; - } - - vfid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); - if (vfid == NULL) { - dprintk(DEBUG_ERROR, "out of memory\n"); - err = -ENOMEM; - goto clunk_fid; + goto Clunk_Fid; } file->private_data = vfid; - vfid->fid = fid; vfid->fidopen = 1; vfid->fidclunked = 0; vfid->iounit = fcall->params.ropen.iounit; @@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) return 0; -clunk_fid: - v9fs_t_clunk(v9ses, fid); - -free_fcall: +Clunk_Fid: + v9fs_fid_clunk(v9ses, vfid); kfree(fcall); return err; diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 18f26cdfd882..9109ba1d6969 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -416,12 +416,8 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) sb = file_inode->i_sb; v9ses = v9fs_inode2v9ses(file_inode); v9fid = v9fs_fid_lookup(file); - - if (!v9fid) { - dprintk(DEBUG_ERROR, - "no v9fs_fid\n"); - return -EBADF; - } + if(IS_ERR(v9fid)) + return PTR_ERR(v9fid); fid = v9fid->fid; if (fid < 0) { @@ -433,11 +429,13 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) result = v9fs_t_remove(v9ses, fid, &fcall); if (result < 0) { PRINT_FCALL_ERROR("remove fails", fcall); + goto Error; } v9fs_put_idpool(fid, &v9ses->fidpool); v9fs_fid_destroy(v9fid); +Error: kfree(fcall); return result; } @@ -473,9 +471,13 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, inode = NULL; vfid = NULL; v9ses = v9fs_inode2v9ses(dir); - dfid = v9fs_fid_lookup(dentry->d_parent); - perm = unixmode2p9mode(v9ses, mode); + dfid = v9fs_fid_clone(dentry->d_parent); + if(IS_ERR(dfid)) { + err = PTR_ERR(dfid); + goto error; + } + perm = unixmode2p9mode(v9ses, mode); if (nd && nd->flags & LOOKUP_OPEN) flags = nd->intent.open.flags - 1; else @@ -485,9 +487,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); if (err) - goto error; + goto clunk_dfid; vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); + v9fs_fid_clunk(v9ses, dfid); if (IS_ERR(vfid)) { err = PTR_ERR(vfid); vfid = NULL; @@ -525,6 +528,9 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, return 0; +clunk_dfid: + v9fs_fid_clunk(v9ses, dfid); + error: if (vfid) v9fs_fid_destroy(vfid); @@ -551,7 +557,12 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) inode = NULL; vfid = NULL; v9ses = v9fs_inode2v9ses(dir); - dfid = v9fs_fid_lookup(dentry->d_parent); + dfid = v9fs_fid_clone(dentry->d_parent); + if(IS_ERR(dfid)) { + err = PTR_ERR(dfid); + goto error; + } + perm = unixmode2p9mode(v9ses, mode | S_IFDIR); err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, @@ -559,37 +570,36 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (err) { dprintk(DEBUG_ERROR, "create error %d\n", err); - goto error; - } - - err = v9fs_t_clunk(v9ses, fid); - if (err) { - dprintk(DEBUG_ERROR, "clunk error %d\n", err); - goto error; + goto clean_up_dfid; } vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); if (IS_ERR(vfid)) { err = PTR_ERR(vfid); vfid = NULL; - goto error; + goto clean_up_dfid; } + v9fs_fid_clunk(v9ses, dfid); inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; - goto error; + goto clean_up_fids; } dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; -error: +clean_up_fids: if (vfid) v9fs_fid_destroy(vfid); +clean_up_dfid: + v9fs_fid_clunk(v9ses, dfid); + +error: return err; } @@ -622,28 +632,23 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, dentry->d_op = &v9fs_dentry_operations; dirfid = v9fs_fid_lookup(dentry->d_parent); - if (!dirfid) { - dprintk(DEBUG_ERROR, "no dirfid\n"); - return ERR_PTR(-EINVAL); - } + if(IS_ERR(dirfid)) + return ERR_PTR(PTR_ERR(dirfid)); dirfidnum = dirfid->fid; - if (dirfidnum < 0) { - dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n", - dir, dir->i_ino); - return ERR_PTR(-EBADF); - } - newfid = v9fs_get_idpool(&v9ses->fidpool); if (newfid < 0) { eprintk(KERN_WARNING, "newfid fails!\n"); - return ERR_PTR(-ENOSPC); + result = -ENOSPC; + goto Release_Dirfid; } result = v9fs_t_walk(v9ses, dirfidnum, newfid, (char *)dentry->d_name.name, &fcall); + up(&dirfid->lock); + if (result < 0) { if (fcall && fcall->id == RWALK) v9fs_t_clunk(v9ses, newfid); @@ -701,8 +706,12 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, return NULL; - FreeFcall: +Release_Dirfid: + up(&dirfid->lock); + +FreeFcall: kfree(fcall); + return ERR_PTR(result); } @@ -746,10 +755,8 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *old_inode = old_dentry->d_inode; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); - struct v9fs_fid *olddirfid = - v9fs_fid_lookup(old_dentry->d_parent); - struct v9fs_fid *newdirfid = - v9fs_fid_lookup(new_dentry->d_parent); + struct v9fs_fid *olddirfid; + struct v9fs_fid *newdirfid; struct v9fs_wstat wstat; struct v9fs_fcall *fcall = NULL; int fid = -1; @@ -759,16 +766,26 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, dprintk(DEBUG_VFS, "\n"); - if ((!oldfid) || (!olddirfid) || (!newdirfid)) { - dprintk(DEBUG_ERROR, "problem with arguments\n"); - return -EBADF; + if(IS_ERR(oldfid)) + return PTR_ERR(oldfid); + + olddirfid = v9fs_fid_clone(old_dentry->d_parent); + if(IS_ERR(olddirfid)) { + retval = PTR_ERR(olddirfid); + goto Release_lock; + } + + newdirfid = v9fs_fid_clone(new_dentry->d_parent); + if(IS_ERR(newdirfid)) { + retval = PTR_ERR(newdirfid); + goto Clunk_olddir; } /* 9P can only handle file rename in the same directory */ if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); - retval = -EPERM; - goto FreeFcallnBail; + retval = -EXDEV; + goto Clunk_newdir; } fid = oldfid->fid; @@ -779,7 +796,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", old_inode->i_ino); retval = -EBADF; - goto FreeFcallnBail; + goto Clunk_newdir; } v9fs_blank_wstat(&wstat); @@ -788,11 +805,20 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); - FreeFcallnBail: if (retval < 0) PRINT_FCALL_ERROR("wstat error", fcall); kfree(fcall); + +Clunk_newdir: + v9fs_fid_clunk(v9ses, newdirfid); + +Clunk_olddir: + v9fs_fid_clunk(v9ses, olddirfid); + +Release_lock: + up(&oldfid->lock); + return retval; } @@ -810,15 +836,12 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, { struct v9fs_fcall *fcall = NULL; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); + struct v9fs_fid *fid = v9fs_fid_clone(dentry); int err = -EPERM; dprintk(DEBUG_VFS, "dentry: %p\n", dentry); - if (!fid) { - dprintk(DEBUG_ERROR, - "couldn't find fid associated with dentry\n"); - return -EBADF; - } + if(IS_ERR(fid)) + return PTR_ERR(fid); err = v9fs_t_stat(v9ses, fid->fid, &fcall); @@ -831,6 +854,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, } kfree(fcall); + v9fs_fid_clunk(v9ses, fid); return err; } @@ -844,18 +868,14 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) { struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); + struct v9fs_fid *fid = v9fs_fid_clone(dentry); struct v9fs_fcall *fcall = NULL; struct v9fs_wstat wstat; int res = -EPERM; dprintk(DEBUG_VFS, "\n"); - - if (!fid) { - dprintk(DEBUG_ERROR, - "Couldn't find fid associated with dentry\n"); - return -EBADF; - } + if(IS_ERR(fid)) + return PTR_ERR(fid); v9fs_blank_wstat(&wstat); if (iattr->ia_valid & ATTR_MODE) @@ -887,6 +907,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) if (res >= 0) res = inode_setattr(dentry->d_inode, iattr); + v9fs_fid_clunk(v9ses, fid); return res; } @@ -987,18 +1008,15 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) struct v9fs_fcall *fcall = NULL; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_lookup(dentry); + struct v9fs_fid *fid = v9fs_fid_clone(dentry); - if (!fid) { - dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); - retval = -EBADF; - goto FreeFcall; - } + if(IS_ERR(fid)) + return PTR_ERR(fid); if (!v9ses->extended) { retval = -EBADF; dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeFcall; + goto ClunkFid; } dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); @@ -1009,8 +1027,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) goto FreeFcall; } - if (!fcall) - return -EIO; + if (!fcall) { + retval = -EIO; + goto ClunkFid; + } if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { retval = -EINVAL; @@ -1028,9 +1048,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) fcall->params.rstat.stat.extension.str, buffer); retval = buflen; - FreeFcall: +FreeFcall: kfree(fcall); +ClunkFid: + v9fs_fid_clunk(v9ses, fid); + return retval; } @@ -1123,52 +1146,58 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, int err; u32 fid, perm; struct v9fs_session_info *v9ses; - struct v9fs_fid *dfid, *vfid; - struct inode *inode; + struct v9fs_fid *dfid, *vfid = NULL; + struct inode *inode = NULL; - inode = NULL; - vfid = NULL; v9ses = v9fs_inode2v9ses(dir); - dfid = v9fs_fid_lookup(dentry->d_parent); - perm = unixmode2p9mode(v9ses, mode); - if (!v9ses->extended) { dprintk(DEBUG_ERROR, "not extended\n"); return -EPERM; } + dfid = v9fs_fid_clone(dentry->d_parent); + if(IS_ERR(dfid)) { + err = PTR_ERR(dfid); + goto error; + } + + perm = unixmode2p9mode(v9ses, mode); + err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); if (err) - goto error; + goto clunk_dfid; err = v9fs_t_clunk(v9ses, fid); if (err) - goto error; + goto clunk_dfid; vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); if (IS_ERR(vfid)) { err = PTR_ERR(vfid); vfid = NULL; - goto error; + goto clunk_dfid; } inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; - goto error; + goto free_vfid; } dentry->d_op = &v9fs_dentry_operations; d_instantiate(dentry, inode); return 0; -error: - if (vfid) - v9fs_fid_destroy(vfid); +free_vfid: + v9fs_fid_destroy(vfid); + +clunk_dfid: + v9fs_fid_clunk(v9ses, dfid); +error: return err; } @@ -1209,26 +1238,29 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { int retval; + struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); struct v9fs_fid *oldfid; char *name; dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, old_dentry->d_name.name); - oldfid = v9fs_fid_lookup(old_dentry); - if (!oldfid) { - dprintk(DEBUG_ERROR, "can't find oldfid\n"); - return -EPERM; - } + oldfid = v9fs_fid_clone(old_dentry); + if(IS_ERR(oldfid)) + return PTR_ERR(oldfid); name = __getname(); - if (unlikely(!name)) - return -ENOMEM; + if (unlikely(!name)) { + retval = -ENOMEM; + goto clunk_fid; + } sprintf(name, "%d\n", oldfid->fid); retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); __putname(name); +clunk_fid: + v9fs_fid_clunk(v9ses, oldfid); return retval; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 7cb28720f90e..669dbe5b0317 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -682,6 +682,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) retval = PTR_ERR(interpreter); if (IS_ERR(interpreter)) goto out_free_interp; + + /* + * If the binary is not readable then enforce + * mm->dumpable = 0 regardless of the interpreter's + * permissions. + */ + if (file_permission(interpreter, MAY_READ) < 0) + bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; + retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE); if (retval != BINPRM_BUF_SIZE) { @@ -1178,6 +1187,10 @@ static int dump_seek(struct file *file, loff_t off) */ static int maydump(struct vm_area_struct *vma) { + /* The vma can be set up to tell us the answer directly. */ + if (vma->vm_flags & VM_ALWAYSDUMP) + return 1; + /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; @@ -1424,6 +1437,32 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) return sz; } +static struct vm_area_struct *first_vma(struct task_struct *tsk, + struct vm_area_struct *gate_vma) +{ + struct vm_area_struct *ret = tsk->mm->mmap; + + if (ret) + return ret; + return gate_vma; +} +/* + * Helper function for iterating across a vma list. It ensures that the caller + * will visit `gate_vma' prior to terminating the search. + */ +static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, + struct vm_area_struct *gate_vma) +{ + struct vm_area_struct *ret; + + ret = this_vma->vm_next; + if (ret) + return ret; + if (this_vma == gate_vma) + return NULL; + return gate_vma; +} + /* * Actual dumper * @@ -1439,7 +1478,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) int segs; size_t size = 0; int i; - struct vm_area_struct *vma; + struct vm_area_struct *vma, *gate_vma; struct elfhdr *elf = NULL; loff_t offset = 0, dataoff, foffset; unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; @@ -1525,6 +1564,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) segs += ELF_CORE_EXTRA_PHDRS; #endif + gate_vma = get_gate_vma(current); + if (gate_vma != NULL) + segs++; + /* Set up header */ fill_elf_header(elf, segs + 1); /* including notes section */ @@ -1592,7 +1635,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); /* Write program headers for segments dump */ - for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { + for (vma = first_vma(current, gate_vma); vma != NULL; + vma = next_vma(vma, gate_vma)) { struct elf_phdr phdr; size_t sz; @@ -1641,7 +1685,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) /* Align to page */ DUMP_SEEK(dataoff - foffset); - for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { + for (vma = first_vma(current, gate_vma); vma != NULL; + vma = next_vma(vma, gate_vma)) { unsigned long addr; if (!maydump(vma)) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 6e6d4568d548..a4d933a51208 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -234,6 +234,14 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, goto error; } + /* + * If the binary is not readable then enforce + * mm->dumpable = 0 regardless of the interpreter's + * permissions. + */ + if (file_permission(interpreter, MAY_READ) < 0) + bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; + retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE); if (retval < 0) diff --git a/fs/block_dev.c b/fs/block_dev.c index 8b18e43b82fe..d9bdf2b3ade2 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -146,7 +146,7 @@ static int blk_end_aio(struct bio *bio, unsigned int bytes_done, int error) iocb->ki_nbytes = -EIO; if (atomic_dec_and_test(bio_count)) { - if (iocb->ki_nbytes < 0) + if ((long)iocb->ki_nbytes < 0) aio_complete(iocb, iocb->ki_nbytes, 0); else aio_complete(iocb, iocb->ki_left, 0); @@ -190,6 +190,12 @@ static struct page *blk_get_page(unsigned long addr, size_t count, int rw, return pvec->page[pvec->idx++]; } +/* return a page back to pvec array */ +static void blk_unget_page(struct page *page, struct pvec *pvec) +{ + pvec->page[--pvec->idx] = page; +} + static ssize_t blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) @@ -278,6 +284,8 @@ same_bio: count = min(count, nbytes); goto same_bio; } + } else { + blk_unget_page(page, &pvec); } /* bio is ready, submit it */ diff --git a/fs/buffer.c b/fs/buffer.c index 3b116078b4c3..1ad674fd348c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page) int ret = 0; BUG_ON(!PageLocked(page)); - if (PageDirty(page) || PageWriteback(page)) + if (PageWriteback(page)) return 0; if (mapping == NULL) { /* can this still happen? */ @@ -2844,6 +2844,23 @@ int try_to_free_buffers(struct page *page) spin_lock(&mapping->private_lock); ret = drop_buffers(page, &buffers_to_free); + + /* + * If the filesystem writes its buffers by hand (eg ext3) + * then we can have clean buffers against a dirty page. We + * clean the page here; otherwise the VM will never notice + * that the filesystem did any IO at all. + * + * Also, during truncate, discard_buffer will have marked all + * the page's buffers clean. We discover that here and clean + * the page also. + * + * private_lock must be held over this entire operation in order + * to synchronise against __set_page_dirty_buffers and prevent the + * dirty bit from being lost. + */ + if (ret) + cancel_dirty_page(page, PAGE_CACHE_SIZE); spin_unlock(&mapping->private_lock); out: if (buffers_to_free) { diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3539d6ef9611..d04d2f7448d9 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,7 @@ +Version 1.47 +------------ +Fix oops in list_del during mount caused by unaligned string. + Version 1.46 ------------ Support deep tree mounts. Better support OS/2, Win9x (DOS) time stamps. diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 96abeb738978..6017c465440e 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -143,8 +143,8 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); if((ses->serverDomain == NULL) || (ses->serverOS == NULL) || (ses->serverNOS == NULL)) { - buf += sprintf("\nentry for %s not fully displayed\n\t", - ses->serverName); + buf += sprintf(buf, "\nentry for %s not fully " + "displayed\n\t", ses->serverName); } else { length = diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index a243f779b363..8aa66dcf13bd 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -100,5 +100,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); extern int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.46" +#define CIFS_VERSION "1.47" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index aedf683f011f..19cc294c7c70 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -71,9 +71,7 @@ sesInfoAlloc(void) { struct cifsSesInfo *ret_buf; - ret_buf = - (struct cifsSesInfo *) kzalloc(sizeof (struct cifsSesInfo), - GFP_KERNEL); + ret_buf = kzalloc(sizeof (struct cifsSesInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); @@ -109,9 +107,7 @@ struct cifsTconInfo * tconInfoAlloc(void) { struct cifsTconInfo *ret_buf; - ret_buf = - (struct cifsTconInfo *) kzalloc(sizeof (struct cifsTconInfo), - GFP_KERNEL); + ret_buf = kzalloc(sizeof (struct cifsTconInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index bbdda99dce61..758464630893 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -182,11 +182,14 @@ static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInf cFYI(1,("bleft %d",bleft)); - /* word align, if bytes remaining is not even */ - if(bleft % 2) { - bleft--; - data++; - } + /* SMB header is unaligned, so cifs servers word align start of + Unicode strings */ + data++; + bleft--; /* Windows servers do not always double null terminate + their final Unicode string - in which case we + now will not attempt to decode the byte of junk + which follows it */ + words_left = bleft / 2; /* save off server operating system */ diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index c403b66ec83c..a4b142a6a2c7 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -251,8 +251,19 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) WARN_ON(inode->i_state & I_WILL_FREE); if ((wbc->sync_mode != WB_SYNC_ALL) && (inode->i_state & I_LOCK)) { + struct address_space *mapping = inode->i_mapping; + int ret; + list_move(&inode->i_list, &inode->i_sb->s_dirty); - return 0; + + /* + * Even if we don't actually write the inode itself here, + * we can at least start some of the data writeout.. + */ + spin_unlock(&inode_lock); + ret = do_writepages(mapping, wbc); + spin_lock(&inode_lock); + return ret; } /* diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 8c58bd453993..1794305f9ed8 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -193,8 +193,12 @@ static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, static void fuse_ctl_kill_sb(struct super_block *sb) { + struct fuse_conn *fc; + mutex_lock(&fuse_mutex); fuse_control_sb = NULL; + list_for_each_entry(fc, &fuse_conn_list, entry) + fc->ctl_ndents = 0; mutex_unlock(&fuse_mutex); kill_litter_super(sb); diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index cca3fb693f99..70543b17e4c7 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -76,7 +76,7 @@ extern int make_symlink(const char *from, const char *to); extern int unlink_file(const char *file); extern int do_mkdir(const char *file, int mode); extern int do_rmdir(const char *file); -extern int do_mknod(const char *file, int mode, int dev); +extern int do_mknod(const char *file, int mode, unsigned int major, unsigned int minor); extern int link_file(const char *from, const char *to); extern int do_readlink(char *file, char *buf, int size); extern int rename_file(char *from, char *to); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 1e6fc3799876..69a376f35a68 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -755,7 +755,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) goto out_put; init_special_inode(inode, mode, dev); - err = do_mknod(name, mode, dev); + err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); if(err) goto out_free; diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 23b7cee72123..1ed5ea389f15 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -295,11 +295,11 @@ int do_rmdir(const char *file) return(0); } -int do_mknod(const char *file, int mode, int dev) +int do_mknod(const char *file, int mode, unsigned int major, unsigned int minor) { int err; - err = mknod(file, mode, dev); + err = mknod(file, mode, makedev(major, minor)); if(err) return(-errno); return(0); } diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c index 077258b2103e..5a95fbdd6fdb 100644 --- a/fs/jffs/jffs_fm.c +++ b/fs/jffs/jffs_fm.c @@ -17,6 +17,7 @@ * */ #include <linux/slab.h> +#include <linux/err.h> #include <linux/blkdev.h> #include <linux/jffs.h> #include "jffs_fm.h" @@ -104,7 +105,7 @@ jffs_build_begin(struct jffs_control *c, int unit) mtd = get_mtd_device(NULL, unit); - if (!mtd) { + if (IS_ERR(mtd)) { kfree(fmc); DJM(no_jffs_fmcontrol--); return NULL; diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 72b4fc13a106..4189e4a36050 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -178,8 +178,8 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, while (ref2) { uint32_t totlen = ref_totlen(c, jeb, ref2); - if (ref2->flash_offset < jeb->offset || - ref2->flash_offset > jeb->offset + c->sector_size) { + if (ref_offset(ref2) < jeb->offset || + ref_offset(ref2) > jeb->offset + c->sector_size) { JFFS2_ERROR("node_ref %#08x shouldn't be in block at %#08x.\n", ref_offset(ref2), jeb->offset); goto error; diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 3daf3bca0376..f89c85d5a3f8 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -13,6 +13,7 @@ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ +#include <linux/sched.h> #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 0 diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 7bc1a4201c0c..abb90c0c09cc 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -502,12 +502,11 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) if (ret) return ret; - c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); + c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL); if (!c->inocache_list) { ret = -ENOMEM; goto out_wbuf; } - memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); jffs2_init_xattr_subsystem(c); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index daff3341ff92..3a3cf225981f 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -838,6 +838,8 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { + cond_resched(); + /* We only care about obsolete ones */ if (!(ref_obsolete(raw))) continue; diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 0ddfd70307fb..4178b4b55948 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -294,23 +294,21 @@ static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) static inline struct jffs2_node_frag *frag_first(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_first(root); if (!node) return NULL; - while(node->rb_left) - node = node->rb_left; + return rb_entry(node, struct jffs2_node_frag, rb); } static inline struct jffs2_node_frag *frag_last(struct rb_root *root) { - struct rb_node *node = root->rb_node; + struct rb_node *node = rb_last(root); if (!node) return NULL; - while(node->rb_right) - node = node->rb_right; + return rb_entry(node, struct jffs2_node_frag, rb); } diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 266423b2709d..58a0b912e9d0 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -944,13 +944,12 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { struct jffs2_raw_inode n; - struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); + struct jffs2_inode_info *f = kzalloc(sizeof(*f), GFP_KERNEL); int ret; if (!f) return -ENOMEM; - memset(f, 0, sizeof(*f)); init_MUTEX_LOCKED(&f->sem); f->inocache = ic; diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index e2413466ddd5..3af746eaff0e 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -128,17 +128,19 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) } if (jffs2_sum_active()) { - s = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); + s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!s) { + kfree(flashbuf); JFFS2_WARNING("Can't allocate memory for summary\n"); return -ENOMEM; } - memset(s, 0, sizeof(struct jffs2_summary)); } for (i=0; i<c->nr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; + cond_resched(); + /* reset summary info for next eraseblock scan */ jffs2_sum_reset_collected(s); diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index e52cef526d90..25265965bdc1 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -26,15 +26,13 @@ int jffs2_sum_init(struct jffs2_sb_info *c) { - c->summary = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); + c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!c->summary) { JFFS2_WARNING("Can't allocate memory for summary information!\n"); return -ENOMEM; } - memset(c->summary, 0, sizeof(struct jffs2_summary)); - c->summary->sum_buf = vmalloc(c->sector_size); if (!c->summary->sum_buf) { @@ -398,6 +396,8 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras for (i=0; i<je32_to_cpu(summary->sum_num); i++) { dbg_summary("processing summary index %d\n", i); + cond_resched(); + /* Make sure there's a spare ref for dirty space */ err = jffs2_prealloc_raw_node_refs(c, jeb, 2); if (err) diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 7deb78254021..08a0e6c49e61 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/fs.h> +#include <linux/err.h> #include <linux/mount.h> #include <linux/jffs2.h> #include <linux/pagemap.h> @@ -184,9 +185,9 @@ static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type, struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); - if (!mtd) { + if (IS_ERR(mtd)) { D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); - return -EINVAL; + return PTR_ERR(mtd); } return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); @@ -221,7 +222,7 @@ static int jffs2_get_sb(struct file_system_type *fs_type, D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); - if (mtd) { + if (!IS_ERR(mtd)) { if (!strcmp(mtd->name, dev_name+4)) return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); put_mtd_device(mtd); diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index fc211b6e9b03..b90d5aa3d969 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -51,7 +51,7 @@ static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) */ if (!p) { - printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n"); + printk(KERN_ERR "jffs2_follow_link(): can't find symlink target\n"); p = ERR_PTR(-EIO); } D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->target)); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 70707309dfa1..9c99859f5edd 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -969,8 +969,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, int oobsize = c->mtd->oobsize; struct mtd_oob_ops ops; - ops.len = NR_OOB_SCAN_PAGES * oobsize; - ops.ooblen = oobsize; + ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; ops.oobbuf = c->oobbuf; ops.ooboffs = 0; ops.datbuf = NULL; @@ -983,10 +982,10 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, return ret; } - if (ops.retlen < ops.len) { + if (ops.oobretlen < ops.ooblen) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " "returned short read (%zd bytes not %d) for block " - "at %08x\n", ops.retlen, ops.len, jeb->offset)); + "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); return -EIO; } @@ -1005,7 +1004,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, } /* we know, we are aligned :) */ - for (page = oobsize; page < ops.len; page += sizeof(long)) { + for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { long dat = *(long *)(&ops.oobbuf[page]); if(dat != -1) return 1; @@ -1033,7 +1032,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, return 2; } - ops.len = oobsize; ops.ooblen = oobsize; ops.oobbuf = c->oobbuf; ops.ooboffs = 0; @@ -1048,10 +1046,10 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, return ret; } - if (ops.retlen < ops.len) { + if (ops.oobretlen < ops.ooblen) { D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " "Read OOB return short read (%zd bytes not %d) " - "for block at %08x\n", ops.retlen, ops.len, + "for block at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); return -EIO; } @@ -1090,8 +1088,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); - ops.len = c->fsdata_len; - ops.ooblen = c->fsdata_len;; + ops.ooblen = c->fsdata_len; ops.oobbuf = (uint8_t *)&n; ops.ooboffs = c->fsdata_pos; ops.datbuf = NULL; @@ -1105,10 +1102,10 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, jeb->offset, ret)); return ret; } - if (ops.retlen != ops.len) { + if (ops.oobretlen != ops.ooblen) { D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " "Short write for block at %08x: %zd not %d\n", - jeb->offset, ops.retlen, ops.len)); + jeb->offset, ops.oobretlen, ops.ooblen)); return -EIO; } return 0; diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 4da09ce1d1f5..4bb3f1897330 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -399,8 +399,6 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu { /* must be called under down_write(xattr_sem) */ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { - uint32_t xid = xd->xid, version = xd->version; - unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; if (xd->node == (void *)xd) { @@ -411,7 +409,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu } spin_unlock(&c->erase_completion_lock); - dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version); + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", + xd->xid, xd->version); } } diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 062707745162..f4d45d4d835b 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -176,7 +176,7 @@ reclaimer(void *ptr) lock_kernel(); lockd_up(0); /* note: this cannot fail as lockd is already running */ - dprintk("lockd: reclaiming locks for host %s", host->h_name); + dprintk("lockd: reclaiming locks for host %s\n", host->h_name); restart: nsmstate = host->h_nsmstate; @@ -206,7 +206,7 @@ restart: host->h_reclaiming = 0; up_write(&host->h_rwsem); - dprintk("NLM: done reclaiming locks for host %s", host->h_name); + dprintk("NLM: done reclaiming locks for host %s\n", host->h_name); /* Now, wake up all processes that sleep on a blocked lock */ list_for_each_entry(block, &nlm_blocked, b_list) { diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index dee3d6c0f194..d9ba8cb0ee75 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -532,7 +532,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) lock_kernel(); - res = nfs_revalidate_mapping(inode, filp->f_mapping); + res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping); if (res < 0) { unlock_kernel(); return res; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index fab20d06d936..9e4a2b70995a 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -434,8 +434,9 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl) BUG(); } if (res < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); + dprintk(KERN_WARNING "%s: VFS is out of sync with lock manager" + " - error %d!\n", + __FUNCTION__, res); return res; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 63e470279309..d83498282837 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -665,49 +665,86 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) return __nfs_revalidate_inode(server, inode); } +static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (mapping->nrpages != 0) { + int ret = invalidate_inode_pages2(mapping); + if (ret < 0) + return ret; + } + spin_lock(&inode->i_lock); + nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; + if (S_ISDIR(inode->i_mode)) { + memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); + /* This ensures we revalidate child dentries */ + nfsi->cache_change_attribute = jiffies; + } + spin_unlock(&inode->i_lock); + nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); + dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", + inode->i_sb->s_id, (long long)NFS_FILEID(inode)); + return 0; +} + +static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) +{ + int ret = 0; + + mutex_lock(&inode->i_mutex); + if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_DATA) { + ret = nfs_sync_mapping(mapping); + if (ret == 0) + ret = nfs_invalidate_mapping_nolock(inode, mapping); + } + mutex_unlock(&inode->i_mutex); + return ret; +} + /** - * nfs_revalidate_mapping - Revalidate the pagecache + * nfs_revalidate_mapping_nolock - Revalidate the pagecache * @inode - pointer to host inode * @mapping - pointer to mapping */ -int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) +int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) { struct nfs_inode *nfsi = NFS_I(inode); int ret = 0; - if (NFS_STALE(inode)) - ret = -ESTALE; if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) - || nfs_attribute_timeout(inode)) + || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); - if (ret < 0) - goto out; + if (ret < 0) + goto out; + } + if (nfsi->cache_validity & NFS_INO_INVALID_DATA) + ret = nfs_invalidate_mapping_nolock(inode, mapping); +out: + return ret; +} - if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { - if (mapping->nrpages != 0) { - if (S_ISREG(inode->i_mode)) { - ret = nfs_sync_mapping(mapping); - if (ret < 0) - goto out; - } - ret = invalidate_inode_pages2(mapping); - if (ret < 0) - goto out; - } - spin_lock(&inode->i_lock); - nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; - if (S_ISDIR(inode->i_mode)) { - memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); - /* This ensures we revalidate child dentries */ - nfsi->cache_change_attribute = jiffies; - } - spin_unlock(&inode->i_lock); +/** + * nfs_revalidate_mapping - Revalidate the pagecache + * @inode - pointer to host inode + * @mapping - pointer to mapping + * + * This version of the function will take the inode->i_mutex and attempt to + * flush out all dirty data if it needs to invalidate the page cache. + */ +int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) +{ + struct nfs_inode *nfsi = NFS_I(inode); + int ret = 0; - nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); - dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", - inode->i_sb->s_id, - (long long)NFS_FILEID(inode)); + if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) + || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { + ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); + if (ret < 0) + goto out; } + if (nfsi->cache_validity & NFS_INO_INVALID_DATA) + ret = nfs_invalidate_mapping(inode, mapping); out: return ret; } diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 6c686112cc03..525c136c7d8c 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -50,7 +50,9 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; struct page *page; - void *err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); + void *err; + + err = ERR_PTR(nfs_revalidate_mapping_nolock(inode, inode->i_mapping)); if (err) goto read_failed; page = read_cache_page(&inode->i_data, 0, diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 248dd92e6a56..49c310b84923 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -35,7 +35,6 @@ #include <linux/lockd/bind.h> #define NFSDDBG_FACILITY NFSDDBG_EXPORT -#define NFSD_PARANOIA 1 typedef struct auth_domain svc_client; typedef struct svc_export svc_export; diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 277df40f098d..e695660921ec 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -990,15 +990,16 @@ encode_entry(struct readdir_cd *ccd, const char *name, } int -nfs3svc_encode_entry(struct readdir_cd *cd, const char *name, - int namlen, loff_t offset, ino_t ino, unsigned int d_type) +nfs3svc_encode_entry(void *cd, const char *name, + int namlen, loff_t offset, u64 ino, unsigned int d_type) { return encode_entry(cd, name, namlen, offset, ino, d_type, 0); } int -nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name, - int namlen, loff_t offset, ino_t ino, unsigned int d_type) +nfs3svc_encode_entry_plus(void *cd, const char *name, + int namlen, loff_t offset, u64 ino, + unsigned int d_type) { return encode_entry(cd, name, namlen, offset, ino, d_type, 1); } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index fea46368afb2..18aa9440df14 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1880,9 +1880,10 @@ nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) } static int -nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen, - loff_t offset, ino_t ino, unsigned int d_type) +nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) { + struct readdir_cd *ccd = ccdv; struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); int buflen; __be32 *p = cd->buffer; diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index b06bf9f70efc..98338a569dc0 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -24,8 +24,6 @@ #include <linux/nfsd/nfsd.h> #define NFSDDBG_FACILITY NFSDDBG_FH -#define NFSD_PARANOIA 1 -/* #define NFSD_DEBUG_VERBOSE 1 */ static int nfsd_nr_verified; @@ -230,13 +228,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) error = nfserrno(PTR_ERR(dentry)); goto out; } -#ifdef NFSD_PARANOIA + if (S_ISDIR(dentry->d_inode->i_mode) && (dentry->d_flags & DCACHE_DISCONNECTED)) { printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); } -#endif fhp->fh_dentry = dentry; fhp->fh_export = exp; @@ -267,12 +264,13 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) /* Finally, check access permissions. */ error = nfsd_permission(exp, dentry, access); -#ifdef NFSD_PARANOIA_EXTREME 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 >> 24)); + dprintk("fh_verify: %s/%s permission failure, " + "acc=%x, error=%d\n", + dentry->d_parent->d_name.name, + dentry->d_name.name, + access, (error >> 24)); } -#endif out: if (exp && !IS_ERR(exp)) exp_put(exp); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 0aaccb03bf76..fbf5d51947ea 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -72,7 +72,7 @@ static struct svc_program nfsd_acl_program = { .pg_prog = NFS_ACL_PROGRAM, .pg_nvers = NFSD_ACL_NRVERS, .pg_vers = nfsd_acl_versions, - .pg_name = "nfsd", + .pg_name = "nfsacl", .pg_class = "nfsd", .pg_stats = &nfsd_acl_svcstats, .pg_authenticate = &svc_set_client, @@ -118,16 +118,16 @@ int nfsd_vers(int vers, enum vers_op change) switch(change) { case NFSD_SET: nfsd_versions[vers] = nfsd_version[vers]; - break; #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) if (vers < NFSD_ACL_NRVERS) - nfsd_acl_version[vers] = nfsd_acl_version[vers]; + nfsd_acl_versions[vers] = nfsd_acl_version[vers]; #endif + break; case NFSD_CLEAR: nfsd_versions[vers] = NULL; #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) if (vers < NFSD_ACL_NRVERS) - nfsd_acl_version[vers] = NULL; + nfsd_acl_versions[vers] = NULL; #endif break; case NFSD_TEST: diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index f5243f943996..6555c50d9006 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -462,9 +462,10 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p, } int -nfssvc_encode_entry(struct readdir_cd *ccd, const char *name, - int namlen, loff_t offset, ino_t ino, unsigned int d_type) +nfssvc_encode_entry(void *ccdv, const char *name, + int namlen, loff_t offset, u64 ino, unsigned int d_type) { + struct readdir_cd *ccd = ccdv; struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common); __be32 *p = cd->buffer; int buflen, slen; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 7a79c23aa6d4..8283236c6a0f 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -59,7 +59,6 @@ #include <asm/uaccess.h> #define NFSDDBG_FACILITY NFSDDBG_FILEOP -#define NFSD_PARANOIA /* We must ignore files (but only files) which might have mandatory @@ -822,7 +821,8 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset rqstp->rq_res.page_len = size; } else if (page != pp[-1]) { get_page(page); - put_page(*pp); + if (*pp) + put_page(*pp); *pp = page; rqstp->rq_resused++; rqstp->rq_res.page_len += size; @@ -1244,7 +1244,6 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 err; int host_err; __u32 v_mtime=0, v_atime=0; - int v_mode=0; err = nfserr_perm; if (!flen) @@ -1281,16 +1280,11 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (createmode == NFS3_CREATE_EXCLUSIVE) { - /* while the verifier would fit in mtime+atime, - * solaris7 gets confused (bugid 4218508) if these have - * the high bit set, so we use the mode as well + /* solaris7 gets confused (bugid 4218508) if these have + * the high bit set, so just clear the high bits. */ v_mtime = verifier[0]&0x7fffffff; v_atime = verifier[1]&0x7fffffff; - v_mode = S_IFREG - | ((verifier[0]&0x80000000) >> (32-7)) /* u+x */ - | ((verifier[1]&0x80000000) >> (32-9)) /* u+r */ - ; } if (dchild->d_inode) { @@ -1318,7 +1312,6 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, case NFS3_CREATE_EXCLUSIVE: if ( dchild->d_inode->i_mtime.tv_sec == v_mtime && dchild->d_inode->i_atime.tv_sec == v_atime - && dchild->d_inode->i_mode == v_mode && dchild->d_inode->i_size == 0 ) break; /* fallthru */ @@ -1340,26 +1333,22 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, } if (createmode == NFS3_CREATE_EXCLUSIVE) { - /* Cram the verifier into atime/mtime/mode */ + /* Cram the verifier into atime/mtime */ iap->ia_valid = ATTR_MTIME|ATTR_ATIME - | ATTR_MTIME_SET|ATTR_ATIME_SET - | ATTR_MODE; + | ATTR_MTIME_SET|ATTR_ATIME_SET; /* XXX someone who knows this better please fix it for nsec */ iap->ia_mtime.tv_sec = v_mtime; iap->ia_atime.tv_sec = v_atime; iap->ia_mtime.tv_nsec = 0; iap->ia_atime.tv_nsec = 0; - iap->ia_mode = v_mode; } /* Set file attributes. - * Mode has already been set but we might need to reset it - * for CREATE_EXCLUSIVE * Irix appears to send along the gid when it tries to * implement setgid directories via NFS. Clear out all that cruft. */ set_attr: - if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0) { + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) { __be32 err2 = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); if (err2) err = err2; @@ -1726,7 +1715,7 @@ out: */ __be32 nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, - struct readdir_cd *cdp, encode_dent_fn func) + struct readdir_cd *cdp, filldir_t func) { __be32 err; int host_err; @@ -1751,7 +1740,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, do { cdp->err = nfserr_eof; /* will be cleared on successful read */ - host_err = vfs_readdir(file, (filldir_t) func, cdp); + host_err = vfs_readdir(file, func, cdp); } while (host_err >=0 && cdp->err == nfs_ok); if (host_err) err = nfserrno(host_err); diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 35cc4b1d60f7..af4ef808fa94 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog @@ -17,6 +17,13 @@ ToDo/Notes: happen is unclear however so it is worth waiting until someone hits the problem. +2.1.28 - Fix a deadlock. + + - Fix deadlock in fs/ntfs/inode.c::ntfs_put_inode(). Thanks to Sergey + Vlasov for the report and detailed analysis of the deadlock. The fix + involved getting rid of ntfs_put_inode() altogether and hence NTFS no + longer has a ->put_inode super operation. + 2.1.27 - Various bug fixes and cleanups. - Fix two compiler warnings on Alpha. Thanks to Andrew Morton for diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index e27b4eacffbf..825508385565 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ unistr.o upcase.o -EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.27\" +EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.28\" ifeq ($(CONFIG_NTFS_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 7b2c8f4f6a6f..629e7abdd840 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -92,10 +92,12 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ofs = 0; if (file_ofs < init_size) ofs = init_size - file_ofs; + local_irq_save(flags); kaddr = kmap_atomic(page, KM_BIO_SRC_IRQ); memset(kaddr + bh_offset(bh) + ofs, 0, bh->b_size - ofs); kunmap_atomic(kaddr, KM_BIO_SRC_IRQ); + local_irq_restore(flags); flush_dcache_page(page); } } else { @@ -143,11 +145,13 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) recs = PAGE_CACHE_SIZE / rec_size; /* Should have been verified before we got here... */ BUG_ON(!recs); + local_irq_save(flags); kaddr = kmap_atomic(page, KM_BIO_SRC_IRQ); for (i = 0; i < recs; i++) post_read_mst_fixup((NTFS_RECORD*)(kaddr + i * rec_size), rec_size); kunmap_atomic(kaddr, KM_BIO_SRC_IRQ); + local_irq_restore(flags); flush_dcache_page(page); if (likely(page_uptodate && !PageError(page))) SetPageUptodate(page); diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 8296c29ae3b8..74f99a6a369b 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1,7 +1,7 @@ /** * dir.c - NTFS kernel directory operations. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -1249,16 +1249,12 @@ skip_index_root: /* Get the offset into the index allocation attribute. */ ia_pos = (s64)fpos - vol->mft_record_size; ia_mapping = vdir->i_mapping; - bmp_vi = ndir->itype.index.bmp_ino; - if (unlikely(!bmp_vi)) { - ntfs_debug("Inode 0x%lx, regetting index bitmap.", vdir->i_ino); - bmp_vi = ntfs_attr_iget(vdir, AT_BITMAP, I30, 4); - if (IS_ERR(bmp_vi)) { - ntfs_error(sb, "Failed to get bitmap attribute."); - err = PTR_ERR(bmp_vi); - goto err_out; - } - ndir->itype.index.bmp_ino = bmp_vi; + ntfs_debug("Inode 0x%lx, getting index bitmap.", vdir->i_ino); + bmp_vi = ntfs_attr_iget(vdir, AT_BITMAP, I30, 4); + if (IS_ERR(bmp_vi)) { + ntfs_error(sb, "Failed to get bitmap attribute."); + err = PTR_ERR(bmp_vi); + goto err_out; } bmp_mapping = bmp_vi->i_mapping; /* Get the starting bitmap bit position and sanity check it. */ @@ -1266,7 +1262,7 @@ skip_index_root: if (unlikely(bmp_pos >> 3 >= i_size_read(bmp_vi))) { ntfs_error(sb, "Current index allocation position exceeds " "index bitmap size."); - goto err_out; + goto iput_err_out; } /* Get the starting bit position in the current bitmap page. */ cur_bmp_pos = bmp_pos & ((PAGE_CACHE_SIZE * 8) - 1); @@ -1282,7 +1278,7 @@ get_next_bmp_page: ntfs_error(sb, "Reading index bitmap failed."); err = PTR_ERR(bmp_page); bmp_page = NULL; - goto err_out; + goto iput_err_out; } bmp = (u8*)page_address(bmp_page); /* Find next index block in use. */ @@ -1429,6 +1425,7 @@ find_next_index_buffer: /* @ia_page is already unlocked in this case. */ ntfs_unmap_page(ia_page); ntfs_unmap_page(bmp_page); + iput(bmp_vi); goto abort; } } @@ -1439,6 +1436,7 @@ unm_EOD: ntfs_unmap_page(ia_page); } ntfs_unmap_page(bmp_page); + iput(bmp_vi); EOD: /* We are finished, set fpos to EOD. */ fpos = i_size + vol->mft_record_size; @@ -1455,8 +1453,11 @@ done: filp->f_pos = fpos; return 0; err_out: - if (bmp_page) + if (bmp_page) { ntfs_unmap_page(bmp_page); +iput_err_out: + iput(bmp_vi); + } if (ia_page) { unlock_page(ia_page); ntfs_unmap_page(ia_page); @@ -1529,14 +1530,22 @@ static int ntfs_dir_open(struct inode *vi, struct file *filp) static int ntfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync) { - struct inode *vi = dentry->d_inode; - ntfs_inode *ni = NTFS_I(vi); + struct inode *bmp_vi, *vi = dentry->d_inode; int err, ret; + ntfs_attr na; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(!S_ISDIR(vi->i_mode)); - if (NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) - write_inode_now(ni->itype.index.bmp_ino, !datasync); + /* If the bitmap attribute inode is in memory sync it, too. */ + na.mft_no = vi->i_ino; + na.type = AT_BITMAP; + na.name = I30; + na.name_len = 4; + bmp_vi = ilookup5(vi->i_sb, vi->i_ino, (test_t)ntfs_test_inode, &na); + if (bmp_vi) { + write_inode_now(bmp_vi, !datasync); + iput(bmp_vi); + } ret = ntfs_write_inode(vi, 1); write_inode_now(vi, !datasync); err = sync_blockdev(vi->i_sb->s_bdev); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 247989891b4b..f8bf8da67ee8 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1,7 +1,7 @@ /** * inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -95,7 +95,7 @@ int ntfs_test_inode(struct inode *vi, ntfs_attr *na) * If initializing the normal file/directory inode, set @na->type to AT_UNUSED. * In that case, @na->name and @na->name_len should be set to NULL and 0, * respectively. Although that is not strictly necessary as - * ntfs_read_inode_locked() will fill them in later. + * ntfs_read_locked_inode() will fill them in later. * * Return 0 on success and -errno on error. * @@ -171,8 +171,8 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no) { struct inode *vi; - ntfs_attr na; int err; + ntfs_attr na; na.mft_no = mft_no; na.type = AT_UNUSED; @@ -229,8 +229,8 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, ntfschar *name, u32 name_len) { struct inode *vi; - ntfs_attr na; int err; + ntfs_attr na; /* Make sure no one calls ntfs_attr_iget() for indices. */ BUG_ON(type == AT_INDEX_ALLOCATION); @@ -287,8 +287,8 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, u32 name_len) { struct inode *vi; - ntfs_attr na; int err; + ntfs_attr na; na.mft_no = base_vi->i_ino; na.type = AT_INDEX_ALLOCATION; @@ -402,7 +402,6 @@ void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni) ntfs_init_runlist(&ni->attr_list_rl); lockdep_set_class(&ni->attr_list_rl.lock, &attr_list_rl_lock_class); - ni->itype.index.bmp_ino = NULL; ni->itype.index.block_size = 0; ni->itype.index.vcn_size = 0; ni->itype.index.collation_rule = 0; @@ -546,6 +545,7 @@ static int ntfs_read_locked_inode(struct inode *vi) { ntfs_volume *vol = NTFS_SB(vi->i_sb); ntfs_inode *ni; + struct inode *bvi; MFT_RECORD *m; ATTR_RECORD *a; STANDARD_INFORMATION *si; @@ -780,7 +780,6 @@ skip_attr_list_load: */ if (S_ISDIR(vi->i_mode)) { loff_t bvi_size; - struct inode *bvi; ntfs_inode *bni; INDEX_ROOT *ir; u8 *ir_end, *index_end; @@ -985,13 +984,12 @@ skip_attr_list_load: err = PTR_ERR(bvi); goto unm_err_out; } - ni->itype.index.bmp_ino = bvi; bni = NTFS_I(bvi); if (NInoCompressed(bni) || NInoEncrypted(bni) || NInoSparse(bni)) { ntfs_error(vi->i_sb, "$BITMAP attribute is compressed " "and/or encrypted and/or sparse."); - goto unm_err_out; + goto iput_unm_err_out; } /* Consistency check bitmap size vs. index allocation size. */ bvi_size = i_size_read(bvi); @@ -1000,8 +998,10 @@ skip_attr_list_load: ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) " "for index allocation (0x%llx).", bvi_size << 3, vi->i_size); - goto unm_err_out; + goto iput_unm_err_out; } + /* No longer need the bitmap attribute inode. */ + iput(bvi); skip_large_dir_stuff: /* Setup the operations for this inode. */ vi->i_op = &ntfs_dir_inode_ops; @@ -1176,7 +1176,8 @@ no_data_attr_special_case: vi->i_blocks = ni->allocated_size >> 9; ntfs_debug("Done."); return 0; - +iput_unm_err_out: + iput(bvi); unm_err_out: if (!err) err = -EIO; @@ -1697,7 +1698,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) vi->i_size); goto iput_unm_err_out; } - ni->itype.index.bmp_ino = bvi; + iput(bvi); skip_large_index_stuff: /* Setup the operations for this index inode. */ vi->i_op = NULL; @@ -1714,7 +1715,6 @@ skip_large_index_stuff: ntfs_debug("Done."); return 0; - iput_unm_err_out: iput(bvi); unm_err_out: @@ -2191,37 +2191,6 @@ err_out: return -1; } -/** - * ntfs_put_inode - handler for when the inode reference count is decremented - * @vi: vfs inode - * - * The VFS calls ntfs_put_inode() every time the inode reference count (i_count) - * is about to be decremented (but before the decrement itself. - * - * If the inode @vi is a directory with two references, one of which is being - * dropped, we need to put the attribute inode for the directory index bitmap, - * if it is present, otherwise the directory inode would remain pinned for - * ever. - */ -void ntfs_put_inode(struct inode *vi) -{ - if (S_ISDIR(vi->i_mode) && atomic_read(&vi->i_count) == 2) { - ntfs_inode *ni = NTFS_I(vi); - if (NInoIndexAllocPresent(ni)) { - struct inode *bvi = NULL; - mutex_lock(&vi->i_mutex); - if (atomic_read(&vi->i_count) == 2) { - bvi = ni->itype.index.bmp_ino; - if (bvi) - ni->itype.index.bmp_ino = NULL; - } - mutex_unlock(&vi->i_mutex); - if (bvi) - iput(bvi); - } - } -} - static void __ntfs_clear_inode(ntfs_inode *ni) { /* Free all alocated memory. */ @@ -2287,18 +2256,6 @@ void ntfs_clear_big_inode(struct inode *vi) { ntfs_inode *ni = NTFS_I(vi); - /* - * If the inode @vi is an index inode we need to put the attribute - * inode for the index bitmap, if it is present, otherwise the index - * inode would disappear and the attribute inode for the index bitmap - * would no longer be referenced from anywhere and thus it would remain - * pinned for ever. - */ - if (NInoAttr(ni) && (ni->type == AT_INDEX_ALLOCATION) && - NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) { - iput(ni->itype.index.bmp_ino); - ni->itype.index.bmp_ino = NULL; - } #ifdef NTFS_RW if (NInoDirty(ni)) { bool was_bad = (is_bad_inode(vi)); diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index f088291e017c..117eaf8032a3 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -2,7 +2,7 @@ * inode.h - Defines for inode structures NTFS Linux kernel driver. Part of * the Linux-NTFS project. * - * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -101,8 +101,6 @@ struct _ntfs_inode { runlist attr_list_rl; /* Run list for the attribute list value. */ union { struct { /* It is a directory, $MFT, or an index inode. */ - struct inode *bmp_ino; /* Attribute inode for the - index $BITMAP. */ u32 block_size; /* Size of an index block. */ u32 vcn_size; /* Size of a vcn in this index. */ @@ -300,8 +298,6 @@ extern void ntfs_clear_extent_inode(ntfs_inode *ni); extern int ntfs_read_inode_mount(struct inode *vi); -extern void ntfs_put_inode(struct inode *vi); - extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt); #ifdef NTFS_RW diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 03a391ac7145..babf94d90def 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1,7 +1,7 @@ /* * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (c) 2001,2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -2702,9 +2702,6 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) static struct super_operations ntfs_sops = { .alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */ .destroy_inode = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */ - .put_inode = ntfs_put_inode, /* VFS: Called just before - the inode reference count - is decreased. */ #ifdef NTFS_RW //.dirty_inode = NULL, /* VFS: Called from // __mark_inode_dirty(). */ @@ -3261,7 +3258,7 @@ static void __exit exit_ntfs_fs(void) } MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>"); -MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2006 Anton Altaparmakov"); +MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2007 Anton Altaparmakov"); MODULE_VERSION(NTFS_VERSION); MODULE_LICENSE("GPL"); #ifdef DEBUG diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index 06be6e774cf9..56e1fefc1205 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -60,14 +60,11 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, void *vobjp) inode = ocfs2_iget(OCFS2_SB(sb), handle->ih_blkno, 0); - if (IS_ERR(inode)) { - mlog_errno(PTR_ERR(inode)); + if (IS_ERR(inode)) return (void *)inode; - } if (handle->ih_generation != inode->i_generation) { iput(inode); - mlog_errno(-ESTALE); return ERR_PTR(-ESTALE); } diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index e4d91493d7d7..28ab56f2b98c 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -146,7 +146,6 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, int flags) if (is_bad_inode(inode)) { iput(inode); inode = ERR_PTR(-ESTALE); - mlog_errno(PTR_ERR(inode)); goto bail; } @@ -155,8 +154,7 @@ bail: mlog(0, "returning inode with number %llu\n", (unsigned long long)OCFS2_I(inode)->ip_blkno); mlog_exit_ptr(inode); - } else - mlog_errno(PTR_ERR(inode)); + } return inode; } @@ -247,7 +245,7 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, * today. change if needed. */ if (!OCFS2_IS_VALID_DINODE(fe) || !(fe->i_flags & cpu_to_le32(OCFS2_VALID_FL))) { - mlog(ML_ERROR, "Invalid dinode: i_ino=%lu, i_blkno=%llu, " + mlog(0, "Invalid dinode: i_ino=%lu, i_blkno=%llu, " "signature = %.*s, flags = 0x%x\n", inode->i_ino, (unsigned long long)le64_to_cpu(fe->i_blkno), 7, @@ -478,11 +476,8 @@ static int ocfs2_read_locked_inode(struct inode *inode, S_ISBLK(le16_to_cpu(fe->i_mode))) inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); - if (ocfs2_populate_inode(inode, fe, 0) < 0) { - mlog(ML_ERROR, "populate failed! i_blkno=%llu, i_ino=%lu\n", - (unsigned long long)fe->i_blkno, inode->i_ino); + if (ocfs2_populate_inode(inode, fe, 0) < 0) goto bail; - } BUG_ON(args->fi_blkno != le64_to_cpu(fe->i_blkno)); diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 9637039c2633..f3d7803b4b46 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -932,14 +932,15 @@ static int ocfs2_unlink(struct inode *dir, goto leave; } - if (S_ISDIR(inode->i_mode)) { + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + if (S_ISDIR(inode->i_mode)) drop_nlink(dir); - status = ocfs2_mark_inode_dirty(handle, dir, - parent_node_bh); - if (status < 0) { - mlog_errno(status); + + status = ocfs2_mark_inode_dirty(handle, dir, parent_node_bh); + if (status < 0) { + mlog_errno(status); + if (S_ISDIR(inode->i_mode)) inc_nlink(dir); - } } leave: @@ -1068,6 +1069,7 @@ static int ocfs2_rename(struct inode *old_dir, char orphan_name[OCFS2_ORPHAN_NAMELEN + 1]; struct buffer_head *orphan_entry_bh = NULL; struct buffer_head *newfe_bh = NULL; + struct buffer_head *old_inode_bh = NULL; struct buffer_head *insert_entry_bh = NULL; struct ocfs2_super *osb = NULL; u64 newfe_blkno; @@ -1079,7 +1081,7 @@ static int ocfs2_rename(struct inode *old_dir, struct buffer_head *new_de_bh = NULL, *old_de_bh = NULL; // bhs for above struct buffer_head *old_inode_de_bh = NULL; // if old_dentry is a dir, // this is the 1st dirent bh - nlink_t old_dir_nlink = old_dir->i_nlink, new_dir_nlink = new_dir->i_nlink; + nlink_t old_dir_nlink = old_dir->i_nlink; /* At some point it might be nice to break this function up a * bit. */ @@ -1139,12 +1141,11 @@ static int ocfs2_rename(struct inode *old_dir, } /* - * Though we don't require an inode meta data update if - * old_inode is not a directory, we lock anyway here to ensure - * the vote thread on other nodes won't have to concurrently - * downconvert the inode and the dentry locks. + * Aside from allowing a meta data update, the locking here + * also ensures that the vote thread on other nodes won't have + * to concurrently downconvert the inode and the dentry locks. */ - status = ocfs2_meta_lock(old_inode, NULL, 1); + status = ocfs2_meta_lock(old_inode, &old_inode_bh, 1); if (status < 0) { if (status != -ENOENT) mlog_errno(status); @@ -1355,6 +1356,7 @@ static int ocfs2_rename(struct inode *old_dir, old_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(old_inode); + ocfs2_mark_inode_dirty(handle, old_inode, old_inode_bh); /* now that the name has been added to new_dir, remove the old name */ status = ocfs2_delete_entry(handle, old_dir, old_de, old_de_bh); @@ -1384,27 +1386,22 @@ static int ocfs2_rename(struct inode *old_dir, } } mark_inode_dirty(old_dir); - if (new_inode) + ocfs2_mark_inode_dirty(handle, old_dir, old_dir_bh); + if (new_inode) { mark_inode_dirty(new_inode); + ocfs2_mark_inode_dirty(handle, new_inode, newfe_bh); + } - if (old_dir != new_dir) - if (new_dir_nlink != new_dir->i_nlink) { - if (!new_dir_bh) { - mlog(ML_ERROR, "need to change nlink for new " - "dir %llu from %d to %d but bh is NULL\n", - (unsigned long long)OCFS2_I(new_dir)->ip_blkno, - (int)new_dir_nlink, new_dir->i_nlink); - } else { - struct ocfs2_dinode *fe; - status = ocfs2_journal_access(handle, - new_dir, - new_dir_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - fe = (struct ocfs2_dinode *) new_dir_bh->b_data; - fe->i_links_count = cpu_to_le16(new_dir->i_nlink); - status = ocfs2_journal_dirty(handle, new_dir_bh); - } - } + if (old_dir != new_dir) { + /* Keep the same times on both directories.*/ + new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime; + + /* + * This will also pick up the i_nlink change from the + * block above. + */ + ocfs2_mark_inode_dirty(handle, new_dir, new_dir_bh); + } if (old_dir_nlink != old_dir->i_nlink) { if (!old_dir_bh) { @@ -1455,6 +1452,8 @@ bail: iput(new_inode); if (newfe_bh) brelse(newfe_bh); + if (old_inode_bh) + brelse(old_inode_bh); if (old_dir_bh) brelse(old_dir_bh); if (new_dir_bh) @@ -1826,6 +1825,13 @@ static int __ocfs2_add_entry(handle_t *handle, (le16_to_cpu(de->rec_len) >= rec_len)) || (le16_to_cpu(de->rec_len) >= (OCFS2_DIR_REC_LEN(de->name_len) + rec_len))) { + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + retval = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh); + if (retval < 0) { + mlog_errno(retval); + goto bail; + } + status = ocfs2_journal_access(handle, dir, insert_bh, OCFS2_JOURNAL_ACCESS_WRITE); /* By now the buffer is marked for journaling */ @@ -1848,7 +1854,6 @@ static int __ocfs2_add_entry(handle_t *handle, de->name_len = namelen; memcpy(de->name, name, namelen); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_version++; status = ocfs2_journal_dirty(handle, insert_bh); retval = 0; diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index b5c68567077e..e61e218f5e0b 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -85,7 +85,7 @@ #define OCFS2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_incompat &= ~(mask) -#define OCFS2_FEATURE_COMPAT_SUPP 0 +#define OCFS2_FEATURE_COMPAT_SUPP OCFS2_FEATURE_COMPAT_BACKUP_SB #define OCFS2_FEATURE_INCOMPAT_SUPP OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT #define OCFS2_FEATURE_RO_COMPAT_SUPP 0 @@ -110,6 +110,20 @@ #define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC 0x0010 /* + * backup superblock flag is used to indicate that this volume + * has backup superblocks. + */ +#define OCFS2_FEATURE_COMPAT_BACKUP_SB 0x0001 + +/* The byte offset of the first backup block will be 1G. + * The following will be 4G, 16G, 64G, 256G and 1T. + */ +#define OCFS2_BACKUP_SB_START 1 << 30 + +/* the max backup superblock nums */ +#define OCFS2_MAX_BACKUP_SUPERBLOCKS 6 + +/* * Flags on ocfs2_dinode.i_flags */ #define OCFS2_VALID_FL (0x00000001) /* Inode is valid */ @@ -566,6 +580,20 @@ static inline int ocfs2_truncate_recs_per_inode(struct super_block *sb) return size / sizeof(struct ocfs2_truncate_rec); } + +static inline u64 ocfs2_backup_super_blkno(struct super_block *sb, int index) +{ + u64 offset = OCFS2_BACKUP_SB_START; + + if (index >= 0 && index < OCFS2_MAX_BACKUP_SUPERBLOCKS) { + offset <<= (2 * index); + offset >>= sb->s_blocksize_bits; + return offset; + } + + return 0; + +} #else static inline int ocfs2_fast_symlink_chars(int blocksize) { @@ -631,6 +659,19 @@ static inline int ocfs2_truncate_recs_per_inode(int blocksize) return size / sizeof(struct ocfs2_truncate_rec); } + +static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index) +{ + uint64_t offset = OCFS2_BACKUP_SB_START; + + if (index >= 0 && index < OCFS2_MAX_BACKUP_SUPERBLOCKS) { + offset <<= (2 * index); + offset /= blocksize; + return offset; + } + + return 0; +} #endif /* __KERNEL__ */ diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index 957d6878b03e..03b0191534d5 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -158,8 +158,7 @@ static void *ocfs2_follow_link(struct dentry *dentry, } status = vfs_follow_link(nd, link); - if (status && status != -ENOENT) - mlog_errno(status); + bail: if (page) { kunmap(page); diff --git a/fs/proc/base.c b/fs/proc/base.c index 77a57b5799c4..ff7a66850602 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -371,9 +371,11 @@ static int mounts_open(struct inode *inode, struct file *file) if (task) { task_lock(task); - ns = task->nsproxy->mnt_ns; - if (ns) - get_mnt_ns(ns); + if (task->nsproxy) { + ns = task->nsproxy->mnt_ns; + if (ns) + get_mnt_ns(ns); + } task_unlock(task); put_task_struct(task); } diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 99b6f329ba23..5109f1d5e7ff 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -48,6 +48,11 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp) } mutex_lock(&inode->i_mutex); + + mutex_lock(&(REISERFS_I(inode)->i_mmap)); + if (REISERFS_I(inode)->i_flags & i_ever_mapped) + REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask; + reiserfs_write_lock(inode->i_sb); /* freeing preallocation only involves relogging blocks that * are already in the current transaction. preallocation gets @@ -100,11 +105,24 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp) err = reiserfs_truncate_file(inode, 0); } out: + mutex_unlock(&(REISERFS_I(inode)->i_mmap)); mutex_unlock(&inode->i_mutex); reiserfs_write_unlock(inode->i_sb); return err; } +static int reiserfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode; + + inode = file->f_path.dentry->d_inode; + mutex_lock(&(REISERFS_I(inode)->i_mmap)); + REISERFS_I(inode)->i_flags |= i_ever_mapped; + mutex_unlock(&(REISERFS_I(inode)->i_mmap)); + + return generic_file_mmap(file, vma); +} + static void reiserfs_vfs_truncate_file(struct inode *inode) { reiserfs_truncate_file(inode, 1); @@ -1527,7 +1545,7 @@ const struct file_operations reiserfs_file_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = reiserfs_compat_ioctl, #endif - .mmap = generic_file_mmap, + .mmap = reiserfs_file_mmap, .open = generic_file_open, .release = reiserfs_file_release, .fsync = reiserfs_sync_file, diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index f3d1c4a77979..9fcbfe316977 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1125,6 +1125,7 @@ static void init_inode(struct inode *inode, struct treepath *path) REISERFS_I(inode)->i_prealloc_count = 0; REISERFS_I(inode)->i_trans_id = 0; REISERFS_I(inode)->i_jl = NULL; + mutex_init(&(REISERFS_I(inode)->i_mmap)); reiserfs_init_acl_access(inode); reiserfs_init_acl_default(inode); reiserfs_init_xattr_rwsem(inode); @@ -1832,6 +1833,7 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th, REISERFS_I(inode)->i_attrs = REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK; sd_attrs_to_i_attrs(REISERFS_I(inode)->i_attrs, inode); + mutex_init(&(REISERFS_I(inode)->i_mmap)); reiserfs_init_acl_access(inode); reiserfs_init_acl_default(inode); reiserfs_init_xattr_rwsem(inode); diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 2e0021e8f366..638f4c585e89 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -227,24 +227,27 @@ failed: * We can come here from ufs_writepage or ufs_prepare_write, * locked_page is argument of these functions, so we already lock it. */ -static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk, +static void ufs_change_blocknr(struct inode *inode, unsigned int beg, unsigned int count, unsigned int oldb, unsigned int newb, struct page *locked_page) { - unsigned int blk_per_page = 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits); - struct address_space *mapping = inode->i_mapping; - pgoff_t index, cur_index = locked_page->index; - unsigned int i, j; + const unsigned mask = (1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1; + struct address_space * const mapping = inode->i_mapping; + pgoff_t index, cur_index; + unsigned end, pos, j; struct page *page; struct buffer_head *head, *bh; UFSD("ENTER, ino %lu, count %u, oldb %u, newb %u\n", inode->i_ino, count, oldb, newb); + BUG_ON(!locked_page); BUG_ON(!PageLocked(locked_page)); - for (i = 0; i < count; i += blk_per_page) { - index = (baseblk+i) >> (PAGE_CACHE_SHIFT - inode->i_blkbits); + cur_index = locked_page->index; + + for (end = count + beg; beg < end; beg = (beg | mask) + 1) { + index = beg >> (PAGE_CACHE_SHIFT - inode->i_blkbits); if (likely(cur_index != index)) { page = ufs_get_locked_page(mapping, index); @@ -253,21 +256,32 @@ static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk, } else page = locked_page; - j = i; head = page_buffers(page); bh = head; + pos = beg & mask; + for (j = 0; j < pos; ++j) + bh = bh->b_this_page; + j = 0; do { - if (likely(bh->b_blocknr == j + oldb && j < count)) { - unmap_underlying_metadata(bh->b_bdev, - bh->b_blocknr); - bh->b_blocknr = newb + j++; - mark_buffer_dirty(bh); + if (buffer_mapped(bh)) { + pos = bh->b_blocknr - oldb; + if (pos < count) { + UFSD(" change from %llu to %llu\n", + (unsigned long long)pos + oldb, + (unsigned long long)pos + newb); + bh->b_blocknr = newb + pos; + unmap_underlying_metadata(bh->b_bdev, + bh->b_blocknr); + mark_buffer_dirty(bh); + ++j; + } } bh = bh->b_this_page; } while (bh != head); - set_page_dirty(page); + if (j) + set_page_dirty(page); if (likely(cur_index != index)) ufs_put_locked_page(page); @@ -415,14 +429,14 @@ unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment, } result = ufs_alloc_fragments (inode, cgno, goal, request, err); if (result) { + ufs_clear_frags(inode, result + oldcount, newcount - oldcount, + locked_page != NULL); ufs_change_blocknr(inode, fragment - oldcount, oldcount, tmp, result, locked_page); *p = cpu_to_fs32(sb, result); *err = 0; UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); - ufs_clear_frags(inode, result + oldcount, newcount - oldcount, - locked_page != NULL); unlock_super(sb); if (newcount < request) ufs_free_fragments (inode, result + newcount, request - newcount); diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 2fbab0aab688..4295ca91cf85 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -242,7 +242,8 @@ repeat: goal = tmp + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, fragment - blockoff, goal, required + blockoff, - err, locked_page); + err, + phys != NULL ? locked_page : NULL); } /* * We will extend last allocated block @@ -250,7 +251,7 @@ repeat: else if (lastblock == block) { tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff), fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff), - err, locked_page); + err, phys != NULL ? locked_page : NULL); } else /* (lastblock > block) */ { /* * We will allocate new block before last allocated block @@ -261,7 +262,8 @@ repeat: goal = tmp + uspi->s_fpb; } tmp = ufs_new_fragments(inode, p, fragment - blockoff, - goal, uspi->s_fpb, err, locked_page); + goal, uspi->s_fpb, err, + phys != NULL ? locked_page : NULL); } if (!tmp) { if ((!blockoff && *p) || @@ -438,9 +440,11 @@ int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buffer_head * it much more readable: */ #define GET_INODE_DATABLOCK(x) \ - ufs_inode_getfrag(inode, x, fragment, 1, &err, &phys, &new, bh_result->b_page) + ufs_inode_getfrag(inode, x, fragment, 1, &err, &phys, &new,\ + bh_result->b_page) #define GET_INODE_PTR(x) \ - ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, NULL, NULL, NULL) + ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, NULL, NULL,\ + bh_result->b_page) #define GET_INDIRECT_DATABLOCK(x) \ ufs_inode_getblock(inode, bh, x, fragment, \ &err, &phys, &new, bh_result->b_page) diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index ea11d04c41a0..0437b0a6fe97 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -109,10 +109,10 @@ static int ufs_trunc_direct (struct inode * inode) tmp = fs32_to_cpu(sb, *p); if (!tmp ) ufs_panic (sb, "ufs_trunc_direct", "internal error"); + frag2 -= frag1; frag1 = ufs_fragnum (frag1); - frag2 = ufs_fragnum (frag2); - ufs_free_fragments (inode, tmp + frag1, frag2 - frag1); + ufs_free_fragments(inode, tmp + frag1, frag2); mark_inode_dirty(inode); frag_to_free = tmp + frag1; |