X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fnamei.c;h=e179f71bfcb058df613f83cb1ff923d47ffc4618;hb=4fe91d518e4958af7edebbeb112a3272b2be232d;hp=856b2f5da51d74928108430bf0f6b96d14d8394f;hpb=e63340ae6b6205fef26b40a75673d1c9c0c8bb90;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/namei.c b/fs/namei.c index 856b2f5..e179f71 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -107,6 +106,8 @@ * any extra contention... */ +static int __link_path_walk(const char *name, struct nameidata *nd); + /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -226,10 +227,14 @@ 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; + struct vfsmount *mnt = NULL; + + if (nd) + mnt = nd->path.mnt; if (mask & MAY_WRITE) { + umode_t mode = inode->i_mode; /* * Nobody gets write access to a read-only fs. @@ -245,22 +250,34 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) return -EACCES; } - - /* - * MAY_EXEC on regular files requires special handling: We override - * filesystem execute permissions if the mode bits aren't set or - * the fs is mounted with the "noexec" flag. - */ - if ((mask & MAY_EXEC) && S_ISREG(mode) && (!(mode & S_IXUGO) || - (nd && nd->mnt && (nd->mnt->mnt_flags & MNT_NOEXEC)))) - return -EACCES; + if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { + /* + * MAY_EXEC on regular files is denied if the fs is mounted + * with the "noexec" flag. + */ + if (mnt && (mnt->mnt_flags & MNT_NOEXEC)) + return -EACCES; + } /* Ordinary permission routines do not understand MAY_APPEND. */ submask = mask & ~MAY_APPEND; - if (inode->i_op && inode->i_op->permission) + if (inode->i_op && inode->i_op->permission) { retval = inode->i_op->permission(inode, submask, nd); - else + if (!retval) { + /* + * Exec permission on a regular file is denied if none + * of the execute bits are set. + * + * This check should be done by the ->permission() + * method. + */ + if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && + !(inode->i_mode & S_IXUGO)) + return -EACCES; + } + } else { retval = generic_permission(inode, submask, NULL); + } if (retval) return retval; @@ -279,7 +296,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) */ int vfs_permission(struct nameidata *nd, int mask) { - return permission(nd->dentry->d_inode, mask, nd); + return permission(nd->path.dentry->d_inode, mask, nd); } /** @@ -345,21 +362,31 @@ int deny_write_access(struct file * file) return 0; } -void path_release(struct nameidata *nd) +/** + * path_get - get a reference to a path + * @path: path to get the reference to + * + * Given a path increment the reference count to the dentry and the vfsmount. + */ +void path_get(struct path *path) { - dput(nd->dentry); - mntput(nd->mnt); + mntget(path->mnt); + dget(path->dentry); } +EXPORT_SYMBOL(path_get); -/* - * umount() mustn't call path_release()/mntput() as that would clear - * mnt_expiry_mark +/** + * path_put - put a reference to a path + * @path: path to put the reference to + * + * Given a path decrement the reference count to the dentry and the vfsmount. */ -void path_release_on_umount(struct nameidata *nd) +void path_put(struct path *path) { - dput(nd->dentry); - mntput_no_expire(nd->mnt); + dput(path->dentry); + mntput(path->mnt); } +EXPORT_SYMBOL(path_put); /** * release_open_intent - free up open intent resources @@ -522,20 +549,51 @@ walk_init_root(const char *name, struct nameidata *nd) 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); + if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { + nd->path = fs->altroot; + path_get(&fs->altroot); read_unlock(&fs->lock); if (__emul_lookup_dentry(name,nd)) return 0; read_lock(&fs->lock); } - nd->mnt = mntget(fs->rootmnt); - nd->dentry = dget(fs->root); + nd->path = fs->root; + path_get(&fs->root); read_unlock(&fs->lock); return 1; } +/* + * Wrapper to retry pathname resolution whenever the underlying + * file system returns an ESTALE. + * + * Retry the whole path once, forcing real lookup requests + * instead of relying on the dcache. + */ +static __always_inline int link_path_walk(const char *name, struct nameidata *nd) +{ + struct path save = nd->path; + int result; + + /* make sure the stuff we saved doesn't go away */ + dget(save.dentry); + mntget(save.mnt); + + result = __link_path_walk(name, nd); + if (result == -ESTALE) { + /* nd->path had been dropped */ + nd->path = save; + dget(nd->path.dentry); + mntget(nd->path.mnt); + nd->flags |= LOOKUP_REVAL; + result = __link_path_walk(name, nd); + } + + path_put(&save); + + return result; +} + static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) { int res = 0; @@ -544,7 +602,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l goto fail; if (*link == '/') { - path_release(nd); + path_put(&nd->path); if (!walk_init_root(link, nd)) /* weird __emul_prefix() stuff did it */ goto out; @@ -560,31 +618,31 @@ out: */ name = __getname(); if (unlikely(!name)) { - path_release(nd); + path_put(&nd->path); return -ENOMEM; } strcpy(name, nd->last.name); nd->last.name = name; return 0; fail: - path_release(nd); + path_put(&nd->path); return PTR_ERR(link); } -static inline void dput_path(struct path *path, struct nameidata *nd) +static void path_put_conditional(struct path *path, struct nameidata *nd) { dput(path->dentry); - if (path->mnt != nd->mnt) + if (path->mnt != nd->path.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; + dput(nd->path.dentry); + if (nd->path.mnt != path->mnt) + mntput(nd->path.mnt); + nd->path.mnt = path->mnt; + nd->path.dentry = path->dentry; } static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) @@ -596,7 +654,7 @@ 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) { + if (path->mnt != nd->path.mnt) { path_to_nameidata(path, nd); dget(dentry); } @@ -611,8 +669,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata if (dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, nd, cookie); } - dput(dentry); - mntput(path->mnt); + path_put(path); return error; } @@ -644,8 +701,8 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd) nd->depth--; return err; loop: - dput_path(path, nd); - path_release(nd); + path_put_conditional(path, nd); + path_put(&nd->path); return err; } @@ -726,37 +783,37 @@ static __always_inline void follow_dotdot(struct nameidata *nd) while(1) { struct vfsmount *parent; - struct dentry *old = nd->dentry; + struct dentry *old = nd->path.dentry; read_lock(&fs->lock); - if (nd->dentry == fs->root && - nd->mnt == fs->rootmnt) { + if (nd->path.dentry == fs->root.dentry && + nd->path.mnt == fs->root.mnt) { read_unlock(&fs->lock); break; } read_unlock(&fs->lock); spin_lock(&dcache_lock); - if (nd->dentry != nd->mnt->mnt_root) { - nd->dentry = dget(nd->dentry->d_parent); + if (nd->path.dentry != nd->path.mnt->mnt_root) { + nd->path.dentry = dget(nd->path.dentry->d_parent); spin_unlock(&dcache_lock); dput(old); break; } spin_unlock(&dcache_lock); spin_lock(&vfsmount_lock); - parent = nd->mnt->mnt_parent; - if (parent == nd->mnt) { + parent = nd->path.mnt->mnt_parent; + if (parent == nd->path.mnt) { spin_unlock(&vfsmount_lock); break; } mntget(parent); - nd->dentry = dget(nd->mnt->mnt_mountpoint); + nd->path.dentry = dget(nd->path.mnt->mnt_mountpoint); spin_unlock(&vfsmount_lock); dput(old); - mntput(nd->mnt); - nd->mnt = parent; + mntput(nd->path.mnt); + nd->path.mnt = parent; } - follow_mount(&nd->mnt, &nd->dentry); + follow_mount(&nd->path.mnt, &nd->path.dentry); } /* @@ -767,8 +824,8 @@ static __always_inline void follow_dotdot(struct nameidata *nd) static int do_lookup(struct nameidata *nd, struct qstr *name, struct path *path) { - struct vfsmount *mnt = nd->mnt; - struct dentry *dentry = __d_lookup(nd->dentry, name); + struct vfsmount *mnt = nd->path.mnt; + struct dentry *dentry = __d_lookup(nd->path.dentry, name); if (!dentry) goto need_lookup; @@ -781,7 +838,7 @@ done: return 0; need_lookup: - dentry = real_lookup(nd->dentry, name, nd); + dentry = real_lookup(nd->path.dentry, name, nd); if (IS_ERR(dentry)) goto fail; goto done; @@ -806,7 +863,7 @@ fail: * Returns 0 and nd will have valid dentry and mnt on success. * Returns error and drops reference to input namei data on failure. */ -static fastcall int __link_path_walk(const char * name, struct nameidata *nd) +static int __link_path_walk(const char *name, struct nameidata *nd) { struct path next; struct inode *inode; @@ -818,7 +875,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) if (!*name) goto return_reval; - inode = nd->dentry->d_inode; + inode = nd->path.dentry->d_inode; if (nd->depth) lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE); @@ -866,7 +923,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) if (this.name[1] != '.') break; follow_dotdot(nd); - inode = nd->dentry->d_inode; + inode = nd->path.dentry->d_inode; /* fallthrough */ case 1: continue; @@ -875,8 +932,9 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) * See if the low-level filesystem might want * to use its own hash.. */ - if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { - err = nd->dentry->d_op->d_hash(nd->dentry, &this); + if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) { + err = nd->path.dentry->d_op->d_hash(nd->path.dentry, + &this); if (err < 0) break; } @@ -898,7 +956,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) if (err) goto return_err; err = -ENOENT; - inode = nd->dentry->d_inode; + inode = nd->path.dentry->d_inode; if (!inode) break; err = -ENOTDIR; @@ -926,13 +984,14 @@ last_component: if (this.name[1] != '.') break; follow_dotdot(nd); - inode = nd->dentry->d_inode; + inode = nd->path.dentry->d_inode; /* fallthrough */ case 1: goto return_reval; } - if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { - err = nd->dentry->d_op->d_hash(nd->dentry, &this); + if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) { + err = nd->path.dentry->d_op->d_hash(nd->path.dentry, + &this); if (err < 0) break; } @@ -945,7 +1004,7 @@ last_component: err = do_follow_link(&next, nd); if (err) goto return_err; - inode = nd->dentry->d_inode; + inode = nd->path.dentry->d_inode; } else path_to_nameidata(&next, nd); err = -ENOENT; @@ -973,56 +1032,26 @@ return_reval: * We bypassed the ordinary revalidation routines. * We may need to check the cached dentry for staleness. */ - if (nd->dentry && nd->dentry->d_sb && - (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { + if (nd->path.dentry && nd->path.dentry->d_sb && + (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { err = -ESTALE; /* Note: we do not d_invalidate() */ - if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd)) + if (!nd->path.dentry->d_op->d_revalidate( + nd->path.dentry, nd)) break; } return_base: return 0; out_dput: - dput_path(&next, nd); + path_put_conditional(&next, nd); break; } - path_release(nd); + path_put(&nd->path); return_err: return err; } -/* - * Wrapper to retry pathname resolution whenever the underlying - * file system returns an ESTALE. - * - * Retry the whole path once, forcing real lookup requests - * instead of relying on the dcache. - */ -int fastcall link_path_walk(const char *name, struct nameidata *nd) -{ - struct nameidata save = *nd; - int result; - - /* make sure the stuff we saved doesn't go away */ - dget(save.dentry); - mntget(save.mnt); - - result = __link_path_walk(name, nd); - if (result == -ESTALE) { - *nd = save; - dget(nd->dentry); - mntget(nd->mnt); - nd->flags |= LOOKUP_REVAL; - result = __link_path_walk(name, nd); - } - - dput(save.dentry); - mntput(save.mnt); - - return result; -} - -int fastcall path_walk(const char * name, struct nameidata *nd) +static int path_walk(const char *name, struct nameidata *nd) { current->total_link_count = 0; return link_path_walk(name, nd); @@ -1037,9 +1066,9 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd) if (path_walk(name, nd)) return 0; /* something went wrong... */ - if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) { - struct dentry *old_dentry = nd->dentry; - struct vfsmount *old_mnt = nd->mnt; + if (!nd->path.dentry->d_inode || + S_ISDIR(nd->path.dentry->d_inode->i_mode)) { + struct path old_path = nd->path; struct qstr last = nd->last; int last_type = nd->last_type; struct fs_struct *fs = current->fs; @@ -1050,19 +1079,17 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd) */ nd->last_type = LAST_ROOT; read_lock(&fs->lock); - nd->mnt = mntget(fs->rootmnt); - nd->dentry = dget(fs->root); + nd->path = fs->root; + path_get(&fs->root); read_unlock(&fs->lock); if (path_walk(name, nd) == 0) { - if (nd->dentry->d_inode) { - dput(old_dentry); - mntput(old_mnt); + if (nd->path.dentry->d_inode) { + path_put(&old_path); return 1; } - path_release(nd); + path_put(&nd->path); } - nd->dentry = old_dentry; - nd->mnt = old_mnt; + nd->path = old_path; nd->last = last; nd->last_type = last_type; } @@ -1073,33 +1100,26 @@ void set_fs_altroot(void) { char *emul = __emul_prefix(); struct nameidata nd; - struct vfsmount *mnt = NULL, *oldmnt; - struct dentry *dentry = NULL, *olddentry; + struct path path = {}, old_path; int err; struct fs_struct *fs = current->fs; if (!emul) goto set_it; err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd); - if (!err) { - mnt = nd.mnt; - dentry = nd.dentry; - } + if (!err) + path = nd.path; set_it: write_lock(&fs->lock); - oldmnt = fs->altrootmnt; - olddentry = fs->altroot; - fs->altrootmnt = mnt; - fs->altroot = dentry; + old_path = fs->altroot; + fs->altroot = path; write_unlock(&fs->lock); - if (olddentry) { - dput(olddentry); - mntput(oldmnt); - } + if (old_path.dentry) + path_put(&old_path); } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int fastcall do_path_lookup(int dfd, const char *name, +static int do_path_lookup(int dfd, const char *name, unsigned int flags, struct nameidata *nd) { int retval = 0; @@ -1113,21 +1133,21 @@ static int fastcall do_path_lookup(int dfd, const char *name, if (*name=='/') { read_lock(&fs->lock); - if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) { - nd->mnt = mntget(fs->altrootmnt); - nd->dentry = dget(fs->altroot); + if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { + nd->path = fs->altroot; + path_get(&fs->altroot); read_unlock(&fs->lock); if (__emul_lookup_dentry(name,nd)) goto out; /* found in altroot */ read_lock(&fs->lock); } - nd->mnt = mntget(fs->rootmnt); - nd->dentry = dget(fs->root); + nd->path = fs->root; + path_get(&fs->root); read_unlock(&fs->lock); } else if (dfd == AT_FDCWD) { read_lock(&fs->lock); - nd->mnt = mntget(fs->pwdmnt); - nd->dentry = dget(fs->pwd); + nd->path = fs->pwd; + path_get(&fs->pwd); read_unlock(&fs->lock); } else { struct dentry *dentry; @@ -1147,19 +1167,17 @@ static int fastcall do_path_lookup(int dfd, const char *name, if (retval) goto fput_fail; - nd->mnt = mntget(file->f_path.mnt); - nd->dentry = dget(dentry); + nd->path = file->f_path; + path_get(&file->f_path); fput_light(file, fput_needed); } - current->total_link_count = 0; - retval = link_path_walk(name, nd); + + retval = path_walk(name, nd); out: - if (likely(retval == 0)) { - if (unlikely(!audit_dummy_context() && nd && nd->dentry && - nd->dentry->d_inode)) - audit_inode(name, nd->dentry->d_inode); - } + if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && + nd->path.dentry->d_inode)) + audit_inode(name, nd->path.dentry); out_fail: return retval; @@ -1168,12 +1186,43 @@ fput_fail: goto out_fail; } -int fastcall path_lookup(const char *name, unsigned int flags, +int path_lookup(const char *name, unsigned int flags, struct nameidata *nd) { return do_path_lookup(AT_FDCWD, name, flags, nd); } +/** + * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair + * @dentry: pointer to dentry of the base directory + * @mnt: pointer to vfs mount of the base directory + * @name: pointer to file name + * @flags: lookup flags + * @nd: pointer to nameidata + */ +int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, + const char *name, unsigned int flags, + struct nameidata *nd) +{ + int retval; + + /* same as do_path_lookup */ + nd->last_type = LAST_ROOT; + nd->flags = flags; + nd->depth = 0; + + nd->path.mnt = mntget(mnt); + nd->path.dentry = dget(dentry); + + retval = path_walk(name, nd); + if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && + nd->path.dentry->d_inode)) + audit_inode(name, nd->path.dentry); + + return retval; + +} + static int __path_lookup_intent_open(int dfd, const char *name, unsigned int lookup_flags, struct nameidata *nd, int open_flags, int create_mode) @@ -1190,7 +1239,7 @@ static int __path_lookup_intent_open(int dfd, const char *name, if (IS_ERR(nd->intent.open.file)) { if (err == 0) { err = PTR_ERR(nd->intent.open.file); - path_release(nd); + path_put(&nd->path); } } else if (err != 0) release_open_intent(nd); @@ -1242,7 +1291,8 @@ int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, return err; } -static inline struct dentry *__lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd) +static struct dentry *__lookup_hash(struct qstr *name, + struct dentry *base, struct nameidata *nd) { struct dentry *dentry; struct inode *inode; @@ -1282,31 +1332,18 @@ out: * needs parent already locked. Doesn't follow mounts. * SMP-safe. */ -static inline struct dentry * __lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) +static struct dentry *lookup_hash(struct nameidata *nd) { - struct dentry *dentry; - struct inode *inode; int err; - inode = base->d_inode; - - err = permission(inode, MAY_EXEC, nd); - dentry = ERR_PTR(err); + err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd); if (err) - goto out; - - dentry = __lookup_hash_kern(name, base, nd); -out: - return dentry; -} - -static struct dentry *lookup_hash(struct nameidata *nd) -{ - return __lookup_hash(&nd->last, nd->dentry, nd); + return ERR_PTR(err); + return __lookup_hash(&nd->last, nd->path.dentry, nd); } -/* SMP-safe */ -static inline int __lookup_one_len(const char *name, struct qstr *this, struct dentry *base, int len) +static int __lookup_one_len(const char *name, struct qstr *this, + struct dentry *base, int len) { unsigned long hash; unsigned int c; @@ -1327,6 +1364,17 @@ static inline int __lookup_one_len(const char *name, struct qstr *this, struct d return 0; } +/** + * lookup_one_len - filesystem helper to lookup single pathname component + * @name: pathname component to lookup + * @base: base directory to lookup from + * @len: maximum length @len should be interpreted to + * + * Note that this routine is purely a helper for filesystem usage and should + * not be called by generic code. Also note that by using this function the + * nameidata argument is passed to the filesystem methods and a filesystem + * using this helper needs to be prepared for that. + */ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) { int err; @@ -1335,21 +1383,36 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) err = __lookup_one_len(name, &this, base, len); if (err) return ERR_PTR(err); + + err = permission(base->d_inode, MAY_EXEC, NULL); + if (err) + return ERR_PTR(err); return __lookup_hash(&this, base, NULL); } -struct dentry *lookup_one_len_kern(const char *name, struct dentry *base, int len) +/** + * lookup_one_noperm - bad hack for sysfs + * @name: pathname component to lookup + * @base: base directory to lookup from + * + * This is a variant of lookup_one_len that doesn't perform any permission + * checks. It's a horrible hack to work around the braindead sysfs + * architecture and should not be used anywhere else. + * + * DON'T USE THIS FUNCTION EVER, thanks. + */ +struct dentry *lookup_one_noperm(const char *name, struct dentry *base) { int err; struct qstr this; - err = __lookup_one_len(name, &this, base, len); + err = __lookup_one_len(name, &this, base, strlen(name)); if (err) return ERR_PTR(err); - return __lookup_hash_kern(&this, base, NULL); + return __lookup_hash(&this, base, NULL); } -int fastcall __user_walk_fd(int dfd, const char __user *name, unsigned flags, +int __user_walk_fd(int dfd, const char __user *name, unsigned flags, struct nameidata *nd) { char *tmp = getname(name); @@ -1362,7 +1425,7 @@ int fastcall __user_walk_fd(int dfd, const char __user *name, unsigned flags, return err; } -int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) +int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) { return __user_walk_fd(AT_FDCWD, name, flags, nd); } @@ -1409,7 +1472,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); + audit_inode_child(victim->d_name.name, victim, dir); error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); if (error) @@ -1535,7 +1598,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode, int may_open(struct nameidata *nd, int acc_mode, int flag) { - struct dentry *dentry = nd->dentry; + struct dentry *dentry = nd->path.dentry; struct inode *inode = dentry->d_inode; int error; @@ -1545,13 +1608,9 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) if (S_ISLNK(inode->i_mode)) return -ELOOP; - if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) + if (S_ISDIR(inode->i_mode) && (acc_mode & MAY_WRITE)) return -EISDIR; - error = vfs_permission(nd, acc_mode); - if (error) - return error; - /* * FIFO's, sockets and device files are special: they don't * actually live on the filesystem itself, and as such you @@ -1560,12 +1619,15 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { flag &= ~O_TRUNC; } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { - if (nd->mnt->mnt_flags & MNT_NODEV) + if (nd->path.mnt->mnt_flags & MNT_NODEV) return -EACCES; flag &= ~O_TRUNC; - } else if (IS_RDONLY(inode) && (flag & FMODE_WRITE)) - return -EROFS; + } + + error = vfs_permission(nd, acc_mode); + if (error) + return error; /* * An append-only file must be opened in append mode for writing. */ @@ -1578,7 +1640,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) /* O_NOATIME can only be set by the owner or superuser */ if (flag & O_NOATIME) - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; /* @@ -1599,8 +1661,10 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) error = locks_verify_locked(inode); if (!error) { DQUOT_INIT(inode); - - error = do_truncate(dentry, 0, ATTR_MTIME|ATTR_CTIME, NULL); + + error = do_truncate(dentry, 0, + ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, + NULL); } put_write_access(inode); if (error) @@ -1612,18 +1676,23 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) return 0; } -static int open_namei_create(struct nameidata *nd, struct path *path, +/* + * Be careful about ever adding any more callers of this + * function. Its flags must be in the namei format, not + * what get passed to sys_open(). + */ +static int __open_namei_create(struct nameidata *nd, struct path *path, int flag, int mode) { int error; - struct dentry *dir = nd->dentry; + struct dentry *dir = nd->path.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; + dput(nd->path.dentry); + nd->path.dentry = path->dentry; if (error) return error; /* Don't check for write permission, don't truncate */ @@ -1631,26 +1700,56 @@ static int open_namei_create(struct nameidata *nd, struct path *path, } /* - * open_namei() - * - * namei for open - this is in fact almost the whole open-routine. + * Note that while the flag value (low two bits) for sys_open means: + * 00 - read-only + * 01 - write-only + * 10 - read-write + * 11 - special + * it is changed into + * 00 - no permissions needed + * 01 - read-permission + * 10 - write-permission + * 11 - read-write + * for the internal routines (ie open_namei()/follow_link() etc) + * This is more logical, and also allows the 00 "no perm needed" + * to be used for symlinks (where the permissions are checked + * later). * - * Note that the low bits of "flag" aren't the same as in the open - * system call - they are 00 - no permissions needed - * 01 - read permission needed - * 10 - write permission needed - * 11 - read/write permissions needed - * which is a lot more logical, and also allows the "no perm" needed - * for symlinks (where the permissions are checked later). - * SMP-safe +*/ +static inline int open_to_namei_flags(int flag) +{ + if ((flag+1) & O_ACCMODE) + flag++; + return flag; +} + +static int open_will_write_to_fs(int flag, struct inode *inode) +{ + /* + * We'll never write to the fs underlying + * a device file. + */ + if (special_file(inode->i_mode)) + return 0; + return (flag & O_TRUNC); +} + +/* + * Note that the low bits of the passed in "open_flag" + * are not the same as in the local variable "flag". See + * open_to_namei_flags() for more details. */ -int open_namei(int dfd, const char *pathname, int flag, - int mode, struct nameidata *nd) +struct file *do_filp_open(int dfd, const char *pathname, + int open_flag, int mode) { + struct file *filp; + struct nameidata nd; int acc_mode, error; struct path path; struct dentry *dir; int count = 0; + int will_write; + int flag = open_to_namei_flags(open_flag); acc_mode = ACC_MODE(flag); @@ -1668,18 +1767,19 @@ int open_namei(int dfd, const char *pathname, int flag, */ if (!(flag & O_CREAT)) { error = path_lookup_open(dfd, pathname, lookup_flags(flag), - nd, flag); + &nd, flag); if (error) - return error; + return ERR_PTR(error); goto ok; } /* * Create - we need to know the parent. */ - error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode); + error = path_lookup_create(dfd, pathname, LOOKUP_PARENT, + &nd, flag, mode); if (error) - return error; + return ERR_PTR(error); /* * We have the parent and last component. First of all, check @@ -1687,14 +1787,14 @@ int open_namei(int dfd, const char *pathname, int flag, * will not do. */ error = -EISDIR; - if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) + if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len]) goto exit; - dir = nd->dentry; - nd->flags &= ~LOOKUP_PARENT; + dir = nd.path.dentry; + nd.flags &= ~LOOKUP_PARENT; mutex_lock(&dir->d_inode->i_mutex); - path.dentry = lookup_hash(nd); - path.mnt = nd->mnt; + path.dentry = lookup_hash(&nd); + path.mnt = nd.path.mnt; do_last: error = PTR_ERR(path.dentry); @@ -1703,25 +1803,38 @@ 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; + if (IS_ERR(nd.intent.open.file)) { + error = PTR_ERR(nd.intent.open.file); + goto exit_mutex_unlock; } /* Negative dentry, just create the file */ if (!path.dentry->d_inode) { - error = open_namei_create(nd, &path, flag, mode); + /* + * This write is needed to ensure that a + * ro->rw transition does not occur between + * the time when the file is created and when + * a permanent write count is taken through + * the 'struct file' in nameidata_to_filp(). + */ + error = mnt_want_write(nd.path.mnt); if (error) + goto exit_mutex_unlock; + error = __open_namei_create(&nd, &path, flag, mode); + if (error) { + mnt_drop_write(nd.path.mnt); goto exit; - return 0; + } + filp = nameidata_to_filp(&nd, open_flag); + mnt_drop_write(nd.path.mnt); + return filp; } /* * It already exists. */ mutex_unlock(&dir->d_inode->i_mutex); - audit_inode_update(path.dentry->d_inode); + audit_inode(pathname, path.dentry); error = -EEXIST; if (flag & O_EXCL) @@ -1739,23 +1852,52 @@ do_last: if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link) goto do_link; - path_to_nameidata(&path, nd); + path_to_nameidata(&path, &nd); error = -EISDIR; if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode)) goto exit; ok: - error = may_open(nd, acc_mode, flag); - if (error) + /* + * Consider: + * 1. may_open() truncates a file + * 2. a rw->ro mount transition occurs + * 3. nameidata_to_filp() fails due to + * the ro mount. + * That would be inconsistent, and should + * be avoided. Taking this mnt write here + * ensures that (2) can not occur. + */ + will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode); + if (will_write) { + error = mnt_want_write(nd.path.mnt); + if (error) + goto exit; + } + error = may_open(&nd, acc_mode, flag); + if (error) { + if (will_write) + mnt_drop_write(nd.path.mnt); goto exit; - return 0; + } + filp = nameidata_to_filp(&nd, open_flag); + /* + * It is now safe to drop the mnt write + * because the filp has had a write taken + * on its behalf. + */ + if (will_write) + mnt_drop_write(nd.path.mnt); + return filp; +exit_mutex_unlock: + mutex_unlock(&dir->d_inode->i_mutex); exit_dput: - dput_path(&path, nd); + path_put_conditional(&path, &nd); exit: - if (!IS_ERR(nd->intent.open.file)) - release_open_intent(nd); - path_release(nd); - return error; + if (!IS_ERR(nd.intent.open.file)) + release_open_intent(&nd); + path_put(&nd.path); + return ERR_PTR(error); do_link: error = -ELOOP; @@ -1771,43 +1913,60 @@ do_link: * stored in nd->last.name and we will have to putname() it when we * are done. Procfs-like symlinks just set LAST_BIND. */ - nd->flags |= LOOKUP_PARENT; - error = security_inode_follow_link(path.dentry, nd); + nd.flags |= LOOKUP_PARENT; + error = security_inode_follow_link(path.dentry, &nd); if (error) goto exit_dput; - error = __do_follow_link(&path, nd); + error = __do_follow_link(&path, &nd); 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; + release_open_intent(&nd); + return ERR_PTR(error); } - nd->flags &= ~LOOKUP_PARENT; - if (nd->last_type == LAST_BIND) + nd.flags &= ~LOOKUP_PARENT; + if (nd.last_type == LAST_BIND) goto ok; error = -EISDIR; - if (nd->last_type != LAST_NORM) + if (nd.last_type != LAST_NORM) goto exit; - if (nd->last.name[nd->last.len]) { - __putname(nd->last.name); + if (nd.last.name[nd.last.len]) { + __putname(nd.last.name); goto exit; } error = -ELOOP; if (count++==32) { - __putname(nd->last.name); + __putname(nd.last.name); goto exit; } - dir = nd->dentry; + dir = nd.path.dentry; mutex_lock(&dir->d_inode->i_mutex); - path.dentry = lookup_hash(nd); - path.mnt = nd->mnt; - __putname(nd->last.name); + path.dentry = lookup_hash(&nd); + path.mnt = nd.path.mnt; + __putname(nd.last.name); goto do_last; } /** + * filp_open - open file and return file pointer + * + * @filename: path to open + * @flags: open flags as per the open(2) second argument + * @mode: mode for the new file if O_CREAT is set, else ignored + * + * This is the helper to open a file from kernelspace if you really + * have to. But in generally you should not do this, so please move + * along, nothing to see here.. + */ +struct file *filp_open(const char *filename, int flags, int mode) +{ + return do_filp_open(AT_FDCWD, filename, flags, mode); +} +EXPORT_SYMBOL(filp_open); + +/** * lookup_create - lookup a dentry, creating it if it doesn't exist * @nd: nameidata info * @is_dir: directory flag @@ -1815,13 +1974,13 @@ do_link: * Simple function to lookup and return a dentry and create it * if it doesn't exist. Is SMP-safe. * - * Returns with nd->dentry->d_inode->i_mutex locked. + * Returns with nd->path.dentry->d_inode->i_mutex locked. */ struct dentry *lookup_create(struct nameidata *nd, int is_dir) { struct dentry *dentry = ERR_PTR(-EEXIST); - mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); /* * Yucky last component or no last component at all? * (foo/., foo/.., /////) @@ -1880,6 +2039,23 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) return error; } +static int may_mknod(mode_t mode) +{ + switch (mode & S_IFMT) { + case S_IFREG: + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + case 0: /* zero mode translates to S_IFREG */ + return 0; + case S_IFDIR: + return -EPERM; + default: + return -EINVAL; + } +} + asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode, unsigned dev) { @@ -1898,32 +2074,36 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode, if (error) goto out; dentry = lookup_create(&nd, 0); - error = PTR_ERR(dentry); - - if (!IS_POSIXACL(nd.dentry->d_inode)) + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto out_unlock; + } + if (!IS_POSIXACL(nd.path.dentry->d_inode)) mode &= ~current->fs->umask; - if (!IS_ERR(dentry)) { - switch (mode & S_IFMT) { + error = may_mknod(mode); + if (error) + goto out_dput; + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; + switch (mode & S_IFMT) { case 0: case S_IFREG: - error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd); + error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd); break; case S_IFCHR: case S_IFBLK: - error = vfs_mknod(nd.dentry->d_inode,dentry,mode, + error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode, new_decode_dev(dev)); break; case S_IFIFO: case S_IFSOCK: - error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0); + error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0); break; - case S_IFDIR: - error = -EPERM; - break; - default: - error = -EINVAL; - } - dput(dentry); } - mutex_unlock(&nd.dentry->d_inode->i_mutex); - path_release(&nd); + mnt_drop_write(nd.path.mnt); +out_dput: + dput(dentry); +out_unlock: + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + path_put(&nd.path); out: putname(tmp); @@ -1977,13 +2157,18 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode) if (IS_ERR(dentry)) goto out_unlock; - if (!IS_POSIXACL(nd.dentry->d_inode)) + if (!IS_POSIXACL(nd.path.dentry->d_inode)) mode &= ~current->fs->umask; - error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; + error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); + mnt_drop_write(nd.path.mnt); +out_dput: dput(dentry); out_unlock: - mutex_unlock(&nd.dentry->d_inode->i_mutex); - path_release(&nd); + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + path_put(&nd.path); out: putname(tmp); out_err: @@ -2081,17 +2266,22 @@ static long do_rmdir(int dfd, const char __user *pathname) error = -EBUSY; goto exit1; } - mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit2; - error = vfs_rmdir(nd.dentry->d_inode, dentry); + error = mnt_want_write(nd.path.mnt); + if (error) + goto exit3; + error = vfs_rmdir(nd.path.dentry->d_inode, dentry); + mnt_drop_write(nd.path.mnt); +exit3: dput(dentry); exit2: - mutex_unlock(&nd.dentry->d_inode->i_mutex); + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); exit1: - path_release(&nd); + path_put(&nd.path); exit: putname(name); return error; @@ -2126,6 +2316,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) /* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { + fsnotify_link_count(dentry->d_inode); d_delete(dentry); } @@ -2156,7 +2347,7 @@ static long do_unlinkat(int dfd, const char __user *pathname) error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; - mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -2166,15 +2357,19 @@ static long do_unlinkat(int dfd, const char __user *pathname) inode = dentry->d_inode; if (inode) atomic_inc(&inode->i_count); - error = vfs_unlink(nd.dentry->d_inode, dentry); + error = mnt_want_write(nd.path.mnt); + if (error) + goto exit2; + error = vfs_unlink(nd.path.dentry->d_inode, dentry); + mnt_drop_write(nd.path.mnt); exit2: dput(dentry); } - mutex_unlock(&nd.dentry->d_inode->i_mutex); + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); if (inode) iput(inode); /* truncate the inode here */ exit1: - path_release(&nd); + path_put(&nd.path); exit: putname(name); return error; @@ -2247,11 +2442,16 @@ asmlinkage long sys_symlinkat(const char __user *oldname, if (IS_ERR(dentry)) goto out_unlock; - error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO); + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; + error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO); + mnt_drop_write(nd.path.mnt); +out_dput: dput(dentry); out_unlock: - mutex_unlock(&nd.dentry->d_inode->i_mutex); - path_release(&nd); + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + path_put(&nd.path); out: putname(to); out_putname: @@ -2298,7 +2498,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); + fsnotify_link(dir, old_dentry->d_inode, new_dentry); return error; } @@ -2336,20 +2536,25 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, if (error) goto out; error = -EXDEV; - if (old_nd.mnt != nd.mnt) + if (old_nd.path.mnt != nd.path.mnt) goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto out_unlock; - error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; + error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry); + mnt_drop_write(nd.path.mnt); +out_dput: dput(new_dentry); out_unlock: - mutex_unlock(&nd.dentry->d_inode->i_mutex); + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); out_release: - path_release(&nd); + path_put(&nd.path); out: - path_release(&old_nd); + path_put(&old_nd.path); exit: putname(to); @@ -2500,7 +2705,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!error) { const char *new_name = old_dentry->d_name.name; fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir, - new_dentry->d_inode, old_dentry->d_inode); + new_dentry->d_inode, old_dentry); } fsnotify_oldname_free(old_name); @@ -2525,15 +2730,15 @@ static int do_rename(int olddfd, const char *oldname, goto exit1; error = -EXDEV; - if (oldnd.mnt != newnd.mnt) + if (oldnd.path.mnt != newnd.path.mnt) goto exit2; - old_dir = oldnd.dentry; + old_dir = oldnd.path.dentry; error = -EBUSY; if (oldnd.last_type != LAST_NORM) goto exit2; - new_dir = newnd.dentry; + new_dir = newnd.path.dentry; if (newnd.last_type != LAST_NORM) goto exit2; @@ -2568,8 +2773,12 @@ static int do_rename(int olddfd, const char *oldname, if (new_dentry == trap) goto exit5; + error = mnt_want_write(oldnd.path.mnt); + if (error) + goto exit5; error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); + mnt_drop_write(oldnd.path.mnt); exit5: dput(new_dentry); exit4: @@ -2577,9 +2786,9 @@ exit4: exit3: unlock_rename(new_dir, old_dir); exit2: - path_release(&newnd); + path_put(&newnd.path); exit1: - path_release(&oldnd); + path_put(&oldnd.path); exit: return error; } @@ -2698,53 +2907,29 @@ int __page_symlink(struct inode *inode, const char *symname, int len, { struct address_space *mapping = inode->i_mapping; struct page *page; + void *fsdata; int err; char *kaddr; retry: - err = -ENOMEM; - 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; - } + err = pagecache_write_begin(NULL, mapping, 0, len-1, + AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (err) - goto fail_map; + goto fail; + kaddr = kmap_atomic(page, KM_USER0); memcpy(kaddr, symname, len-1); kunmap_atomic(kaddr, KM_USER0); - 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 - * that it is unmapped (typically even will not look into inode - - * ->i_size will be enough for everything) and zero it out. - * OTOH it's obviously correct and should make the page up-to-date. - */ - if (!PageUptodate(page)) { - err = mapping->a_ops->readpage(NULL, page); - if (err != AOP_TRUNCATED_PAGE) - wait_on_page_locked(page); - } else { - unlock_page(page); - } - page_cache_release(page); + + err = pagecache_write_end(NULL, mapping, 0, len-1, len-1, + page, fsdata); if (err < 0) goto fail; + if (err < len-1) + goto retry; + mark_inode_dirty(inode); return 0; -fail_map: - unlock_page(page); - page_cache_release(page); fail: return err; } @@ -2776,8 +2961,7 @@ EXPORT_SYMBOL(__page_symlink); EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); -EXPORT_SYMBOL(path_release); -EXPORT_SYMBOL(path_walk); +EXPORT_SYMBOL(vfs_path_lookup); EXPORT_SYMBOL(permission); EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(file_permission);