内核模块实验3 Linux内核open的分析

Linux内核open的分析

linux下一切皆文件,要实现设备的驱动的话表现给用户层的调用接口也是文件的形式,因此我们要对文件的操作有大致的了解。

当应用层调用int fd = open("/home/lx/a.txt", O_RDONLY); 时,会触发系统调用中断,从而陷入内核中。

open打开文件的流程

├ sys_open(fs/open.c)
├ ├── do_sys_open(fs/open.c)
├ ├── getname(fs/namei.c)
├ ├── get_unused_fd_flags(fs/file.c)
├ ├── do_filp_open(fs/namei.c)
├ ├───── ├─ path_openat(fs/namei.c)
├ ├──────── ├─ get_empty_filp(fs/file_table.c)
├ ├──────── ├─ link_path_walk(fs/namei.c)
├ ├──────── ├─ do_last(fs/namei.c)
├ ├──────────── ├─ lookup_fast(fs/namei.c)
├ ├──────────── ├─ lookup_open(fs/namei.c)
├ ├──────────────── ├─ lookup_dcache(fs/namei.c)
├ ├──────────────── ├─ lookup_real(fs/namei.c)
├ ├──────────────────── ├─ ext2_lookup(fs/ext2/inode.c)
├ ├──────────────────────── ├─ init_special_inode(fs/inode.c)
├ ├──────────────────────────── ├─ inode->i_fop = &def_chr_fops;
├ ├── fd_install(fs/namei.c)

从do_sys_open()函数开始分析,函数内容如下

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)  //lx 系统调用open进入 dfd 传入AT_FDCWD(-100)
{
    /* 函数传入参数如下:
    * dfd = -100 (AT_FDCWD)
    * filename = "/home/lx/a.txt"
    * flags = 00100000 (0x8000 ) (O_RDONLY | O_LARGEFILE);
    * mode = 0
    */
	struct open_flags op;
	int fd = build_open_flags(flags, mode, &op);  //lx 判断flags 和mode 参数的合法性,不合法fd为-EINVAL(-23) 合法赋值到op结构体中
	struct filename *tmp;

	if (fd)
		return fd;

	tmp = getname(filename);  //lx 将文件名从用户空间拷贝到内核空间
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	fd = get_unused_fd_flags(flags); //lx 获取一个未使用的文件描述符
	if (fd >= 0) {
		struct file *f = do_filp_open(dfd, tmp, &op);  // lx 构造一个struct file 的结构体,并初始化里边的成员
		if (IS_ERR(f)) {
			put_unused_fd(fd);
			fd = PTR_ERR(f);
		} else {
			fsnotify_open(f);
			fd_install(fd, f);
		}
	}
	putname(tmp);
	return fd;
}
  • 首先从 build_open_flags 开始分析
    函数源码如下,函数位于 fs/open.c
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
{
    /* 函数传入参数如下:
    * flags = 00100000 (0x8000 ) (O_RDONLY | O_LARGEFILE);
    * mode = 0
    * struct open_flags *op
    */
	int lookup_flags = 0;
	int acc_mode = ACC_MODE(flags);  //lx 0 = "\000\004\002\006"[(0x8000 )& 0003]

	/*
	 * Clear out all open flags we don't know about so that we don't report
	 * them in fcntl(F_GETFD) or similar interfaces.
	 */
	flags &= VALID_OPEN_FLAGS;            //清除未开放的标志

	if (flags & (O_CREAT | __O_TMPFILE))
		op->mode = (mode & S_IALLUGO) | S_IFREG;
	else
		op->mode = 0;

	/* Must never be set by userspace */
	flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;

	/*
	 * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
	 * check for O_DSYNC if the need any syncing at all we enforce it's
	 * always set instead of having to deal with possibly weird behaviour
	 * for malicious applications setting only __O_SYNC.
	 */
	if (flags & __O_SYNC)
		flags |= O_DSYNC;

	if (flags & __O_TMPFILE) {
		if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
			return -EINVAL;
		if (!(acc_mode & MAY_WRITE))
			return -EINVAL;
	} else if (flags & O_PATH) {
		/*
		 * If we have O_PATH in the open flag. Then we
		 * cannot have anything other than the below set of flags
		 */
		flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
		acc_mode = 0;
	}

	op->open_flag = flags;

	/* O_TRUNC implies we need access checks for write permissions */
	if (flags & O_TRUNC)
		acc_mode |= MAY_WRITE;

	/* Allow the LSM permission hook to distinguish append
	   access from general write access. */
	if (flags & O_APPEND)
		acc_mode |= MAY_APPEND;

	op->acc_mode = acc_mode;

	op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;

	if (flags & O_CREAT) {
		op->intent |= LOOKUP_CREATE;
		if (flags & O_EXCL)
			op->intent |= LOOKUP_EXCL;
	}

	if (flags & O_DIRECTORY)
		lookup_flags |= LOOKUP_DIRECTORY;
	if (!(flags & O_NOFOLLOW))
		lookup_flags |= LOOKUP_FOLLOW;
	op->lookup_flags = lookup_flags;
	return 0;
}
  • getname()函数
    该函数主要实现将文件名从用户空间拷贝到内核空间
getname()源码,位于fs/namei.c

#define EMBEDDED_NAME_MAX	(PATH_MAX - offsetof(struct filename, iname))

struct filename *
getname_flags(const char __user *filename, int flags, int *empty)
{
	struct filename *result;
	char *kname;
	int len;
	BUILD_BUG_ON(offsetof(struct filename, iname) % sizeof(long) != 0);

	result = audit_reusename(filename);
	if (result)
		return result;

	result = __getname();
	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);
	if (unlikely(len < 0)) {
		__putname(result);
		return ERR_PTR(len);
	}

	/*
	 * 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)) {
		const size_t size = offsetof(struct filename, iname[1]);
		kname = (char *)result;

		/*
		 * size is chosen that way we to guarantee that
		 * result->iname[0] is within the same object and that
		 * kname can't be equal to result->iname, no matter what.
		 */
		result = kzalloc(size, GFP_KERNEL);
		if (unlikely(!result)) {
			__putname(kname);
			return ERR_PTR(-ENOMEM);
		}
		result->name = kname;
		len = strncpy_from_user(kname, filename, PATH_MAX);
		if (unlikely(len < 0)) {
			__putname(kname);
			kfree(result);
			return ERR_PTR(len);
		}
		if (unlikely(len == PATH_MAX)) {
			__putname(kname);
			kfree(result);
			return ERR_PTR(-ENAMETOOLONG);
		}
	}

	result->refcnt = 1;
	/* The empty path is special. */
	if (unlikely(!len)) {
		if (empty)
			*empty = 1;
		if (!(flags & LOOKUP_EMPTY)) {
			putname(result);
			return ERR_PTR(-ENOENT);
		}
	}

	result->uptr = filename;
	result->aname = NULL;
	audit_getname(result);
	return result;
}

struct filename *
getname(const char __user * filename)
{
	return getname_flags(filename, 0, NULL);
}

posted @ 2022-08-28 21:57  lxblog  阅读(250)  评论(0编辑  收藏  举报