6581 lines · 178.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/fs/namei.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ /* * Some corrections by tytso. */ /* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname * lookup logic. *//* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture. */ #include <linux/init.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/wordpart.h>#include <linux/fs.h>#include <linux/filelock.h>#include <linux/namei.h>#include <linux/pagemap.h>#include <linux/sched/mm.h>#include <linux/fsnotify.h>#include <linux/personality.h>#include <linux/security.h>#include <linux/syscalls.h>#include <linux/mount.h>#include <linux/audit.h>#include <linux/capability.h>#include <linux/file.h>#include <linux/fcntl.h>#include <linux/device_cgroup.h>#include <linux/fs_struct.h>#include <linux/posix_acl.h>#include <linux/hash.h>#include <linux/bitops.h>#include <linux/init_task.h>#include <linux/uaccess.h> #include <asm/runtime-const.h> #include "internal.h"#include "mount.h" /* [Feb-1997 T. Schoebel-Theuer] * Fundamental changes in the pathname lookup mechanisms (namei) * were necessary because of omirr.  The reason is that omirr needs * to know the _real_ pathname, not the user-supplied one, in case * of symlinks (and also when transname replacements occur). * * The new code replaces the old recursive symlink resolution with * an iterative one (in case of non-nested symlink chains).  It does * this with calls to <fs>_follow_link(). * As a side effect, dir_namei(), _namei() and follow_link() are now  * replaced with a single function lookup_dentry() that can handle all  * the special cases of the former code. * * With the new dcache, the pathname is stored at each inode, at least as * long as the refcount of the inode is positive.  As a side effect, the * size of the dcache depends on the inode cache and thus is dynamic. * * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink * resolution to correspond with current state of the code. * * Note that the symlink resolution is not *completely* iterative. * There is still a significant amount of tail- and mid- recursion in * the algorithm.  Also, note that <fs>_readlink() is not used in * lookup_dentry(): lookup_dentry() on the result of <fs>_readlink() * may return different results than <fs>_follow_link().  Many virtual * filesystems (including /proc) exhibit this behavior. */ /* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation: * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL * and the name already exists in form of a symlink, try to create the new * name indicated by the symlink. The old code always complained that the * name already exists, due to not following the symlink even if its target * is nonexistent.  The new semantics affects also mknod() and link() when * the name is a symlink pointing to a non-existent name. * * I don't know which semantics is the right one, since I have no access * to standards. But I found by trial that HP-UX 9.0 has the full "new" * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the * "old" one. Personally, I think the new semantics is much more logical. * Note that "ln old new" where "new" is a symlink pointing to a non-existing * file does succeed in both HP-UX and SunOs, but not in Solaris * and in the old Linux semantics. */ /* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink * semantics.  See the comments in "open_namei" and "do_link" below. * * [10-Sep-98 Alan Modra] Another symlink change. */ /* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks: *	inside the path - always follow. *	in the last component in creation/removal/renaming - never follow. *	if LOOKUP_FOLLOW passed - follow. *	if the pathname has trailing slashes - follow. *	otherwise - don't follow. * (applied in that order). * * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT * restored for 2.4. This is the last surviving part of old 4.2BSD bug. * During the 2.4 we need to fix the userland stuff depending on it - * hopefully we will be able to get rid of that wart in 2.5. So far only * XEmacs seems to be relying on it... *//* * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland) * implemented.  Let's see if raised priority of ->s_vfs_rename_mutex gives * any extra contention... */ /* 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.. * * POSIX.1 2.4: an empty pathname is invalid (ENOENT). * PATH_MAX includes the nul terminator --RR. */ /* SLAB cache for struct filename instances */static struct kmem_cache *__names_cache __ro_after_init;#define names_cache	runtime_const_ptr(__names_cache) /* * Type of the last component on LOOKUP_PARENT */enum last_type {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT}; void __init filename_init(void){	__names_cache = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0,			 SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),			 EMBEDDED_NAME_MAX, NULL);	runtime_const_init(ptr, __names_cache);} static inline struct filename *alloc_filename(void){	return kmem_cache_alloc(names_cache, GFP_KERNEL);} static inline void free_filename(struct filename *p){	kmem_cache_free(names_cache, p);} static inline void initname(struct filename *name){	name->aname = NULL;	name->refcnt = 1;} static int getname_long(struct filename *name, const char __user *filename){	int len;	char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL);	if (unlikely(!p))		return -ENOMEM; 	memcpy(p, &name->iname, EMBEDDED_NAME_MAX);	len = strncpy_from_user(p + EMBEDDED_NAME_MAX,				filename + EMBEDDED_NAME_MAX,				PATH_MAX - EMBEDDED_NAME_MAX);	if (unlikely(len < 0))		return len;	if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX))		return -ENAMETOOLONG;	name->name = no_free_ptr(p);	return 0;} static struct filename *do_getname(const char __user *filename, int flags, bool incomplete){	struct filename *result;	char *kname;	int len; 	result = alloc_filename();	if (unlikely(!result))		return ERR_PTR(-ENOMEM); 	/*	 * First, try to embed the struct filename inside the names_cache	 * allocation	 */	kname = (char *)result->iname;	result->name = kname; 	len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX);	/*	 * Handle both empty path and copy failure in one go.	 */	if (unlikely(len <= 0)) {		/* The empty path is special. */		if (!len && !(flags & LOOKUP_EMPTY))			len = -ENOENT;	} 	/*	 * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a	 * separate struct filename so we can dedicate the entire	 * names_cache allocation for the pathname, and re-do the copy from	 * userland.	 */	if (unlikely(len == EMBEDDED_NAME_MAX))		len = getname_long(result, filename);	if (unlikely(len < 0)) {		free_filename(result);		return ERR_PTR(len);	} 	initname(result);	if (likely(!incomplete))		audit_getname(result);	return result;} struct filename *getname_flags(const char __user *filename, int flags){	return do_getname(filename, flags, false);} struct filename *getname_uflags(const char __user *filename, int uflags){	int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; 	return getname_flags(filename, flags);} struct filename *__getname_maybe_null(const char __user *pathname){	char c; 	/* try to save on allocations; loss on um, though */	if (get_user(c, pathname))		return ERR_PTR(-EFAULT);	if (!c)		return NULL; 	CLASS(filename_flags, name)(pathname, LOOKUP_EMPTY);	/* empty pathname translates to NULL */	if (!IS_ERR(name) && !(name->name[0]))		return NULL;	return no_free_ptr(name);} static struct filename *do_getname_kernel(const char *filename, bool incomplete){	struct filename *result;	int len = strlen(filename) + 1;	char *p; 	if (unlikely(len > PATH_MAX))		return ERR_PTR(-ENAMETOOLONG); 	result = alloc_filename();	if (unlikely(!result))		return ERR_PTR(-ENOMEM); 	if (len <= EMBEDDED_NAME_MAX) {		p = (char *)result->iname;		memcpy(p, filename, len);	} else {		p = kmemdup(filename, len, GFP_KERNEL);		if (unlikely(!p)) {			free_filename(result);			return ERR_PTR(-ENOMEM);		}	}	result->name = p;	initname(result);	if (likely(!incomplete))		audit_getname(result);	return result;} struct filename *getname_kernel(const char *filename){	return do_getname_kernel(filename, false);}EXPORT_SYMBOL(getname_kernel); void putname(struct filename *name){	int refcnt; 	if (IS_ERR_OR_NULL(name))		return; 	refcnt = name->refcnt;	if (unlikely(refcnt != 1)) {		if (WARN_ON_ONCE(!refcnt))			return; 		name->refcnt--;		return;	} 	if (unlikely(name->name != name->iname))		kfree(name->name);	free_filename(name);}EXPORT_SYMBOL(putname); static inline int __delayed_getname(struct delayed_filename *v,			   const char __user *string, int flags){	v->__incomplete_filename = do_getname(string, flags, true);	return PTR_ERR_OR_ZERO(v->__incomplete_filename);} int delayed_getname(struct delayed_filename *v, const char __user *string){	return __delayed_getname(v, string, 0);} int delayed_getname_uflags(struct delayed_filename *v, const char __user *string,			 int uflags){	int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;	return __delayed_getname(v, string, flags);} int putname_to_delayed(struct delayed_filename *v, struct filename *name){	if (likely(name->refcnt == 1)) {		v->__incomplete_filename = name;		return 0;	}	name->refcnt--;	v->__incomplete_filename = do_getname_kernel(name->name, true);	return PTR_ERR_OR_ZERO(v->__incomplete_filename);} void dismiss_delayed_filename(struct delayed_filename *v){	putname(no_free_ptr(v->__incomplete_filename));} struct filename *complete_getname(struct delayed_filename *v){	struct filename *res = no_free_ptr(v->__incomplete_filename);	if (!IS_ERR(res))		audit_getname(res);	return res;} /** * check_acl - perform ACL permission checking * @idmap:	idmap of the mount the inode was found from * @inode:	inode to check permissions on * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...) * * This function performs the ACL permission checking. Since this function * retrieve POSIX acls it needs to know whether it is called from a blocking or * non-blocking context and thus cares about the MAY_NOT_BLOCK bit. * * If the inode has been found through an idmapped mount the idmap of * the vfsmount must be passed through @idmap. This function will then take * care to map the inode according to @idmap before checking permissions. * On non-idmapped mounts or if permission checking is to be performed on the * raw inode simply pass @nop_mnt_idmap. */static int check_acl(struct mnt_idmap *idmap,		     struct inode *inode, int mask){#ifdef CONFIG_FS_POSIX_ACL	struct posix_acl *acl; 	if (mask & MAY_NOT_BLOCK) {		acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);	        if (!acl)	                return -EAGAIN;		/* no ->get_inode_acl() calls in RCU mode... */		if (is_uncached_acl(acl))			return -ECHILD;	        return posix_acl_permission(idmap, inode, acl, mask);	} 	acl = get_inode_acl(inode, ACL_TYPE_ACCESS);	if (IS_ERR(acl))		return PTR_ERR(acl);	if (acl) {	        int error = posix_acl_permission(idmap, inode, acl, mask);	        posix_acl_release(acl);	        return error;	}#endif 	return -EAGAIN;} /* * Very quick optimistic "we know we have no ACL's" check. * * Note that this is purely for ACL_TYPE_ACCESS, and purely * for the "we have cached that there are no ACLs" case. * * If this returns true, we know there are no ACLs. But if * it returns false, we might still not have ACLs (it could * be the is_uncached_acl() case). */static inline bool no_acl_inode(struct inode *inode){#ifdef CONFIG_FS_POSIX_ACL	return likely(!READ_ONCE(inode->i_acl));#else	return true;#endif} /** * acl_permission_check - perform basic UNIX permission checking * @idmap:	idmap of the mount the inode was found from * @inode:	inode to check permissions on * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...) * * This function performs the basic UNIX permission checking. Since this * function may retrieve POSIX acls it needs to know whether it is called from a * blocking or non-blocking context and thus cares about the MAY_NOT_BLOCK bit. * * If the inode has been found through an idmapped mount the idmap of * the vfsmount must be passed through @idmap. This function will then take * care to map the inode according to @idmap before checking permissions. * On non-idmapped mounts or if permission checking is to be performed on the * raw inode simply pass @nop_mnt_idmap. */static int acl_permission_check(struct mnt_idmap *idmap,				struct inode *inode, int mask){	unsigned int mode = inode->i_mode;	vfsuid_t vfsuid; 	/*	 * Common cheap case: everybody has the requested	 * rights, and there are no ACLs to check. No need	 * to do any owner/group checks in that case.	 *	 *  - 'mask&7' is the requested permission bit set	 *  - multiplying by 0111 spreads them out to all of ugo	 *  - '& ~mode' looks for missing inode permission bits	 *  - the '!' is for "no missing permissions"	 *	 * After that, we just need to check that there are no	 * ACL's on the inode - do the 'IS_POSIXACL()' check last	 * because it will dereference the ->i_sb pointer and we	 * want to avoid that if at all possible.	 */	if (!((mask & 7) * 0111 & ~mode)) {		if (no_acl_inode(inode))			return 0;		if (!IS_POSIXACL(inode))			return 0;	} 	/* Are we the owner? If so, ACL's don't matter */	vfsuid = i_uid_into_vfsuid(idmap, inode);	if (likely(vfsuid_eq_kuid(vfsuid, current_fsuid()))) {		mask &= 7;		mode >>= 6;		return (mask & ~mode) ? -EACCES : 0;	} 	/* Do we have ACL's? */	if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {		int error = check_acl(idmap, inode, mask);		if (error != -EAGAIN)			return error;	} 	/* Only RWX matters for group/other mode bits */	mask &= 7; 	/*	 * Are the group permissions different from	 * the other permissions in the bits we care	 * about? Need to check group ownership if so.	 */	if (mask & (mode ^ (mode >> 3))) {		vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);		if (vfsgid_in_group_p(vfsgid))			mode >>= 3;	} 	/* Bits in 'mode' clear that we require? */	return (mask & ~mode) ? -EACCES : 0;} /** * generic_permission -  check for access rights on a Posix-like filesystem

Lines 1–500 of 6581.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t