Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05016b0f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents c6b1de1b 55422d0b
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -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);

+90 −53
Original line number Diff line number Diff line
@@ -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))

@@ -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
@@ -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;
	}
@@ -202,7 +195,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
	return result;

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

@@ -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)
{
@@ -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;
}

@@ -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;
}

@@ -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);

@@ -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;
@@ -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);
@@ -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);

@@ -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)
@@ -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;
}
+8 −2
Original line number Diff line number Diff line
@@ -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);

+0 −3
Original line number Diff line number Diff line
@@ -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 */
@@ -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)
+2 −7
Original line number Diff line number Diff line
@@ -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? */
};

@@ -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,
@@ -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 *);
Loading