系统rm的处理机制

来源:互联网 发布:通过网络赚钱的方法 编辑:程序博客网 时间:2024/06/05 20:34

在linux下,常常可以执行rm -rf /home/tmp/test这样的命令删除一个目录,或是使用其他的参数删除一个文件或目录,纠结在系统内部,这些删除命令是如何处理的呢?

      这些命令其实是由系统提供的可执行程序实现的,而这些程序调用了库函数或是直接调用了系统调用函数,主要的相关的系统调用有两个,下面先介绍这些相关的系统调用函数:

  • asmlinkage long sys_unlink(const char __user *pathname);

             < do_unlinkat

                   < vfs_unlink:检查删除的条目是否是一个挂载点,如果是则返回EBUSY的错误;否则删除该inode,如果这个条目是一个私有条目,则调用安全删除机制将其删除,否则直接调用文件系统的inode删除接口删除;

                                < security_inode_unlink:安全删除,如果是一个非私有数据,则返回0;

                                   dir->i_op->unlink:调用具体文件系统的删除接口;

  

 

  • asmlinkage long sys_rmdir(const char __user *pathname);

              < do_rmdir:查找路径,然后判断删除目录的类型,对目录上锁,找到相应的dentry,调用vfs层的目录删除接口

                       < vfs_rmdir:对目录项上锁,判断删除的目录是否为挂载点,如果是挂载点则返回EBUSY错误;否则,判断删除的inode是否为私有节点,如果是的话则使用安全删除机制删除,返回直接调用文件系统的删除命令;

                                < security_inode_rmdir:

                                    dir->i_op->rmdir:

现在重点介绍块inode中的元数据操作

:const struct inode_operations ext3_dir_inode_operations = {.create = ext3_create,.lookup = ext3_lookup,.link = ext3_link,.unlink = ext3_unlink,.symlink = ext3_symlink,.mkdir = ext3_mkdir,.rmdir = ext3_rmdir,.mknod = ext3_mknod,.rename = ext3_rename,.setattr = ext3_setattr,#ifdef CONFIG_EXT3_FS_XATTR.setxattr = generic_setxattr,.getxattr = generic_getxattr,.listxattr = ext3_listxattr,.removexattr = generic_removexattr,#endif.permission = ext3_permission,};static int ext3_unlink(struct inode * dir, struct dentry *dentry){int retval;struct inode * inode;struct buffer_head * bh;struct ext3_dir_entry_2 * de;handle_t *handle;/* Initialize quotas before so that eventual writes go * in separate transaction */DQUOT_INIT(dentry->d_inode);//申请一个原子操作handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));if (IS_ERR(handle))return PTR_ERR(handle);if (IS_DIRSYNC(dir))handle->h_sync = 1;retval = -ENOENT;//找到inode对应的buffer headbh = ext3_find_entry (dentry, &de);if (!bh)goto end_unlink;inode = dentry->d_inode;retval = -EIO;if (le32_to_cpu(de->inode) != inode->i_ino)goto end_unlink;if (!inode->i_nlink) {//inode中的i_nlink字段为0,表示删除的目标文件不存在ext3_warning (inode->i_sb, "ext3_unlink",      "Deleting nonexistent file (%lu), %d",      inode->i_ino, inode->i_nlink);inode->i_nlink = 1;}//通过将目标目录下与前一个目录下合并,删除目标目录项retval = ext3_delete_entry(handle, dir, de, bh);if (retval)goto end_unlink;dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;ext3_update_dx_flag(dir);ext3_mark_inode_dirty(handle, dir);drop_nlink(inode);if (!inode->i_nlink)//如果inode的i_nlink为0,则认为当前节点为孤儿节点,加入到super block中的孤儿节点链表中ext3_orphan_add(handle, inode);inode->i_ctime = dir->i_ctime;ext3_mark_inode_dirty(handle, inode);retval = 0;end_unlink:ext3_journal_stop(handle);brelse (bh);return retval;}static int ext3_rmdir (struct inode * dir, struct dentry *dentry){int retval;struct inode * inode;struct buffer_head * bh;struct ext3_dir_entry_2 * de;handle_t *handle;/* Initialize quotas before so that eventual writes go in * separate transaction */DQUOT_INIT(dentry->d_inode);handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));if (IS_ERR(handle))return PTR_ERR(handle);retval = -ENOENT;bh = ext3_find_entry (dentry, &de);if (!bh)goto end_rmdir;if (IS_DIRSYNC(dir))handle->h_sync = 1;inode = dentry->d_inode;retval = -EIO;if (le32_to_cpu(de->inode) != inode->i_ino)goto end_rmdir;retval = -ENOTEMPTY;//判断删除的目录是否是空目录,如果不为空目录,则//返回ENOTEMPTY的错误if (!empty_dir (inode))goto end_rmdir;//将删除的目标dentry与磁盘上的前一个dentry合并,//从而删除目标dentryretval = ext3_delete_entry(handle, dir, de, bh);if (retval)goto end_rmdir;if (inode->i_nlink != 2)ext3_warning (inode->i_sb, "ext3_rmdir",      "empty directory has nlink!=2 (%d)",      inode->i_nlink);inode->i_version++;//将i_nlink设置为0clear_nlink(inode);/* There's no need to set i_disksize: the fact that i_nlink is * zero will ensure that the right thing happens during any * recovery. */inode->i_size = 0;ext3_orphan_add(handle, inode);inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;ext3_mark_inode_dirty(handle, inode);drop_nlink(dir);ext3_update_dx_flag(dir);ext3_mark_inode_dirty(handle, dir);end_rmdir:ext3_journal_stop(handle);brelse (bh);return retval;}


接下来看看coreutils中具体的程序流程:与删除相关的源文件主要有

  • remove.h
  • remove.c
  • rm.c
  • rmdir.c

1.首先看remove.h文件,该文件定义了一些与删除操作相关的数据结构:

struct rm_options:定义了rm操作的参数,即上层应用指定的rm -r -f -i等之类的选项

enum RM_status:定义了删除操作的返回状态,即删除成功、出错或是目标目录非空等

 

2.接着重点看看remove.c文件,与rm相关的操作核心函数就在这个文件中

  • 数据结构
enum Prompt_action  {    PA_DESCEND_INTO_DIR = 2,   //进入到目录内部    PA_REMOVE_DIR              //删除目录  };
struct AD_ent   //在active directory堆栈中的一个条目,每个条目对应一个active directory{  Hash_table *unremovable;   //相应目录中,不能删除的条目的文件名(如.和..,以及调用删除操作失败的条目)  enum RM_status status;  union  {    struct dev_ino a; //指示上级目录,当调用chdir进入子目录后,再调用chdir ..时通过该参数回到上次访问的目录下    struct saved_cwd saved_cwd; //重构初始工作目录必须的信息,只有最底层条目才有该结构  } u;};
struct dirstack_state{  struct obstack dir_stack; //正在处理的目录的文件名,当用户进入子目录后,将在堆栈中压入一个新的条目,使用chdir时将堆栈栈顶的元素出栈  struct obstack len_stack; //目录名长度堆栈  struct obstack Active_dir; //active目录的栈,第一个条目是初始工作目录,当用户调用chdir时,将相应的条目压栈  struct cycle_check_state cycle_check_state;  jmp_buf current_arg_jumpbuf;};typedef struct dirstack_state Dirstack_state;




 

  • 处理函数

 

 

 

注意:ext3_rmdir只能删除一个空目录,如果目录非空,则函数将返回ENOTEMPTY的错误。无论是ext3_unlink还是ext3_rmdir函数,当目录为非空时,都不会变量该目录的子目录递归删除非空的目录,因此推测,rm -rf /hone/test这样的shell命令删除一个非空的目录,应该是由shell对命令进行解析和递归调用,传给系统的调用应该只能删除一个目录或是文件,而不能批量删除目录和文件。

参考资料:

http://www.ibm.com/developerworks/cn/linux/l-cn-usagecounter/index.html

原创粉丝点击