内核模块实验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);
}