diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/binfmt_flat.c | 6 | ||||
-rw-r--r-- | fs/cifs/CHANGES | 3 | ||||
-rw-r--r-- | fs/cifs/file.c | 2 | ||||
-rw-r--r-- | fs/namei.c | 153 | ||||
-rw-r--r-- | fs/nfs/dir.c | 49 |
5 files changed, 128 insertions, 85 deletions
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index f0cd67d9d31..c8998dc6688 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -520,7 +520,7 @@ static int load_flat_file(struct linux_binprm * bprm, DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n"); down_write(¤t->mm->mmap_sem); - textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, 0, 0); + textpos = do_mmap(bprm->file, 0, text_len, PROT_READ|PROT_EXEC, MAP_SHARED, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) @@ -532,7 +532,7 @@ static int load_flat_file(struct linux_binprm * bprm, down_write(¤t->mm->mmap_sem); realdatastart = do_mmap(0, 0, data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), - PROT_READ|PROT_WRITE|PROT_EXEC, 0, 0); + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); if (realdatastart == 0 || realdatastart >= (unsigned long)-4096) { @@ -574,7 +574,7 @@ static int load_flat_file(struct linux_binprm * bprm, down_write(¤t->mm->mmap_sem); textpos = do_mmap(0, 0, text_len + data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), - PROT_READ | PROT_EXEC | PROT_WRITE, 0, 0); + PROT_READ | PROT_EXEC | PROT_WRITE, MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); if (!textpos || textpos >= (unsigned long) -4096) { if (!textpos) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 95483baab70..dab4774ee7b 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -6,7 +6,8 @@ kills the cifsd thread (NB: killing the cifs kernel threads is not recommended, unmount and rmmod cifs will kill them when they are no longer needed). Fix readdir to ASCII servers (ie older servers which do not support Unicode) and also require asterik. - +Fix out of memory case in which data could be written one page +off in the page cache. Version 1.33 ------------ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ca74c1151be..0cec0e76d85 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1365,6 +1365,8 @@ static void cifs_copy_cache_pages(struct address_space *mapping, GFP_KERNEL)) { page_cache_release(page); cFYI(1, ("Add page cache failed")); + data += PAGE_CACHE_SIZE; + bytes_read -= PAGE_CACHE_SIZE; continue; } diff --git a/fs/namei.c b/fs/namei.c index dd78f01b6de..a7f7f44119b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -493,12 +493,21 @@ fail: return PTR_ERR(link); } -static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) +struct path { + struct vfsmount *mnt; + struct dentry *dentry; +}; + +static inline int __do_follow_link(struct path *path, struct nameidata *nd) { int error; + struct dentry *dentry = path->dentry; - touch_atime(nd->mnt, dentry); + touch_atime(path->mnt, dentry); nd_set_link(nd, NULL); + + if (path->mnt == nd->mnt) + mntget(path->mnt); error = dentry->d_inode->i_op->follow_link(dentry, nd); if (!error) { char *s = nd_get_link(nd); @@ -507,6 +516,8 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) if (dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, nd); } + dput(dentry); + mntput(path->mnt); return error; } @@ -518,7 +529,7 @@ static inline int __do_follow_link(struct dentry *dentry, struct nameidata *nd) * Without that kind of total limit, nasty chains of consecutive * symlinks can cause almost arbitrarily long lookups. */ -static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) +static inline int do_follow_link(struct path *path, struct nameidata *nd) { int err = -ELOOP; if (current->link_count >= MAX_NESTED_LINKS) @@ -527,17 +538,20 @@ static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) goto loop; BUG_ON(nd->depth >= MAX_NESTED_LINKS); cond_resched(); - err = security_inode_follow_link(dentry, nd); + err = security_inode_follow_link(path->dentry, nd); if (err) goto loop; current->link_count++; current->total_link_count++; nd->depth++; - err = __do_follow_link(dentry, nd); + err = __do_follow_link(path, nd); current->link_count--; nd->depth--; return err; loop: + dput(path->dentry); + if (path->mnt != nd->mnt) + mntput(path->mnt); path_release(nd); return err; } @@ -565,87 +579,91 @@ int follow_up(struct vfsmount **mnt, struct dentry **dentry) /* no need for dcache_lock, as serialization is taken care in * namespace.c */ -static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) +static int __follow_mount(struct path *path) { int res = 0; + while (d_mountpoint(path->dentry)) { + struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry); + if (!mounted) + break; + dput(path->dentry); + if (res) + mntput(path->mnt); + path->mnt = mounted; + path->dentry = dget(mounted->mnt_root); + res = 1; + } + return res; +} + +static void follow_mount(struct vfsmount **mnt, struct dentry **dentry) +{ while (d_mountpoint(*dentry)) { struct vfsmount *mounted = lookup_mnt(*mnt, *dentry); if (!mounted) break; + dput(*dentry); mntput(*mnt); *mnt = mounted; - dput(*dentry); *dentry = dget(mounted->mnt_root); - res = 1; } - return res; } /* no need for dcache_lock, as serialization is taken care in * namespace.c */ -static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) +int follow_down(struct vfsmount **mnt, struct dentry **dentry) { struct vfsmount *mounted; mounted = lookup_mnt(*mnt, *dentry); if (mounted) { + dput(*dentry); mntput(*mnt); *mnt = mounted; - dput(*dentry); *dentry = dget(mounted->mnt_root); return 1; } return 0; } -int follow_down(struct vfsmount **mnt, struct dentry **dentry) -{ - return __follow_down(mnt,dentry); -} - -static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) +static inline void follow_dotdot(struct nameidata *nd) { while(1) { struct vfsmount *parent; - struct dentry *old = *dentry; + struct dentry *old = nd->dentry; read_lock(¤t->fs->lock); - if (*dentry == current->fs->root && - *mnt == current->fs->rootmnt) { + if (nd->dentry == current->fs->root && + nd->mnt == current->fs->rootmnt) { read_unlock(¤t->fs->lock); break; } read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); - if (*dentry != (*mnt)->mnt_root) { - *dentry = dget((*dentry)->d_parent); + if (nd->dentry != nd->mnt->mnt_root) { + nd->dentry = dget(nd->dentry->d_parent); spin_unlock(&dcache_lock); dput(old); break; } spin_unlock(&dcache_lock); spin_lock(&vfsmount_lock); - parent = (*mnt)->mnt_parent; - if (parent == *mnt) { + parent = nd->mnt->mnt_parent; + if (parent == nd->mnt) { spin_unlock(&vfsmount_lock); break; } mntget(parent); - *dentry = dget((*mnt)->mnt_mountpoint); + nd->dentry = dget(nd->mnt->mnt_mountpoint); spin_unlock(&vfsmount_lock); dput(old); - mntput(*mnt); - *mnt = parent; + mntput(nd->mnt); + nd->mnt = parent; } - follow_mount(mnt, dentry); + follow_mount(&nd->mnt, &nd->dentry); } -struct path { - struct vfsmount *mnt; - struct dentry *dentry; -}; - /* * It's more convoluted than I'd like it to be, but... it's still fairly * small and for now I'd prefer to have fast path as straight as possible. @@ -664,6 +682,7 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, done: path->mnt = mnt; path->dentry = dentry; + __follow_mount(path); return 0; need_lookup: @@ -751,7 +770,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) case 2: if (this.name[1] != '.') break; - follow_dotdot(&nd->mnt, &nd->dentry); + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -771,8 +790,6 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) err = do_lookup(nd, &this, &next); if (err) break; - /* Check mountpoints.. */ - follow_mount(&next.mnt, &next.dentry); err = -ENOENT; inode = next.dentry->d_inode; @@ -783,10 +800,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) goto out_dput; if (inode->i_op->follow_link) { - mntget(next.mnt); - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); + err = do_follow_link(&next, nd); if (err) goto return_err; err = -ENOENT; @@ -798,6 +812,8 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) break; } else { dput(nd->dentry); + if (nd->mnt != next.mnt) + mntput(nd->mnt); nd->mnt = next.mnt; nd->dentry = next.dentry; } @@ -819,7 +835,7 @@ last_component: case 2: if (this.name[1] != '.') break; - follow_dotdot(&nd->mnt, &nd->dentry); + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -833,19 +849,17 @@ last_component: err = do_lookup(nd, &this, &next); if (err) break; - follow_mount(&next.mnt, &next.dentry); inode = next.dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { - mntget(next.mnt); - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); + err = do_follow_link(&next, nd); if (err) goto return_err; inode = nd->dentry->d_inode; } else { dput(nd->dentry); + if (nd->mnt != next.mnt) + mntput(nd->mnt); nd->mnt = next.mnt; nd->dentry = next.dentry; } @@ -885,6 +899,8 @@ return_base: return 0; out_dput: dput(next.dentry); + if (nd->mnt != next.mnt) + mntput(next.mnt); break; } path_release(nd); @@ -1398,7 +1414,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) { int acc_mode, error = 0; - struct dentry *dentry; + struct path path; struct dentry *dir; int count = 0; @@ -1442,23 +1458,24 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) dir = nd->dentry; nd->flags &= ~LOOKUP_PARENT; down(&dir->d_inode->i_sem); - dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.mnt = nd->mnt; do_last: - error = PTR_ERR(dentry); - if (IS_ERR(dentry)) { + error = PTR_ERR(path.dentry); + if (IS_ERR(path.dentry)) { up(&dir->d_inode->i_sem); goto exit; } /* Negative dentry, just create the file */ - if (!dentry->d_inode) { + if (!path.dentry->d_inode) { if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; - error = vfs_create(dir->d_inode, dentry, mode, nd); + error = vfs_create(dir->d_inode, path.dentry, mode, nd); up(&dir->d_inode->i_sem); dput(nd->dentry); - nd->dentry = dentry; + nd->dentry = path.dentry; if (error) goto exit; /* Don't check for write permission, don't truncate */ @@ -1476,22 +1493,24 @@ do_last: if (flag & O_EXCL) goto exit_dput; - if (d_mountpoint(dentry)) { + if (__follow_mount(&path)) { error = -ELOOP; if (flag & O_NOFOLLOW) goto exit_dput; - while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry)); } error = -ENOENT; - if (!dentry->d_inode) + if (!path.dentry->d_inode) goto exit_dput; - if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) + if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link) goto do_link; dput(nd->dentry); - nd->dentry = dentry; + nd->dentry = path.dentry; + if (nd->mnt != path.mnt) + mntput(nd->mnt); + nd->mnt = path.mnt; error = -EISDIR; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) + if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode)) goto exit; ok: error = may_open(nd, acc_mode, flag); @@ -1500,7 +1519,9 @@ ok: return 0; exit_dput: - dput(dentry); + dput(path.dentry); + if (nd->mnt != path.mnt) + mntput(path.mnt); exit: path_release(nd); return error; @@ -1520,18 +1541,15 @@ do_link: * are done. Procfs-like symlinks just set LAST_BIND. */ nd->flags |= LOOKUP_PARENT; - error = security_inode_follow_link(dentry, nd); + error = security_inode_follow_link(path.dentry, nd); if (error) goto exit_dput; - error = __do_follow_link(dentry, nd); - dput(dentry); + error = __do_follow_link(&path, nd); if (error) return error; nd->flags &= ~LOOKUP_PARENT; - if (nd->last_type == LAST_BIND) { - dentry = nd->dentry; + if (nd->last_type == LAST_BIND) goto ok; - } error = -EISDIR; if (nd->last_type != LAST_NORM) goto exit; @@ -1546,7 +1564,8 @@ do_link: } dir = nd->dentry; down(&dir->d_inode->i_sem); - dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.dentry = __lookup_hash(&nd->last, nd->dentry, nd); + path.mnt = nd->mnt; putname(nd->last.name); goto do_last; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 73f96acd5d3..ff6155f5e8d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -528,19 +528,39 @@ static inline void nfs_renew_times(struct dentry * dentry) dentry->d_time = jiffies; } +/* + * Return the intent data that applies to this particular path component + * + * Note that the current set of intents only apply to the very last + * component of the path. + * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. + */ +static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask) +{ + if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) + return 0; + return nd->flags & mask; +} + +/* + * Inode and filehandle revalidation for lookups. + * + * We force revalidation in the cases where the VFS sets LOOKUP_REVAL, + * or if the intent information indicates that we're about to open this + * particular file and the "nocto" mount flag is not set. + * + */ static inline int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) { struct nfs_server *server = NFS_SERVER(inode); if (nd != NULL) { - int ndflags = nd->flags; /* VFS wants an on-the-wire revalidation */ - if (ndflags & LOOKUP_REVAL) + if (nd->flags & LOOKUP_REVAL) goto out_force; /* This is an open(2) */ - if ((ndflags & LOOKUP_OPEN) && - !(ndflags & LOOKUP_CONTINUE) && + if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && !(server->flags & NFS_MOUNT_NOCTO)) goto out_force; } @@ -560,12 +580,8 @@ static inline int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { - int ndflags = 0; - - if (nd) - ndflags = nd->flags; /* Don't revalidate a negative dentry if we're creating a new file */ - if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE)) + if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0) return 0; return !nfs_check_verifier(dir, dentry); } @@ -700,12 +716,16 @@ struct dentry_operations nfs_dentry_operations = { .d_iput = nfs_dentry_iput, }; +/* + * Use intent information to check whether or not we're going to do + * an O_EXCL create using this path component. + */ static inline int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) { if (NFS_PROTO(dir)->version == 2) return 0; - if (!nd || (nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_CREATE)) + if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0) return 0; return (nd->intent.open.flags & O_EXCL) != 0; } @@ -772,12 +792,13 @@ struct dentry_operations nfs4_dentry_operations = { .d_iput = nfs_dentry_iput, }; +/* + * Use intent information to determine whether we need to substitute + * the NFSv4-style stateful OPEN for the LOOKUP call + */ static int is_atomic_open(struct inode *dir, struct nameidata *nd) { - if (!nd) - return 0; - /* Check that we are indeed trying to open this file */ - if ((nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_OPEN)) + if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0) return 0; /* NFS does not (yet) have a stateful open for directories */ if (nd->flags & LOOKUP_DIRECTORY) |