mkdir系统调用用来创建一个目录,对应的系统调用在fs/namei.c中
SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
{
return sys_mkdirat(AT_FDCWD, pathname, mode);
}
我里的AT_FDCWD我的理解是在进行路径查找初始化时如果路径不是绝对路径则设置为从当前的工作目录查找,看一下sys_mkdirat
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode)
{
int error = 0;
char * tmp;
struct dentry *dentry;
struct nameidata nd;
error = user_path_parent(dfd, pathname, &nd, &tmp);//获取父目录项的相关信息保存到nd结构
if (error)
goto out_err;
#ifdef DEBUG_ON_LIJJ
printk("mkdirat nd.last.name = %s.\n", nd.last.name);
#endif
dentry = lookup_create(&nd, 1);//创建目录项
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_unlock;
if (!IS_POSIXACL(nd.path.dentry->d_inode))
mode &= ~current_umask();
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
error = security_path_mkdir(&nd.path, dentry, mode);
if (error)
goto out_drop_write;
error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);//执行mkdir
out_drop_write:
mnt_drop_write(nd.path.mnt);
out_dput:
dput(dentry);
out_unlock:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
putname(tmp);
out_err:
return error;
}
这个函数开始查找要创建的目录,找到要创建目录的父目录项,并把要创建的目录的信息也保存在了nd结构中的last成员中,然后调用 lookup_create创建dentry结构。最后,调用vfs_mkdir执行具体的操作
看一下dentry结构的创建过程
/**
* lookup_create - lookup a dentry, creating it if it doesn't exist
* @nd: nameidata info
* @is_dir: directory flag
*
* Simple function to lookup and return a dentry and create it
* if it doesn't exist. Is SMP-safe.
*
* Returns with nd->path.dentry->d_inode->i_mutex locked.
*/
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
struct dentry *dentry = ERR_PTR(-EEXIST);
mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
/*
* Yucky last component or no last component at all?
* (foo/., foo/.., /)
*/
if (nd->last_type != LAST_NORM)
goto fail;
nd->flags &= ~LOOKUP_PARENT;
nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL;
nd->intent.open.flags = O_EXCL;
/*
* Do the final lookup.
*/
dentry = lookup_hash(nd);//执行查找,不存在则创建
if (IS_ERR(dentry))
goto fail;
if (dentry->d_inode)
goto eexist;
/*
* Special case - lookup gave negative, but... we had foo/bar/
* From the vfs_mknod() POV we just have a negative dentry -
* all is fine. Let's be bastards - you had / on the end, you've
* been asking for (non-existent) directory. -ENOENT for you.
*/
if (unlikely(!is_dir && nd->last.name[nd->last.len])) {
dput(dentry);
dentry = ERR_PTR(-ENOENT);
}
return dentry;
eexist:
dput(dentry);
dentry = ERR_PTR(-EEXIST);
fail:
return dentry;
}
这里进行一些初始化操作后调用lookup_hash查找相应的目录项,若不存在则创建
static struct dentry *lookup_hash(struct nameidata *nd)
{
int err;
err = exec_permission(nd->path.dentry->d_inode);
if (err)
return ERR_PTR(err);
return __lookup_hash(&nd->last, nd->path.dentry, nd);
}
这个函数比较简单,进行一些权限检查后调用__lookup_hash进行具体的操作
static struct dentry *__lookup_hash(struct qstr *name,
struct dentry *base, struct nameidata *nd)
{
struct dentry *dentry;
struct inode *inode;
int err;
inode = base->d_inode;
/*
* See if the low-level filesystem might want
* to use its own hash..
*/
if (base->d_op && base->d_op->d_hash) {//文件系统自己定义了hash函数
err = base->d_op->d_hash(base, name);
dentry = ERR_PTR(err);
if (err < 0)
goto out;
}
dentry = __d_lookup(base, name);//在base目录查找name
/* lockess __d_lookup may fail due to concurrent d_move()
* in some unrelated directory, so try with d_lookup
*/
if (!dentry)
dentry = d_lookup(base, name);
if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
dentry = do_revalidate(dentry, nd);
if (!dentry) {//创建dentry结构
struct dentry *new;
/* Don't create child dentry for a dead directory. */
dentry = ERR_PTR(-ENOENT);
if (IS_DEADDIR(inode))
goto out;
#ifdef DEBUG_ON_LIJJ
printk("__lookup_hash name->name = %s.\n", name->name);
#endif
new = d_alloc(base, name);
dentry = ERR_PTR(-ENOMEM);
if (!new)
goto out;
dentry = inode->i_op->lookup(inode, new, nd);
if (!dentry)
dentry = new;
else
dput(new);
}
out:
return dentry;
}
这里还是首先会查找相应的目录下是否已经存在该目录,不存在的话调用d_alloc分配该目录项
struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
{
struct dentry *dentry;
char *dname;
dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
if (!dentry)
return NULL;
if (name->len > DNAME_INLINE_LEN-1) {
dname = kmalloc(name->len + 1, GFP_KERNEL);
if (!dname) {
kmem_cache_free(dentry_cache, dentry);
return NULL;
}
} else {
dname = dentry->d_iname;
}
dentry->d_name.name = dname;
dentry->d_name.len = name->len;
dentry->d_name.hash = name->hash;
memcpy(dname, name->name, name->len);//名字
dname[name->len] = 0;
atomic_set(&dentry->d_count, 1);
dentry->d_flags = DCACHE_UNHASHED;
spin_lock_init(&dentry->d_lock);
dentry->d_inode = NULL;
dentry->d_parent = NULL;
dentry->d_sb = NULL;
dentry->d_op = NULL;
dentry->d_fsdata = NULL;
dentry->d_mounted = 0;
INIT_HLIST_NODE(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_lru);
INIT_LIST_HEAD(&dentry->d_subdirs);
INIT_LIST_HEAD(&dentry->d_alias);
if (parent) {//创建节点的parent
dentry->d_parent = dget(parent);
dentry->d_sb = parent->d_sb;
} else {
INIT_LIST_HEAD(&dentry->d_u.d_child);
}
spin_lock(&dcache_lock);
if (parent)//添加到父节点的子目录项
list_add(&dentry->d_u.d_child, &parent->d_subdirs);
dentry_stat.nr_dentry++;
spin_unlock(&dcache_lock);
return dentry;
}
回到mkdirat函数,继续vfs_mkdir
//dir 是父目录的inode结构,dentry是要建文件的dentry结构
int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int error = may_create(dir, dentry);//权限检查
if (error)
return error;
if (!dir->i_op->mkdir)
return -EPERM;
mode &= (S_IRWXUGO|S_ISVTX);
error = security_inode_mkdir(dir, dentry, mode);
if (error)
return error;
error = dir->i_op->mkdir(dir, dentry, mode);//调用父目录的目录创建函数
if (!error)
fsnotify_mkdir(dir, dentry);
return error;
}
vfs_mkdir也比较简单主要就是调用父目录的mkdir函数进行相应的操作
这里以在/dev目录下创建目录为例,这里查找到的父目录是"/",而我们在http://blog.csdn.net/new_abc/article/details/7650102讲解创建"/"的时候可以看出,对“/"目录的inode的赋值为:
inode->i_op = &ramfs_dir_inode_operations;
inode->i_fop = &dcache_dir_ops;
inode->i_sb = sb;
看一下ramfs_dir_inode_operations
static const struct inode_operations ramfs_dir_inode_operations = {
.create = ramfs_create,
.lookup = simple_lookup,
.link = simple_link,
.unlink = simple_unlink,
.symlink = ramfs_symlink,
.mkdir = ramfs_mkdir,
.rmdir = simple_rmdir,
.mknod = ramfs_mknod,
.rename = simple_rename,
};
所以这里的dir->i_op->mkdir函数也就是ramfs_mkdir
static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
int retval = ramfs_mknod(dir, dentry, mode | S_IFDIR, 0);//这里mode有一个S_IFDIR
if (!retval)
inc_nlink(dir);
return retval;
}
这个函数 比较简单,直接又调用了ramfs_mknod
static int
ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
struct inode * inode = ramfs_get_inode(dir->i_sb, dir, mode, dev);//获取一个inode
int error = -ENOSPC;
if (inode) {
d_instantiate(dentry, inode);//关联dentry和inode结构
dget(dentry); /* Extra count - pin the dentry in core */
error = 0;
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
}
return error;
}
再看一下ramfs_get_inode
struct inode *ramfs_get_inode(struct super_block *sb,
const struct inode *dir, int mode, dev_t dev)
{
struct inode * inode = new_inode(sb);//分配一个inode结构
if (inode) {
inode_init_owner(inode, dir, mode);//初始化这个i节点gid和uid
inode->i_mapping->a_ops = &ramfs_aops;
inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
mapping_set_unevictable(inode->i_mapping);
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {//这里为S_IFDIR
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_op = &ramfs_file_inode_operations;
inode->i_fop = &ramfs_file_operations;
break;
case S_IFDIR:
inode->i_op = &ramfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
break;
}
}
return inode;
}
这个函数主要是分配一个inode,然后对这个Inode进行了一些初始化操作,new_inode的分析可以参考 http://blog.csdn.net/new_abc/article/details/7650102,最重要的也就是赋值了i_op,i_fop这样以后对他下面文件进行操作提供了函数的指针
回到ramfs_mknod,inode创建成功后,调用d_instantiate对indode和前面创建的dentry结构进行关联,最终是通过__d_instantiate函数完成的.
static void __d_instantiate(struct dentry *dentry, struct inode *inode)
{
if (inode)
list_add(&dentry->d_alias, &inode->i_dentry);
dentry->d_inode = inode;
fsnotify_d_instantiate(dentry, inode);
}
这里把inode添加到dentry的d_alias链表,并将dentry的d_inode结构设置为d_inode.。
到这里mkdir主要工作也就完成了。这里总结一下mkdir主要做的工作:
1、首先创建了一个dentry结构,并将其与其父dentry关联起来
2、调用相应i节点的mkdir函数。
这里还要注意一点,我们在android启动过程分析--启动init进程这一节中可以看到,在启动Init进程的时候有一句
mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
在这之后,当我们在/dev目录下面创建新的文件或文件夹时,它的操作就变成了tmpfs里面相应的操作了,具体的可以参考 文件系统-- 标准路径名查找。