namei.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,434 行 · 第 1/3 页

C
1,434
字号
/* *  linux/fs/namei.c * *  Copyright (C) 1991, 1992  Linus Torvalds * *  OSKit support added by the University of Utah, 1997 *//* * Some corrections by tytso. *//* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname * lookup logic. */#include <linux/mm.h>#include <linux/proc_fs.h>#include <linux/smp_lock.h>#include <linux/quotaops.h>#include <asm/uaccess.h>#include <asm/unaligned.h>#include <asm/semaphore.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/namei.h>/* This can be removed after the beta phase. */#define CACHE_SUPERVISE	/* debug the correctness of dcache entries */#undef DEBUG		/* some other debugging */#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])#ifndef OSKIT/* [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-existant 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. *//* 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). */static inline int do_getname(const char *filename, char *page){	int retval;	unsigned long len = PAGE_SIZE;	if ((unsigned long) filename >= TASK_SIZE) {		if (!segment_eq(get_fs(), KERNEL_DS))			return -EFAULT;	} else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE)		len = TASK_SIZE - (unsigned long) filename;	retval = strncpy_from_user((char *)page, filename, len);	if (retval > 0) {		if (retval < len)			return 0;		return -ENAMETOOLONG;	} else if (!retval)		retval = -ENOENT;	return retval;}#endif /* OSKIT */char * getname(const char * filename){#ifdef OSKIT	return (char *)filename;#else	char *tmp, *result;	result = ERR_PTR(-ENOMEM);	tmp = __getname();	if (tmp)  {		int retval = do_getname(filename, tmp);		result = tmp;		if (retval < 0) {			putname(tmp);			result = ERR_PTR(retval);		}	}	return result;#endif /* OSKIT */}/* *	permission() * * is used to check for read/write/execute permissions on a file. * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things.. */int permission(struct inode * inode,int mask){	int mode = inode->i_mode;	if (inode->i_op && inode->i_op->permission)		return inode->i_op->permission(inode, mask);	else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&		 (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))		return -EROFS; /* Nobody gets write access to a read-only fs */	else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))		return -EACCES; /* Nobody gets write access to an immutable file */	else if (current->fsuid == inode->i_uid)		mode >>= 6;	else if (in_group_p(inode->i_gid))		mode >>= 3;	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))		return 0;	/* read and search access */	if ((mask == S_IROTH) ||	    (S_ISDIR(mode)  && !(mask & ~(S_IROTH | S_IXOTH))))		if (capable(CAP_DAC_READ_SEARCH))			return 0;	return -EACCES;}/* * get_write_access() gets write permission for a file. * put_write_access() releases this write permission. * This is used for regular files. * We cannot support write (and maybe mmap read-write shared) accesses and * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode * can have the following values: * 0: no writers, no VM_DENYWRITE mappings * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist * > 0: (i_writecount) users are writing to the file. */int get_write_access(struct inode * inode){#ifdef OSKIT	/* This doesn't need to do anything since i_writecount is only	   so exec can do ETXTBSY. */#else	if (inode->i_writecount < 0)		return -ETXTBSY;	inode->i_writecount++;#endif /* OSKIT */	return 0;}void put_write_access(struct inode * inode){#ifdef OSKIT	/* This doesn't need to do anything since i_writecount is only	   so exec can do ETXTBSY. */#else	inode->i_writecount--;#endif}/* * "." and ".." are special - ".." especially so because it has to be able * to know about the current root directory and parent relationships */static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name){	struct dentry *result = NULL;	if (name->name[0] == '.') {		switch (name->len) {		default:			break;		case 2:				if (name->name[1] != '.')				break;			if (parent != current->fs->root)				parent = parent->d_covers->d_parent;			/* fallthrough */		case 1:			result = parent;		}	}	return dget(result);}/* * Internal lookup() using the new generic dcache. */static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags){	struct dentry * dentry = d_lookup(parent, name);	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {		if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {			dput(dentry);			dentry = NULL;		}	}	return dentry;}/* * This is called when everything else fails, and we actually have * to go to the low-level filesystem to find out what we should do.. * * We get the directory semaphore, and after getting that we also * make sure that nobody added the entry to the dcache in the meantime.. */static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags){	struct dentry * result;	struct inode *dir = parent->d_inode;	down(&dir->i_sem);	/*	 * First re-do the cached lookup just in case it was created	 * while we waited for the directory semaphore..	 *	 * FIXME! This could use version numbering or similar to	 * avoid unnecessary cache lookups.	 */	result = cached_lookup(parent, name, flags);	if (!result) {		struct dentry * dentry = d_alloc(parent, name);		result = ERR_PTR(-ENOMEM);		if (dentry) {			result = dir->i_op->lookup(dir, dentry);			if (result)				dput(dentry);			else				result = dentry;		}	}	up(&dir->i_sem);	return result;}static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow){	struct inode * inode = dentry->d_inode;	if ((follow & LOOKUP_FOLLOW)	    && inode && inode->i_op && inode->i_op->follow_link) {		if (current->link_count < 5) {			struct dentry * result;			current->link_count++;			/* This eats the base */			result = inode->i_op->follow_link(dentry, base, follow);			current->link_count--;			dput(dentry);			return result;		}		dput(dentry);		dentry = ERR_PTR(-ELOOP);	}	dput(base);	return dentry;}static inline struct dentry * follow_mount(struct dentry * dentry){	struct dentry * mnt = dentry->d_mounts;	if (mnt != dentry) {		dget(mnt);		dput(dentry);		dentry = mnt;	}	return dentry;}/* * Name resolution. * * This is the basic name resolution function, turning a pathname * into the final dentry. */struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags){	struct dentry * dentry;	struct inode *inode;	if (*name == '/') {		if (base)			dput(base);		do {			name++;		} while (*name == '/');		__prefix_lookup_dentry(name, lookup_flags);		base = dget(current->fs->root);	} else if (!base) {		base = dget(current->fs->pwd);	}	if (!*name)		goto return_base;	inode = base->d_inode;	lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK;	/* At this point we know we have a real path component. */	for(;;) {		int err;		unsigned long hash;		struct qstr this;		unsigned int flags;		unsigned int c;		err = permission(inode, MAY_EXEC);		dentry = ERR_PTR(err); 		if (err)			break;		this.name = name;		c = *(const unsigned char *)name;		hash = init_name_hash();		do {			name++;			hash = partial_name_hash(c, hash);			c = *(const unsigned char *)name;		} while (c && (c != '/'));		this.len = name - (const char *) this.name;		this.hash = end_name_hash(hash);		/* remove trailing slashes? */		flags = lookup_flags;		if (c) {			char tmp;			flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;			do {				tmp = *++name;			} while (tmp == '/');			if (tmp)				flags |= LOOKUP_CONTINUE;		}		/*		 * See if the low-level filesystem might want		 * to use its own hash..		 */		if (base->d_op && base->d_op->d_hash) {			int error;			error = base->d_op->d_hash(base, &this);			if (error < 0) {				dentry = ERR_PTR(error);				break;			}		}		/* This does the actual lookups.. */		dentry = reserved_lookup(base, &this);		if (!dentry) {			dentry = cached_lookup(base, &this, flags);			if (!dentry) {				dentry = real_lookup(base, &this, flags);				if (IS_ERR(dentry))					break;			}		}		/* Check mountpoints.. */		dentry = follow_mount(dentry);		base = do_follow_link(base, dentry, flags);		if (IS_ERR(base))			goto return_base;		inode = base->d_inode;		if (flags & LOOKUP_DIRECTORY) {			if (!inode)				goto no_inode;			dentry = ERR_PTR(-ENOTDIR); 			if (!inode->i_op || !inode->i_op->lookup)				break;			if (flags & LOOKUP_CONTINUE)				continue;		}return_base:		return base;/* * The case of a nonexisting file is special. * * In the middle of a pathname lookup (ie when * LOOKUP_CONTINUE is set), it's an obvious * error and returns ENOENT. * * At the end of a pathname lookup it's legal, * and we return a negative dentry. However, we * get here only if there were trailing slashes, * which is legal only if we know it's supposed * to be a directory (ie "mkdir"). Thus the * LOOKUP_SLASHOK flag. */no_inode:		dentry = ERR_PTR(-ENOENT);		if (flags & LOOKUP_CONTINUE)			break;		if (flags & LOOKUP_SLASHOK)			goto return_base;		break;	}	dput(base);	return dentry;}/* *	namei() * * is used by most simple commands to get the inode of a specified name. * Open, link etc use their own routines, but this is enough for things * like 'chmod' etc. * * namei exists in two versions: namei/lnamei. The only difference is * that namei follows links, while lnamei does not. */struct dentry * __namei(const char *pathname, unsigned int lookup_flags){	char *name;	struct dentry *dentry;	name = getname(pathname);	dentry = (struct dentry *) name;	if (!IS_ERR(name)) {		dentry = lookup_dentry(name, NULL, lookup_flags);		putname(name);		if (!IS_ERR(dentry)) {			if (!dentry->d_inode) {				dput(dentry);				dentry = ERR_PTR(-ENOENT);			}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?