[PATCH] knfsd: Check return value of lockd_up in write_ports
[safe/jmp/linux-2.6] / fs / namei.c
index e28de84..28d49b3 100644 (file)
  */
 /*
  * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland)
- * implemented.  Let's see if raised priority of ->s_vfs_rename_sem gives
+ * implemented.  Let's see if raised priority of ->s_vfs_rename_mutex gives
  * any extra contention...
  */
 
@@ -159,7 +159,7 @@ char * getname(const char __user * filename)
 #ifdef CONFIG_AUDITSYSCALL
 void putname(const char *name)
 {
-       if (unlikely(current->audit_context))
+       if (unlikely(!audit_dummy_context()))
                audit_putname(name);
        else
                __putname(name);
@@ -227,10 +227,10 @@ int generic_permission(struct inode *inode, int mask,
 
 int permission(struct inode *inode, int mask, struct nameidata *nd)
 {
+       umode_t mode = inode->i_mode;
        int retval, submask;
 
        if (mask & MAY_WRITE) {
-               umode_t mode = inode->i_mode;
 
                /*
                 * Nobody gets write access to a read-only fs.
@@ -247,6 +247,13 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
        }
 
 
+       /*
+        * MAY_EXEC on regular files requires special handling: We override
+        * filesystem execute permissions if the mode bits aren't set.
+        */
+       if ((mask & MAY_EXEC) && S_ISREG(mode) && !(mode & S_IXUGO))
+               return -EACCES;
+
        /* Ordinary permission routines do not understand MAY_APPEND. */
        submask = mask & ~MAY_APPEND;
        if (inode->i_op && inode->i_op->permission)
@@ -365,6 +372,30 @@ void release_open_intent(struct nameidata *nd)
                fput(nd->intent.open.file);
 }
 
+static inline struct dentry *
+do_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+       int status = dentry->d_op->d_revalidate(dentry, nd);
+       if (unlikely(status <= 0)) {
+               /*
+                * The dentry failed validation.
+                * If d_revalidate returned 0 attempt to invalidate
+                * the dentry otherwise d_revalidate is asking us
+                * to return a fail status.
+                */
+               if (!status) {
+                       if (!d_invalidate(dentry)) {
+                               dput(dentry);
+                               dentry = NULL;
+                       }
+               } else {
+                       dput(dentry);
+                       dentry = ERR_PTR(status);
+               }
+       }
+       return dentry;
+}
+
 /*
  * Internal lookup() using the new generic dcache.
  * SMP-safe
@@ -379,12 +410,9 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
        if (!dentry)
                dentry = d_lookup(parent, name);
 
-       if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
-               if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) {
-                       dput(dentry);
-                       dentry = NULL;
-               }
-       }
+       if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
+               dentry = do_revalidate(dentry, nd);
+
        return dentry;
 }
 
@@ -477,10 +505,9 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
         */
        mutex_unlock(&dir->i_mutex);
        if (result->d_op && result->d_op->d_revalidate) {
-               if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
-                       dput(result);
+               result = do_revalidate(result, nd);
+               if (!result)
                        result = ERR_PTR(-ENOENT);
-               }
        }
        return result;
 }
@@ -491,18 +518,20 @@ static int __emul_lookup_dentry(const char *, struct nameidata *);
 static __always_inline int
 walk_init_root(const char *name, struct nameidata *nd)
 {
-       read_lock(&current->fs->lock);
-       if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-               nd->mnt = mntget(current->fs->altrootmnt);
-               nd->dentry = dget(current->fs->altroot);
-               read_unlock(&current->fs->lock);
+       struct fs_struct *fs = current->fs;
+
+       read_lock(&fs->lock);
+       if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+               nd->mnt = mntget(fs->altrootmnt);
+               nd->dentry = dget(fs->altroot);
+               read_unlock(&fs->lock);
                if (__emul_lookup_dentry(name,nd))
                        return 0;
-               read_lock(&current->fs->lock);
+               read_lock(&fs->lock);
        }
-       nd->mnt = mntget(current->fs->rootmnt);
-       nd->dentry = dget(current->fs->root);
-       read_unlock(&current->fs->lock);
+       nd->mnt = mntget(fs->rootmnt);
+       nd->dentry = dget(fs->root);
+       read_unlock(&fs->lock);
        return 1;
 }
 
@@ -546,6 +575,22 @@ struct path {
        struct dentry *dentry;
 };
 
+static inline void dput_path(struct path *path, struct nameidata *nd)
+{
+       dput(path->dentry);
+       if (path->mnt != nd->mnt)
+               mntput(path->mnt);
+}
+
+static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
+{
+       dput(nd->dentry);
+       if (nd->mnt != path->mnt)
+               mntput(nd->mnt);
+       nd->mnt = path->mnt;
+       nd->dentry = path->dentry;
+}
+
 static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
 {
        int error;
@@ -555,8 +600,11 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
        touch_atime(path->mnt, dentry);
        nd_set_link(nd, NULL);
 
-       if (path->mnt == nd->mnt)
-               mntget(path->mnt);
+       if (path->mnt != nd->mnt) {
+               path_to_nameidata(path, nd);
+               dget(dentry);
+       }
+       mntget(path->mnt);
        cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
        error = PTR_ERR(cookie);
        if (!IS_ERR(cookie)) {
@@ -573,22 +621,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
        return error;
 }
 
-static inline void dput_path(struct path *path, struct nameidata *nd)
-{
-       dput(path->dentry);
-       if (path->mnt != nd->mnt)
-               mntput(path->mnt);
-}
-
-static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
-{
-       dput(nd->dentry);
-       if (nd->mnt != path->mnt)
-               mntput(nd->mnt);
-       nd->mnt = path->mnt;
-       nd->dentry = path->dentry;
-}
-
 /*
  * This limits recursive symlink follows to 8, while
  * limiting consecutive symlinks to 40.
@@ -694,17 +726,19 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry)
 
 static __always_inline void follow_dotdot(struct nameidata *nd)
 {
+       struct fs_struct *fs = current->fs;
+
        while(1) {
                struct vfsmount *parent;
                struct dentry *old = nd->dentry;
 
-                read_lock(&current->fs->lock);
-               if (nd->dentry == current->fs->root &&
-                   nd->mnt == current->fs->rootmnt) {
-                        read_unlock(&current->fs->lock);
+                read_lock(&fs->lock);
+               if (nd->dentry == fs->root &&
+                   nd->mnt == fs->rootmnt) {
+                        read_unlock(&fs->lock);
                        break;
                }
-                read_unlock(&current->fs->lock);
+                read_unlock(&fs->lock);
                spin_lock(&dcache_lock);
                if (nd->dentry != nd->mnt->mnt_root) {
                        nd->dentry = dget(nd->dentry->d_parent);
@@ -757,12 +791,12 @@ need_lookup:
        goto done;
 
 need_revalidate:
-       if (dentry->d_op->d_revalidate(dentry, nd))
-               goto done;
-       if (d_invalidate(dentry))
-               goto done;
-       dput(dentry);
-       goto need_lookup;
+       dentry = do_revalidate(dentry, nd);
+       if (!dentry)
+               goto need_lookup;
+       if (IS_ERR(dentry))
+               goto fail;
+       goto done;
 
 fail:
        return PTR_ERR(dentry);
@@ -1012,15 +1046,17 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
                struct vfsmount *old_mnt = nd->mnt;
                struct qstr last = nd->last;
                int last_type = nd->last_type;
+               struct fs_struct *fs = current->fs;
+
                /*
-                * NAME was not found in alternate root or it's a directory.  Try to find
-                * it in the normal root:
+                * NAME was not found in alternate root or it's a directory.
+                * Try to find it in the normal root:
                 */
                nd->last_type = LAST_ROOT;
-               read_lock(&current->fs->lock);
-               nd->mnt = mntget(current->fs->rootmnt);
-               nd->dentry = dget(current->fs->root);
-               read_unlock(&current->fs->lock);
+               read_lock(&fs->lock);
+               nd->mnt = mntget(fs->rootmnt);
+               nd->dentry = dget(fs->root);
+               read_unlock(&fs->lock);
                if (path_walk(name, nd) == 0) {
                        if (nd->dentry->d_inode) {
                                dput(old_dentry);
@@ -1044,6 +1080,7 @@ void set_fs_altroot(void)
        struct vfsmount *mnt = NULL, *oldmnt;
        struct dentry *dentry = NULL, *olddentry;
        int err;
+       struct fs_struct *fs = current->fs;
 
        if (!emul)
                goto set_it;
@@ -1053,12 +1090,12 @@ void set_fs_altroot(void)
                dentry = nd.dentry;
        }
 set_it:
-       write_lock(&current->fs->lock);
-       oldmnt = current->fs->altrootmnt;
-       olddentry = current->fs->altroot;
-       current->fs->altrootmnt = mnt;
-       current->fs->altroot = dentry;
-       write_unlock(&current->fs->lock);
+       write_lock(&fs->lock);
+       oldmnt = fs->altrootmnt;
+       olddentry = fs->altroot;
+       fs->altrootmnt = mnt;
+       fs->altroot = dentry;
+       write_unlock(&fs->lock);
        if (olddentry) {
                dput(olddentry);
                mntput(oldmnt);
@@ -1072,65 +1109,67 @@ static int fastcall do_path_lookup(int dfd, const char *name,
        int retval = 0;
        int fput_needed;
        struct file *file;
+       struct fs_struct *fs = current->fs;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags;
        nd->depth = 0;
 
-       read_lock(&current->fs->lock);
        if (*name=='/') {
-               if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-                       nd->mnt = mntget(current->fs->altrootmnt);
-                       nd->dentry = dget(current->fs->altroot);
-                       read_unlock(&current->fs->lock);
+               read_lock(&fs->lock);
+               if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+                       nd->mnt = mntget(fs->altrootmnt);
+                       nd->dentry = dget(fs->altroot);
+                       read_unlock(&fs->lock);
                        if (__emul_lookup_dentry(name,nd))
                                goto out; /* found in altroot */
-                       read_lock(&current->fs->lock);
+                       read_lock(&fs->lock);
                }
-               nd->mnt = mntget(current->fs->rootmnt);
-               nd->dentry = dget(current->fs->root);
+               nd->mnt = mntget(fs->rootmnt);
+               nd->dentry = dget(fs->root);
+               read_unlock(&fs->lock);
        } else if (dfd == AT_FDCWD) {
-               nd->mnt = mntget(current->fs->pwdmnt);
-               nd->dentry = dget(current->fs->pwd);
+               read_lock(&fs->lock);
+               nd->mnt = mntget(fs->pwdmnt);
+               nd->dentry = dget(fs->pwd);
+               read_unlock(&fs->lock);
        } else {
                struct dentry *dentry;
 
                file = fget_light(dfd, &fput_needed);
                retval = -EBADF;
                if (!file)
-                       goto unlock_fail;
+                       goto out_fail;
 
                dentry = file->f_dentry;
 
                retval = -ENOTDIR;
                if (!S_ISDIR(dentry->d_inode->i_mode))
-                       goto fput_unlock_fail;
+                       goto fput_fail;
 
                retval = file_permission(file, MAY_EXEC);
                if (retval)
-                       goto fput_unlock_fail;
+                       goto fput_fail;
 
                nd->mnt = mntget(file->f_vfsmnt);
                nd->dentry = dget(dentry);
 
                fput_light(file, fput_needed);
        }
-       read_unlock(&current->fs->lock);
        current->total_link_count = 0;
        retval = link_path_walk(name, nd);
 out:
        if (likely(retval == 0)) {
-               if (unlikely(current->audit_context && nd && nd->dentry &&
+               if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
                                nd->dentry->d_inode))
-               audit_inode(name, nd->dentry->d_inode, flags);
+               audit_inode(name, nd->dentry->d_inode);
        }
+out_fail:
        return retval;
 
-fput_unlock_fail:
+fput_fail:
        fput_light(file, fput_needed);
-unlock_fail:
-       read_unlock(&current->fs->lock);
-       return retval;
+       goto out_fail;
 }
 
 int fastcall path_lookup(const char *name, unsigned int flags,
@@ -1251,7 +1290,7 @@ out:
        return dentry;
 }
 
-struct dentry * lookup_hash(struct nameidata *nd)
+static struct dentry *lookup_hash(struct nameidata *nd)
 {
        return __lookup_hash(&nd->last, nd->dentry, nd);
 }
@@ -1353,6 +1392,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
                return -ENOENT;
 
        BUG_ON(victim->d_parent->d_inode != dir);
+       audit_inode_child(victim->d_name.name, victim->d_inode, dir);
 
        error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
        if (error)
@@ -1418,30 +1458,30 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
        struct dentry *p;
 
        if (p1 == p2) {
-               mutex_lock(&p1->d_inode->i_mutex);
+               mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
                return NULL;
        }
 
-       down(&p1->d_inode->i_sb->s_vfs_rename_sem);
+       mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
 
        for (p = p1; p->d_parent != p; p = p->d_parent) {
                if (p->d_parent == p2) {
-                       mutex_lock(&p2->d_inode->i_mutex);
-                       mutex_lock(&p1->d_inode->i_mutex);
+                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
+                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
                        return p;
                }
        }
 
        for (p = p2; p->d_parent != p; p = p->d_parent) {
                if (p->d_parent == p1) {
-                       mutex_lock(&p1->d_inode->i_mutex);
-                       mutex_lock(&p2->d_inode->i_mutex);
+                       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+                       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
                        return p;
                }
        }
 
-       mutex_lock(&p1->d_inode->i_mutex);
-       mutex_lock(&p2->d_inode->i_mutex);
+       mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+       mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
        return NULL;
 }
 
@@ -1450,7 +1490,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
        mutex_unlock(&p1->d_inode->i_mutex);
        if (p1 != p2) {
                mutex_unlock(&p2->d_inode->i_mutex);
-               up(&p1->d_inode->i_sb->s_vfs_rename_sem);
+               mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
        }
 }
 
@@ -1472,7 +1512,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        DQUOT_INIT(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -1555,6 +1595,24 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
        return 0;
 }
 
+static int open_namei_create(struct nameidata *nd, struct path *path,
+                               int flag, int mode)
+{
+       int error;
+       struct dentry *dir = nd->dentry;
+
+       if (!IS_POSIXACL(dir->d_inode))
+               mode &= ~current->fs->umask;
+       error = vfs_create(dir->d_inode, path->dentry, mode, nd);
+       mutex_unlock(&dir->d_inode->i_mutex);
+       dput(nd->dentry);
+       nd->dentry = path->dentry;
+       if (error)
+               return error;
+       /* Don't check for write permission, don't truncate */
+       return may_open(nd, 0, flag & ~O_TRUNC);
+}
+
 /*
  *     open_namei()
  *
@@ -1628,26 +1686,25 @@ do_last:
                goto exit;
        }
 
+       if (IS_ERR(nd->intent.open.file)) {
+               mutex_unlock(&dir->d_inode->i_mutex);
+               error = PTR_ERR(nd->intent.open.file);
+               goto exit_dput;
+       }
+
        /* Negative dentry, just create the file */
        if (!path.dentry->d_inode) {
-               if (!IS_POSIXACL(dir->d_inode))
-                       mode &= ~current->fs->umask;
-               error = vfs_create(dir->d_inode, path.dentry, mode, nd);
-               mutex_unlock(&dir->d_inode->i_mutex);
-               dput(nd->dentry);
-               nd->dentry = path.dentry;
+               error = open_namei_create(nd, &path, flag, mode);
                if (error)
                        goto exit;
-               /* Don't check for write permission, don't truncate */
-               acc_mode = 0;
-               flag &= ~O_TRUNC;
-               goto ok;
+               return 0;
        }
 
        /*
         * It already exists.
         */
        mutex_unlock(&dir->d_inode->i_mutex);
+       audit_inode_update(path.dentry->d_inode);
 
        error = -EEXIST;
        if (flag & O_EXCL)
@@ -1658,6 +1715,7 @@ do_last:
                if (flag & O_NOFOLLOW)
                        goto exit_dput;
        }
+
        error = -ENOENT;
        if (!path.dentry->d_inode)
                goto exit_dput;
@@ -1701,8 +1759,14 @@ do_link:
        if (error)
                goto exit_dput;
        error = __do_follow_link(&path, nd);
-       if (error)
+       if (error) {
+               /* Does someone understand code flow here? Or it is only
+                * me so stupid? Anathema to whoever designed this non-sense
+                * with "intent.open".
+                */
+               release_open_intent(nd);
                return error;
+       }
        nd->flags &= ~LOOKUP_PARENT;
        if (nd->last_type == LAST_BIND)
                goto ok;
@@ -1740,7 +1804,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
 {
        struct dentry *dentry = ERR_PTR(-EEXIST);
 
-       mutex_lock(&nd->dentry->d_inode->i_mutex);
+       mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        /*
         * Yucky last component or no last component at all?
         * (foo/., foo/.., /////)
@@ -1748,6 +1812,8 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
        if (nd->last_type != LAST_NORM)
                goto fail;
        nd->flags &= ~LOOKUP_PARENT;
+       nd->flags |= LOOKUP_CREATE;
+       nd->intent.open.flags = O_EXCL;
 
        /*
         * Do the final lookup.
@@ -1793,7 +1859,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        DQUOT_INIT(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -1870,7 +1936,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        DQUOT_INIT(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
-               fsnotify_mkdir(dir, dentry->d_name.name);
+               fsnotify_mkdir(dir, dentry);
        return error;
 }
 
@@ -1878,30 +1944,32 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
 {
        int error = 0;
        char * tmp;
+       struct dentry *dentry;
+       struct nameidata nd;
 
        tmp = getname(pathname);
        error = PTR_ERR(tmp);
-       if (!IS_ERR(tmp)) {
-               struct dentry *dentry;
-               struct nameidata nd;
+       if (IS_ERR(tmp))
+               goto out_err;
 
-               error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
-               if (error)
-                       goto out;
-               dentry = lookup_create(&nd, 1);
-               error = PTR_ERR(dentry);
-               if (!IS_ERR(dentry)) {
-                       if (!IS_POSIXACL(nd.dentry->d_inode))
-                               mode &= ~current->fs->umask;
-                       error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
-                       dput(dentry);
-               }
-               mutex_unlock(&nd.dentry->d_inode->i_mutex);
-               path_release(&nd);
-out:
-               putname(tmp);
-       }
+       error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+       if (error)
+               goto out;
+       dentry = lookup_create(&nd, 1);
+       error = PTR_ERR(dentry);
+       if (IS_ERR(dentry))
+               goto out_unlock;
 
+       if (!IS_POSIXACL(nd.dentry->d_inode))
+               mode &= ~current->fs->umask;
+       error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+       dput(dentry);
+out_unlock:
+       mutex_unlock(&nd.dentry->d_inode->i_mutex);
+       path_release(&nd);
+out:
+       putname(tmp);
+out_err:
        return error;
 }
 
@@ -1997,13 +2065,14 @@ static long do_rmdir(int dfd, const char __user *pathname)
                        error = -EBUSY;
                        goto exit1;
        }
-       mutex_lock(&nd.dentry->d_inode->i_mutex);
+       mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
-       if (!IS_ERR(dentry)) {
-               error = vfs_rmdir(nd.dentry->d_inode, dentry);
-               dput(dentry);
-       }
+       if (IS_ERR(dentry))
+               goto exit2;
+       error = vfs_rmdir(nd.dentry->d_inode, dentry);
+       dput(dentry);
+exit2:
        mutex_unlock(&nd.dentry->d_inode->i_mutex);
 exit1:
        path_release(&nd);
@@ -2071,7 +2140,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
        error = -EISDIR;
        if (nd.last_type != LAST_NORM)
                goto exit1;
-       mutex_lock(&nd.dentry->d_inode->i_mutex);
+       mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        dentry = lookup_hash(&nd);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
@@ -2133,7 +2202,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
        DQUOT_INIT(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
-               fsnotify_create(dir, dentry->d_name.name);
+               fsnotify_create(dir, dentry);
        return error;
 }
 
@@ -2143,30 +2212,33 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        int error = 0;
        char * from;
        char * to;
+       struct dentry *dentry;
+       struct nameidata nd;
 
        from = getname(oldname);
        if(IS_ERR(from))
                return PTR_ERR(from);
        to = getname(newname);
        error = PTR_ERR(to);
-       if (!IS_ERR(to)) {
-               struct dentry *dentry;
-               struct nameidata nd;
+       if (IS_ERR(to))
+               goto out_putname;
 
-               error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
-               if (error)
-                       goto out;
-               dentry = lookup_create(&nd, 0);
-               error = PTR_ERR(dentry);
-               if (!IS_ERR(dentry)) {
-                       error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
-                       dput(dentry);
-               }
-               mutex_unlock(&nd.dentry->d_inode->i_mutex);
-               path_release(&nd);
+       error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+       if (error)
+               goto out;
+       dentry = lookup_create(&nd, 0);
+       error = PTR_ERR(dentry);
+       if (IS_ERR(dentry))
+               goto out_unlock;
+
+       error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
+       dput(dentry);
+out_unlock:
+       mutex_unlock(&nd.dentry->d_inode->i_mutex);
+       path_release(&nd);
 out:
-               putname(to);
-       }
+       putname(to);
+out_putname:
        putname(from);
        return error;
 }
@@ -2210,7 +2282,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&old_dentry->d_inode->i_mutex);
        if (!error)
-               fsnotify_create(dir, new_dentry->d_name.name);
+               fsnotify_create(dir, new_dentry);
        return error;
 }
 
@@ -2224,18 +2296,24 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
  * and other special files.  --ADM
  */
 asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
-                          int newdfd, const char __user *newname)
+                          int newdfd, const char __user *newname,
+                          int flags)
 {
        struct dentry *new_dentry;
        struct nameidata nd, old_nd;
        int error;
        char * to;
 
+       if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
+               return -EINVAL;
+
        to = getname(newname);
        if (IS_ERR(to))
                return PTR_ERR(to);
 
-       error = __user_walk_fd(olddfd, oldname, 0, &old_nd);
+       error = __user_walk_fd(olddfd, oldname,
+                              flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
+                              &old_nd);
        if (error)
                goto exit;
        error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
@@ -2246,10 +2324,11 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
                goto out_release;
        new_dentry = lookup_create(&nd, 0);
        error = PTR_ERR(new_dentry);
-       if (!IS_ERR(new_dentry)) {
-               error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
-               dput(new_dentry);
-       }
+       if (IS_ERR(new_dentry))
+               goto out_unlock;
+       error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+       dput(new_dentry);
+out_unlock:
        mutex_unlock(&nd.dentry->d_inode->i_mutex);
 out_release:
        path_release(&nd);
@@ -2263,7 +2342,7 @@ exit:
 
 asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
 {
-       return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname);
+       return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
 /*
@@ -2273,17 +2352,17 @@ asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
  *     a) we can get into loop creation. Check is done in is_subdir().
  *     b) race potential - two innocent renames can create a loop together.
  *        That's where 4.4 screws up. Current fix: serialization on
- *        sb->s_vfs_rename_sem. We might be more accurate, but that's another
+ *        sb->s_vfs_rename_mutex. We might be more accurate, but that's another
  *        story.
  *     c) we have to lock _three_ objects - parents and victim (if it exists).
  *        And that - after we got ->i_mutex on parents (until then we don't know
  *        whether the target exists).  Solution: try to be smart with locking
  *        order for inodes.  We rely on the fact that tree topology may change
- *        only under ->s_vfs_rename_sem _and_ that parent of the object we
+ *        only under ->s_vfs_rename_mutex _and_ that parent of the object we
  *        move will be locked.  Thus we can rank directories by the tree
  *        (ancestors first) and rank all non-directories after them.
  *        That works since everybody except rename does "lock parent, lookup,
- *        lock child" and rename is under ->s_vfs_rename_sem.
+ *        lock child" and rename is under ->s_vfs_rename_mutex.
  *        HOWEVER, it relies on the assumption that any object with ->lookup()
  *        has no more than 1 dentry.  If "hybrid" objects will ever appear,
  *        we'd better make sure that there's no link(2) for them.
@@ -2336,7 +2415,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
                dput(new_dentry);
        }
        if (!error)
-               d_move(old_dentry,new_dentry);
+               if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
+                       d_move(old_dentry,new_dentry);
        return error;
 }
 
@@ -2359,8 +2439,7 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
        else
                error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (!error) {
-               /* The following d_move() should become unconditional */
-               if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME))
+               if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry, new_dentry);
        }
        if (target)
@@ -2562,8 +2641,7 @@ static char *page_getlink(struct dentry * dentry, struct page **ppage)
 {
        struct page * page;
        struct address_space *mapping = dentry->d_inode->i_mapping;
-       page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
-                               NULL);
+       page = read_mapping_page(mapping, 0, NULL);
        if (IS_ERR(page))
                goto sync_fail;
        wait_on_page_locked(page);
@@ -2609,22 +2687,35 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
        }
 }
 
-int page_symlink(struct inode *inode, const char *symname, int len)
+int __page_symlink(struct inode *inode, const char *symname, int len,
+               gfp_t gfp_mask)
 {
        struct address_space *mapping = inode->i_mapping;
-       struct page *page = grab_cache_page(mapping, 0);
+       struct page *page;
        int err = -ENOMEM;
        char *kaddr;
 
+retry:
+       page = find_or_create_page(mapping, 0, gfp_mask);
        if (!page)
                goto fail;
        err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
+       if (err == AOP_TRUNCATED_PAGE) {
+               page_cache_release(page);
+               goto retry;
+       }
        if (err)
                goto fail_map;
        kaddr = kmap_atomic(page, KM_USER0);
        memcpy(kaddr, symname, len-1);
        kunmap_atomic(kaddr, KM_USER0);
-       mapping->a_ops->commit_write(NULL, page, 0, len-1);
+       err = mapping->a_ops->commit_write(NULL, page, 0, len-1);
+       if (err == AOP_TRUNCATED_PAGE) {
+               page_cache_release(page);
+               goto retry;
+       }
+       if (err)
+               goto fail_map;
        /*
         * Notice that we are _not_ going to block here - end of page is
         * unmapped, so this will only try to map the rest of page, see
@@ -2634,7 +2725,8 @@ int page_symlink(struct inode *inode, const char *symname, int len)
         */
        if (!PageUptodate(page)) {
                err = mapping->a_ops->readpage(NULL, page);
-               wait_on_page_locked(page);
+               if (err != AOP_TRUNCATED_PAGE)
+                       wait_on_page_locked(page);
        } else {
                unlock_page(page);
        }
@@ -2650,6 +2742,12 @@ fail:
        return err;
 }
 
+int page_symlink(struct inode *inode, const char *symname, int len)
+{
+       return __page_symlink(inode, symname, len,
+                       mapping_gfp_mask(inode->i_mapping));
+}
+
 struct inode_operations page_symlink_inode_operations = {
        .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
@@ -2663,11 +2761,11 @@ EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
 EXPORT_SYMBOL(getname);
 EXPORT_SYMBOL(lock_rename);
-EXPORT_SYMBOL(lookup_hash);
 EXPORT_SYMBOL(lookup_one_len);
 EXPORT_SYMBOL(page_follow_link_light);
 EXPORT_SYMBOL(page_put_link);
 EXPORT_SYMBOL(page_readlink);
+EXPORT_SYMBOL(__page_symlink);
 EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);