Skip to content

Commit

Permalink
Merge branch 'getname2' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/viro/vfs

Pull getname/putname updates from Al Viro:
 "Rework of getname/getname_kernel/etc., mostly from Paul Moore.  Gets
  rid of quite a pile of kludges between namei and audit..."

* 'getname2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  audit: replace getname()/putname() hacks with reference counters
  audit: fix filename matching in __audit_inode() and __audit_inode_child()
  audit: enable filename recording via getname_kernel()
  simpler calling conventions for filename_mountpoint()
  fs: create proper filename objects using getname_kernel()
  fs: rework getname_kernel to handle up to PATH_MAX sized filenames
  cut down the number of do_path_lookup() callers
  • Loading branch information
torvalds committed Feb 17, 2015
2 parents c6b1de1 + 55422d0 commit 05016b0
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 218 deletions.
10 changes: 8 additions & 2 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,14 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)

struct file *open_exec(const char *name)
{
struct filename tmp = { .name = name };
return do_open_execat(AT_FDCWD, &tmp, 0);
struct filename *filename = getname_kernel(name);
struct file *f = ERR_CAST(filename);

if (!IS_ERR(filename)) {
f = do_open_execat(AT_FDCWD, filename, 0);
putname(filename);
}
return f;
}
EXPORT_SYMBOL(open_exec);

Expand Down
143 changes: 90 additions & 53 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,6 @@
* POSIX.1 2.4: an empty pathname is invalid (ENOENT).
* PATH_MAX includes the nul terminator --RR.
*/
void final_putname(struct filename *name)
{
if (name->separate) {
__putname(name->name);
kfree(name);
} else {
__putname(name);
}
}

#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename))

Expand All @@ -145,6 +136,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
result->refcnt = 1;

/*
* First, try to embed the struct filename inside the names_cache
Expand Down Expand Up @@ -179,6 +171,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
}
result->name = kname;
result->separate = true;
result->refcnt = 1;
max = PATH_MAX;
goto recopy;
}
Expand All @@ -202,7 +195,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
return result;

error:
final_putname(result);
putname(result);
return err;
}

Expand All @@ -212,43 +205,56 @@ getname(const char __user * filename)
return getname_flags(filename, 0, NULL);
}

/*
* The "getname_kernel()" interface doesn't do pathnames longer
* than EMBEDDED_NAME_MAX. Deal with it - you're a kernel user.
*/
struct filename *
getname_kernel(const char * filename)
{
struct filename *result;
char *kname;
int len;

len = strlen(filename);
if (len >= EMBEDDED_NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
int len = strlen(filename) + 1;

result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);

kname = (char *)result + sizeof(*result);
result->name = kname;
if (len <= EMBEDDED_NAME_MAX) {
result->name = (char *)(result) + sizeof(*result);
result->separate = false;
} else if (len <= PATH_MAX) {
struct filename *tmp;

tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
if (unlikely(!tmp)) {
__putname(result);
return ERR_PTR(-ENOMEM);
}
tmp->name = (char *)result;
tmp->separate = true;
result = tmp;
} else {
__putname(result);
return ERR_PTR(-ENAMETOOLONG);
}
memcpy((char *)result->name, filename, len);
result->uptr = NULL;
result->aname = NULL;
result->separate = false;
result->refcnt = 1;
audit_getname(result);

strlcpy(kname, filename, EMBEDDED_NAME_MAX);
return result;
}

#ifdef CONFIG_AUDITSYSCALL
void putname(struct filename *name)
{
if (unlikely(!audit_dummy_context()))
return audit_putname(name);
final_putname(name);
BUG_ON(name->refcnt <= 0);

if (--name->refcnt > 0)
return;

if (name->separate) {
__putname(name->name);
kfree(name);
} else
__putname(name);
}
#endif

static int check_acl(struct inode *inode, int mask)
{
Expand Down Expand Up @@ -2036,31 +2042,47 @@ static int filename_lookup(int dfd, struct filename *name,
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
struct filename filename = { .name = name };
struct filename *filename = getname_kernel(name);
int retval = PTR_ERR(filename);

return filename_lookup(dfd, &filename, flags, nd);
if (!IS_ERR(filename)) {
retval = filename_lookup(dfd, filename, flags, nd);
putname(filename);
}
return retval;
}

/* does lookup, returns the object with parent locked */
struct dentry *kern_path_locked(const char *name, struct path *path)
{
struct filename *filename = getname_kernel(name);
struct nameidata nd;
struct dentry *d;
int err = do_path_lookup(AT_FDCWD, name, LOOKUP_PARENT, &nd);
if (err)
return ERR_PTR(err);
int err;

if (IS_ERR(filename))
return ERR_CAST(filename);

err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd);
if (err) {
d = ERR_PTR(err);
goto out;
}
if (nd.last_type != LAST_NORM) {
path_put(&nd.path);
return ERR_PTR(-EINVAL);
d = ERR_PTR(-EINVAL);
goto out;
}
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
d = __lookup_hash(&nd.last, nd.path.dentry, 0);
if (IS_ERR(d)) {
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
return d;
goto out;
}
*path = nd.path;
out:
putname(filename);
return d;
}

Expand Down Expand Up @@ -2351,13 +2373,17 @@ static int
filename_mountpoint(int dfd, struct filename *s, struct path *path,
unsigned int flags)
{
int error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
int error;
if (IS_ERR(s))
return PTR_ERR(s);
error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
if (unlikely(error == -ECHILD))
error = path_mountpoint(dfd, s->name, path, flags);
if (unlikely(error == -ESTALE))
error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL);
if (likely(!error))
audit_inode(s, path->dentry, 0);
putname(s);
return error;
}

Expand All @@ -2379,21 +2405,14 @@ int
user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags,
struct path *path)
{
struct filename *s = getname(name);
int error;
if (IS_ERR(s))
return PTR_ERR(s);
error = filename_mountpoint(dfd, s, path, flags);
putname(s);
return error;
return filename_mountpoint(dfd, getname(name), path, flags);
}

int
kern_path_mountpoint(int dfd, const char *name, struct path *path,
unsigned int flags)
{
struct filename s = {.name = name};
return filename_mountpoint(dfd, &s, path, flags);
return filename_mountpoint(dfd, getname_kernel(name), path, flags);
}
EXPORT_SYMBOL(kern_path_mountpoint);

Expand Down Expand Up @@ -3273,7 +3292,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
{
struct nameidata nd;
struct file *file;
struct filename filename = { .name = name };
struct filename *filename;
int flags = op->lookup_flags | LOOKUP_ROOT;

nd.root.mnt = mnt;
Expand All @@ -3282,15 +3301,20 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN)
return ERR_PTR(-ELOOP);

file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU);
filename = getname_kernel(name);
if (unlikely(IS_ERR(filename)))
return ERR_CAST(filename);

file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU);
if (unlikely(file == ERR_PTR(-ECHILD)))
file = path_openat(-1, &filename, &nd, op, flags);
file = path_openat(-1, filename, &nd, op, flags);
if (unlikely(file == ERR_PTR(-ESTALE)))
file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL);
file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL);
putname(filename);
return file;
}

struct dentry *kern_path_create(int dfd, const char *pathname,
static struct dentry *filename_create(int dfd, struct filename *name,
struct path *path, unsigned int lookup_flags)
{
struct dentry *dentry = ERR_PTR(-EEXIST);
Expand All @@ -3305,7 +3329,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname,
*/
lookup_flags &= LOOKUP_REVAL;

error = do_path_lookup(dfd, pathname, LOOKUP_PARENT|lookup_flags, &nd);
error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd);
if (error)
return ERR_PTR(error);

Expand Down Expand Up @@ -3359,6 +3383,19 @@ struct dentry *kern_path_create(int dfd, const char *pathname,
path_put(&nd.path);
return dentry;
}

struct dentry *kern_path_create(int dfd, const char *pathname,
struct path *path, unsigned int lookup_flags)
{
struct filename *filename = getname_kernel(pathname);
struct dentry *res;

if (IS_ERR(filename))
return ERR_CAST(filename);
res = filename_create(dfd, filename, path, lookup_flags);
putname(filename);
return res;
}
EXPORT_SYMBOL(kern_path_create);

void done_path_create(struct path *path, struct dentry *dentry)
Expand All @@ -3377,7 +3414,7 @@ struct dentry *user_path_create(int dfd, const char __user *pathname,
struct dentry *res;
if (IS_ERR(tmp))
return ERR_CAST(tmp);
res = kern_path_create(dfd, tmp->name, path, lookup_flags);
res = filename_create(dfd, tmp, path, lookup_flags);
putname(tmp);
return res;
}
Expand Down
10 changes: 8 additions & 2 deletions fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,14 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode)
*/
struct file *filp_open(const char *filename, int flags, umode_t mode)
{
struct filename name = {.name = filename};
return file_open_name(&name, flags, mode);
struct filename *name = getname_kernel(filename);
struct file *file = ERR_CAST(name);

if (!IS_ERR(name)) {
file = file_open_name(name, flags, mode);
putname(name);
}
return file;
}
EXPORT_SYMBOL(filp_open);

Expand Down
3 changes: 0 additions & 3 deletions include/linux/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
extern void __audit_syscall_exit(int ret_success, long ret_value);
extern struct filename *__audit_reusename(const __user char *uptr);
extern void __audit_getname(struct filename *name);
extern void audit_putname(struct filename *name);

#define AUDIT_INODE_PARENT 1 /* dentry represents the parent */
#define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */
Expand Down Expand Up @@ -352,8 +351,6 @@ static inline struct filename *audit_reusename(const __user char *name)
}
static inline void audit_getname(struct filename *name)
{ }
static inline void audit_putname(struct filename *name)
{ }
static inline void __audit_inode(struct filename *name,
const struct dentry *dentry,
unsigned int flags)
Expand Down
9 changes: 2 additions & 7 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2141,6 +2141,7 @@ struct filename {
const char *name; /* pointer to actual string */
const __user char *uptr; /* original userland pointer */
struct audit_names *aname;
int refcnt;
bool separate; /* should "name" be freed? */
};

Expand All @@ -2162,6 +2163,7 @@ extern int filp_close(struct file *, fl_owner_t id);
extern struct filename *getname_flags(const char __user *, int, int *);
extern struct filename *getname(const char __user *);
extern struct filename *getname_kernel(const char *);
extern void putname(struct filename *name);

enum {
FILE_CREATED = 1,
Expand All @@ -2182,15 +2184,8 @@ extern void __init vfs_caches_init(unsigned long);

extern struct kmem_cache *names_cachep;

extern void final_putname(struct filename *name);

#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
#ifndef CONFIG_AUDITSYSCALL
#define putname(name) final_putname(name)
#else
extern void putname(struct filename *name);
#endif

#ifdef CONFIG_BLOCK
extern int register_blkdev(unsigned int, const char *);
Expand Down
Loading

0 comments on commit 05016b0

Please sign in to comment.