namei.c

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

C
1,434
字号
		}	}	return dentry;}/* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. */static inline int check_sticky(struct inode *dir, struct inode *inode){	if (!(dir->i_mode & S_ISVTX))		return 0;	if (inode->i_uid == current->fsuid)		return 0;	if (dir->i_uid == current->fsuid)		return 0;	return !capable(CAP_FOWNER);}/* *	Check whether we can remove a link victim from directory dir, check *  whether the type of victim is right. *  1. We can't do it if dir is read-only (done in permission()) *  2. We should have write and exec permissions on dir *  3. We can't remove anything from append-only dir *  4. We can't do anything with immutable dir (done in permission()) *  5. If the sticky bit on dir is set we should either *	a. be owner of dir, or *	b. be owner of victim, or *	c. have CAP_FOWNER capability *  6. If the victim is append-only or immutable we can't do antyhing with *     links pointing to it. *  7. If we were asked to remove a directory and victim isn't one - ENOTDIR. *  8. If we were asked to remove a non-directory and victim isn't one - EISDIR. *  9. We can't remove a root or mountpoint. */static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir){	int error;	if (!victim->d_inode || victim->d_parent->d_inode != dir)		return -ENOENT;	error = permission(dir,MAY_WRITE | MAY_EXEC);	if (error)		return error;	if (IS_APPEND(dir))		return -EPERM;	if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||	    IS_IMMUTABLE(victim->d_inode))		return -EPERM;	if (isdir) {		if (!S_ISDIR(victim->d_inode->i_mode))			return -ENOTDIR;		if (IS_ROOT(victim))			return -EBUSY;		if (victim->d_mounts != victim->d_covers)			return -EBUSY;	} else if (S_ISDIR(victim->d_inode->i_mode))		return -EISDIR;	return 0;}/*	Check whether we can create an object with dentry child in directory *  dir. *  1. We can't do it if child already exists (open has special treatment for *     this case, but since we are inlined it's OK) *  2. We can't do it if dir is read-only (done in permission()) *  3. We should have write and exec permissions on dir *  4. We can't do it if dir is immutable (done in permission()) */static inline int may_create(struct inode *dir, struct dentry *child) {	if (child->d_inode)		return -EEXIST;	return permission(dir,MAY_WRITE | MAY_EXEC);}static inline struct dentry *get_parent(struct dentry *dentry){	return dget(dentry->d_parent);}static inline void unlock_dir(struct dentry *dir){	up(&dir->d_inode->i_sem);	dput(dir);}/* * We need to do a check-parent every time * after we have locked the parent - to verify * that the parent is still our parent and * that we are still hashed onto it.. * * This is requied in case two processes race * on removing (or moving) the same entry: the * parent lock will serialize them, but the * other process will be too late.. */#define check_parent(dir, dentry) \	((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))/* * Locking the parent is needed to: *  - serialize directory operations *  - make sure the parent doesn't change from *    under us in the middle of an operation. * * NOTE! Right now we'd rather use a "struct inode" * for this, but as I expect things to move toward * using dentries instead for most things it is * probably better to start with the conceptually * better interface of relying on a path of dentries. */static inline struct dentry *lock_parent(struct dentry *dentry){	struct dentry *dir = dget(dentry->d_parent);	down(&dir->d_inode->i_sem);	return dir;}/* * Whee.. Deadlock country. Happily there are only two VFS * operations that do this.. */static inline void double_lock(struct dentry *d1, struct dentry *d2){	struct semaphore *s1 = &d1->d_inode->i_sem;	struct semaphore *s2 = &d2->d_inode->i_sem;	if (s1 != s2) {		if ((unsigned long) s1 < (unsigned long) s2) {			struct semaphore *tmp = s2;			s2 = s1; s1 = tmp;		}		down(s1);	}	down(s2);}static inline void double_unlock(struct dentry *d1, struct dentry *d2){	struct semaphore *s1 = &d1->d_inode->i_sem;	struct semaphore *s2 = &d2->d_inode->i_sem;	up(s1);	if (s1 != s2)		up(s2);	dput(d1);	dput(d2);}/*  * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security * reasons. * * O_DIRECTORY translates into forcing a directory lookup. */static inline int lookup_flags(unsigned int f){	unsigned long retval = LOOKUP_FOLLOW;	if (f & O_NOFOLLOW)		retval &= ~LOOKUP_FOLLOW;		if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))		retval &= ~LOOKUP_FOLLOW;		if (f & O_DIRECTORY)		retval |= LOOKUP_DIRECTORY;		return retval;}/* *	open_namei() * * namei for open - this is in fact almost the whole open-routine. * * 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). */struct dentry * open_namei(const char * pathname, int flag, int mode){	int acc_mode, error;	struct inode *inode;	struct dentry *dentry;	mode &= S_IALLUGO & ~current->fs->umask;	mode |= S_IFREG;	dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));	if (IS_ERR(dentry))		return dentry;	acc_mode = ACC_MODE(flag);	if (flag & O_CREAT) {		struct dentry *dir;		if (dentry->d_inode) {			if (!(flag & O_EXCL))				goto nocreate;			error = -EEXIST;			goto exit;		}		dir = lock_parent(dentry);		if (!check_parent(dir, dentry)) {			/*			 * Really nasty race happened. What's the 			 * right error code? We had a dentry, but			 * before we could use it it was removed			 * by somebody else. We could just re-try			 * everything, I guess.			 *			 * ENOENT is definitely wrong.			 */			error = -ENOENT;			unlock_dir(dir);			goto exit;		}		/*		 * Somebody might have created the file while we		 * waited for the directory lock.. So we have to		 * re-do the existence test.		 */		if (dentry->d_inode) {			error = 0;			if (flag & O_EXCL)				error = -EEXIST;		} else if ((error = may_create(dir->d_inode, dentry)) == 0) {			if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)				error = -EACCES;			else {				DQUOT_INIT(dir->d_inode);				error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);				/* Don't check for write permission, don't truncate */				acc_mode = 0;				flag &= ~O_TRUNC;			}		}		unlock_dir(dir);		if (error)			goto exit;	}nocreate:	error = -ENOENT;	inode = dentry->d_inode;	if (!inode)		goto exit;	error = -ELOOP;	if (S_ISLNK(inode->i_mode))		goto exit;		error = -EISDIR;	if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))		goto exit;	error = permission(inode,acc_mode);	if (error)		goto exit;	/*	 * FIFO's, sockets and device files are special: they don't	 * actually live on the filesystem itself, and as such you	 * can write to them even if the filesystem is read-only.	 */	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)) {		error = -EACCES;		if (IS_NODEV(inode))			goto exit;		flag &= ~O_TRUNC;	} else {		error = -EROFS;		if (IS_RDONLY(inode) && (flag & 2))			goto exit;	}	/*	 * An append-only file must be opened in append mode for writing.	 */	error = -EPERM;	if (IS_APPEND(inode)) {		if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))			goto exit;		if (flag & O_TRUNC)			goto exit;	}	if (flag & O_TRUNC) {		error = get_write_access(inode);		if (error)			goto exit;		/*		 * Refuse to truncate files with mandatory locks held on them.		 */		error = locks_verify_locked(inode);		if (!error) {			DQUOT_INIT(inode);						error = do_truncate(dentry, 0);		}		put_write_access(inode);		if (error)			goto exit;	} else		if (flag & FMODE_WRITE)			DQUOT_INIT(inode);	return dentry;exit:	dput(dentry);	return ERR_PTR(error);}struct dentry * do_mknod(const char * filename, int mode, dev_t dev){	int error;	struct dentry *dir;	struct dentry *dentry, *retval;	mode &= ~current->fs->umask;	dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);	if (IS_ERR(dentry))		return dentry;	dir = lock_parent(dentry);	error = -ENOENT;	if (!check_parent(dir, dentry))		goto exit_lock;	error = may_create(dir->d_inode, dentry);	if (error)		goto exit_lock;	error = -EPERM;	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)		goto exit_lock;	DQUOT_INIT(dir->d_inode);	error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);exit_lock:	retval = ERR_PTR(error);	if (!error)		retval = dget(dentry);	unlock_dir(dir);	dput(dentry);	return retval;}asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev){	int error;	char * tmp;	lock_kernel();	error = -EPERM;	if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !capable(CAP_SYS_ADMIN)))		goto out;	error = -EINVAL;	switch (mode & S_IFMT) {	case 0:		mode |= S_IFREG;		break;	case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:		break;	default:		goto out;	}	tmp = getname(filename);	error = PTR_ERR(tmp);	if (!IS_ERR(tmp)) {		struct dentry * dentry = do_mknod(tmp,mode,dev);		putname(tmp);		error = PTR_ERR(dentry);		if (!IS_ERR(dentry)) {			dput(dentry);			error = 0;		}	}out:	unlock_kernel();	return error;}/* * Look out: this function may change a normal dentry * into a directory dentry (different size).. */#ifdef OSKIT       int do_mkdir(const char * pathname, int mode)#elsestatic inline int do_mkdir(const char * pathname, int mode)#endif{	int error;	struct dentry *dir;	struct dentry *dentry;	dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);	error = PTR_ERR(dentry);	if (IS_ERR(dentry))		goto exit;	/*	 * EEXIST is kind of a strange error code to	 * return, but basically if the dentry was moved	 * or unlinked while we locked the parent, we	 * do know that it _did_ exist before, and as	 * such it makes perfect sense.. In contrast,	 * ENOENT doesn't make sense for mkdir.	 */	dir = lock_parent(dentry);	error = -EEXIST;	if (!check_parent(dir, dentry))		goto exit_lock;	error = may_create(dir->d_inode, dentry);	if (error)		goto exit_lock;	error = -EPERM;	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir)		goto exit_lock;	DQUOT_INIT(dir->d_inode);	mode &= 0777 & ~current->fs->umask;	error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);exit_lock:	unlock_dir(dir);	dput(dentry);exit:	return error;}asmlinkage int sys_mkdir(const char * pathname, int mode){	int error;	char * tmp;	lock_kernel();	tmp = getname(pathname);	error = PTR_ERR(tmp);	if (!IS_ERR(tmp)) {		error = do_mkdir(tmp,mode);		putname(tmp);	}	unlock_kernel();	return error;}int vfs_rmdir(struct inode *dir, struct dentry *dentry){	int error;	error = may_delete(dir, dentry, 1);	if (error)		return error;	if (!dir->i_op || !dir->i_op->rmdir)		return -EPERM;	DQUOT_INIT(dir);

⌨️ 快捷键说明

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