<pre name="code" class="cpp">/*
* linux/fs/ext2/namei.c
*
* Rewrite to pagecache. Almost all code had been changed, so blame me
* if the things go wrong. Please, send bug reports to
* viro@parcelfarce.linux.theplanet.co.uk
*
* Stuff here is basically a glue between the VFS and generic UNIXish
* filesystem that keeps everything in pagecache. All knowledge of the
* directory layout is in fs/ext2/dir.c - it turned out to be easily separatable
* and it's easier to debug that way. In principle we might want to
* generalize that a bit and turn it into a library. Or not.
*
* The only non-static object here is ext2_dir_inode_operations.
*
* TODO: get rid of kmap() use, add readahead.
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/fs/minix/namei.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
#include <linux/pagemap.h>
#include <linux/quotaops.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
#include "xip.h"
static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
int err = ext2_add_link(dentry, inode); /*used in dir.c to link the inode with the dentry in page cache*/
if (!err) {
d_instantiate(dentry, inode);
unlock_new_inode(inode);
return 0;
}
inode_dec_link_count(inode);
unlock_new_inode(inode);
iput(inode);
return err;
}
/*
* Methods themselves.
*/
/*ext 2 do the lookup process for the name structure nd*/
static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
struct inode * inode;
ino_t ino;
if (dentry->d_name.len > EXT2_NAME_LEN) /*the dentry name is error*/
return ERR_PTR(-ENAMETOOLONG);
ino = ext2_inode_by_name(dir, &dentry->d_name); /*check if the dentry is in this dir*/
inode = NULL;
if (ino) { /*the dentry belong to this dir*/
inode = ext2_iget(dir->i_sb, ino); /*read out the inode according to address in ino*/
if (inode == ERR_PTR(-ESTALE)) {
ext2_error(dir->i_sb, __func__,
"deleted inode referenced: %lu",
(unsigned long) ino);
return ERR_PTR(-EIO);
}
}
/*
* one inode can have different dentry in page cache, one path search for one link will generate one
* dentry, they are linked by a_alias, now:
*将一个inode与一个负状态的dentry绑定,在这个函数内部,首先要判断inode是否为目录,
*如果是目录的话,调用__d_find_alias函数搜索一个别名dentry(注意,目录是没有硬链接的),
*如果找到(说明这个inode的dentry曾经被使用过,而后来dentry变为负状态,但里面的很多内容还可以利用),
*调用d_rehash更新hash值, 并调用d_move将这个别名dentry的内容移动需要的dentry上。
*如果__d_find_alias没有找到结果,说明这个inode没有对应的别名dentry,
*需要调用__d_instantiate、d_rehash将inode绑定到dentry上。
*如果inode是一个文件的话,直接使用d_add将inode与dentry绑定
*/
return d_splice_alias(inode, dentry);
}
struct dentry *ext2_get_parent(struct dentry *child)
{
struct qstr dotdot = {.name = "..", .len = 2};
unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot);
if (!ino)
return ERR_PTR(-ENOENT);
return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino)); /*return the unhased dentry*/
}
/*
* By the time this is called, we already have created
* the directory cache entry for the new file, but it
* is so far negative - it has no inode.
*
* If the create succeeds, we fill in the inode information
* with d_instantiate().
*/
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
{
struct inode *inode;
dquot_initialize(dir);
/*create the new inode for the dentry and insert to parent dir,return inode pointer*/
inode = ext2_new_inode(dir, mode, &dentry->d_name);
if (IS_ERR(inode))
return PTR_ERR(inode);
/*set the inode operation pointer as ext2 file inode operations*/
inode->i_op = &ext2_file_inode_operations;
if (ext2_use_xip(inode->i_sb)) {
inode->i_mapping->a_ops = &ext2_aops_xip;
inode->i_fop = &ext2_xip_file_operations;
} else if (test_opt(inode->i_sb, NOBH)) {
inode->i_mapping->a_ops = &ext2_nobh_aops;
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops;
inode->i_fop = &ext2_file_operations;
}
mark_inode_dirty(inode);
return ext2_add_nondir(dentry, inode); /*call the function to link the new created inode with dentry*/
}
/*make the inode for device file, only one inode for one device*/
static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
{
struct inode * inode;
int err;
if (!new_valid_dev(rdev)) /*check if the rdev is valid or not*/
return -EINVAL;
dquot_initialize(dir);
/*create the new inode for this device by the dentry*/
inode = ext2_new_inode (dir, mode, &dentry->d_name);
err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
init_special_inode(inode, inode->i_mode, rdev); /*specify the inode for this rdev*/
#ifdef CONFIG_EXT2_FS_XATTR
inode->i_op = &ext2_special_inode_operations;
#endif
mark_inode_dirty(inode); /*mark the new created inode dirty waiting be flushed*/
err = ext2_add_nondir(dentry, inode); /*link the inode with the dentry */
}
return err;
}
/*create the soft link*/
static int ext2_symlink (struct inode * dir, struct dentry * dentry,
const char * symname)
{
struct super_block * sb = dir->i_sb;
int err = -ENAMETOOLONG;
unsigned l = strlen(symname)+1;
struct inode * inode;
if (l > sb->s_blocksize)
goto out;
dquot_initialize(dir);
inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &dentry->d_name);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out;
if (l > sizeof (EXT2_I(inode)->i_data)) {
/* slow symlink and symname length larger than idata size*/
inode->i_op = &ext2_symlink_inode_operations;
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
inode->i_mapping->a_ops = &ext2_aops;
err = page_symlink(inode, symname, l); /*write the symlink to page cache*/
if (err)
goto out_fail;
} else {
/* fast symlink, data is the symname */
inode->i_op = &ext2_fast_symlink_inode_operations;
memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
inode->i_size = l-1;
}
mark_inode_dirty(inode); /*mark the inode page dirty waiting to be flushed*/
err = ext2_add_nondir(dentry, inode); /*link the new created inode with its dentry*/
out:
return err;
out_fail:
inode_dec_link_count(inode);
unlock_new_inode(inode);
iput (inode);
goto out;
}
/*this is for the dentry already has the inode, and add the link*/
static int ext2_link (struct dentry * old_dentry, struct inode * dir,
struct dentry *dentry)
{
struct inode *inode = old_dentry->d_inode;
int err;
if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;
dquot_initialize(dir);
inode->i_ctime = CURRENT_TIME_SEC;
inode_inc_link_count(inode); /*increase the link count*/
ihold(inode);
err = ext2_add_link(dentry, inode);
if (!err) {
d_instantiate(dentry, inode); /*if the link successes, return 0 and keep the link*/
return 0;
}
inode_dec_link_count(inode); /*if failed, decrease the count and put the inode, return error*/
iput(inode);
return err;
}
/*ext2 make dirctory in dir, it is function operation*/
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
int err = -EMLINK;
if (dir->i_nlink >= EXT2_LINK_MAX)
goto out; /*no link can be add to this dir so directly return error*/
dquot_initialize(dir);
inode_inc_link_count(dir);
inode = ext2_new_inode(dir, S_IFDIR | mode, &dentry->d_name); /*create the new inode*/
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_dir; /*creation failed*/
inode->i_op = &ext2_dir_inode_operations; /*specify the inode operations*/
inode->i_fop = &ext2_dir_operations; /*specify the directory operation for directory*/
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
inode->i_mapping->a_ops = &ext2_aops;
inode_inc_link_count(inode); /*increase the new created dirctory inode's link*/
err = ext2_make_empty(inode, dir); /*call the function in dir.c, make a new and empty directory*/
if (err)
goto out_fail;
err = ext2_add_link(dentry, inode); /*link the dentry with inode*/
if (err)
goto out_fail;
d_instantiate(dentry, inode);
unlock_new_inode(inode); /*in the creation, is inode is locked, and now released*/
out:
return err;
out_fail:
inode_dec_link_count(inode);
inode_dec_link_count(inode);
unlock_new_inode(inode);
iput(inode);
out_dir:
inode_dec_link_count(dir);
goto out;
}
static int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
struct ext2_dir_entry_2 * de;
struct page * page;
int err = -ENOENT;
dquot_initialize(dir);
de = ext2_find_entry (dir, &dentry->d_name, &page); /*find the dentry belong to the parent diretory dir*/
if (!de)
goto out;
err = ext2_delete_entry (de, page); /*delete the dentry in that page start*/
if (err)
goto out;
inode->i_ctime = dir->i_ctime;
inode_dec_link_count(inode); /*decrease the link count*/
err = 0;
out:
return err;
}
/*for remove the existing directory, just delete the dentry*/
static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
int err = -ENOTEMPTY;
if (ext2_empty_dir(inode)) {
err = ext2_unlink(dir, dentry);
/*if the directory is empty, remove and unlink it, if not return error*/
if (!err) {
inode->i_size = 0; /*set the inode correspoinding to is dentry size to 0*/
inode_dec_link_count(inode); /*decrease the inode link*/
inode_dec_link_count(dir); /*decrease the link directory*/
}
}
return err;
}
/*to rename it, a new dentry should be created*/
static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
struct inode * new_dir, struct dentry * new_dentry )
{
struct inode * old_inode = old_dentry->d_inode;
struct inode * new_inode = new_dentry->d_inode;
struct page * dir_page = NULL;
struct ext2_dir_entry_2 * dir_de = NULL;
struct page * old_page;
struct ext2_dir_entry_2 * old_de;
int err = -ENOENT;
dquot_initialize(old_dir);
dquot_initialize(new_dir);
old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page); /*find the old one in page cache*/
if (!old_de)
goto out; /*if no old one, fail to do it*/
if (S_ISDIR(old_inode->i_mode)) { /*check the mode if it is directory or not*/
err = -EIO;
dir_de = ext2_dotdot(old_inode, &dir_page); /*return the old dir page and its dentry*/
if (!dir_de) /*if the dentry is null, release the old page, do nothing*/
goto out_old;
}
if (new_inode) {
struct page *new_page;
struct ext2_dir_entry_2 *new_de;
err = -ENOTEMPTY;
if (dir_de && !ext2_empty_dir (new_inode)) /*if the new inode is empty dir, old dentry not null*/
goto out_dir; /*release the old one page*/
err = -ENOENT;
/*new inode is existing and find the entry in page for new one*/
new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page);
if (!new_de)
goto out_dir;
ext2_set_link(new_dir, new_de, new_page, old_inode, 1); /*link the new dentry with its inode*/
new_inode->i_ctime = CURRENT_TIME_SEC;
if (dir_de)
drop_nlink(new_inode);
inode_dec_link_count(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= EXT2_LINK_MAX)
goto out_dir;
}
err = ext2_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
if (dir_de)
inode_inc_link_count(new_dir);
}
/*
* Like most other Unix systems, set the ctime for inodes on a
* rename.
*/
old_inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(old_inode); /*if new dentry are linked with the old inode, mark it dirty(name changed)*/
ext2_delete_entry (old_de, old_page); /*release the old dentry because the new one is already replace it*/
if (dir_de) {
if (old_dir != new_dir)
ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0);
else {
kunmap(dir_page);
page_cache_release(dir_page);
}
inode_dec_link_count(old_dir);
}
return 0;
out_dir:
if (dir_de) {
kunmap(dir_page);
page_cache_release(dir_page);
}
out_old:
kunmap(old_page);
page_cache_release(old_page);
out:
return err;
}
const struct inode_operations ext2_dir_inode_operations = {
.create = ext2_create,
.lookup = ext2_lookup,
.link = ext2_link,
.unlink = ext2_unlink,
.symlink = ext2_symlink,
.mkdir = ext2_mkdir,
.rmdir = ext2_rmdir,
.mknod = ext2_mknod,
.rename = ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
};
const struct inode_operations ext2_special_inode_operations = {
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
};
EXT2 file system namei.c with my comments
最新推荐文章于 2016-08-01 15:13:15 发布