rm删除命令源码分析

来源:互联网 发布:深入浅出数据分析视频 编辑:程序博客网 时间:2024/05/17 12:01

为什么看?

想要在删除文件前,先覆盖文件内容,防止他人恢复文件,从而得到文件原内容;并且需要支持rm命令原本的参数选项:

NAME
rm - remove files or directories
SYNOPSIS
rm [OPTION]... FILE...
Remove (unlink) the FILE(s).
-f, --force
ignore nonexistent files, never prompt
-i prompt before every removal
-I prompt once before removing more than three files, or when removing recursively. Less intru-
sive than -i, while still giving protection against most mistakes
......

想来还是直接修改rm源文件比较方便,因而查看rm命令的源码文件,在调用删除系统调用前加入覆盖文件内容的操作,从而安全删除文件,并且支持rm命令的参数选项。

怎么获取linux命令源码

可以通过三条命令获得命令的源代码网址

  • which rm //得到文件的绝对路径
  • rpm -qf 路径 //获取该命令所属的软件包名
  • rpm -qi 包名 //获取包信息,包含网址URL信息

如下:

[ty@ty ~]$ which rm
/bin/rm
[ty@ty ~]$ rpm -qf /bin/rm
coreutils-8.4-19.el6.x86_64
[ty@ty ~]$ rpm -qi coreutils
Name : coreutils Relocations: (not relocatable)
Version : 8.4 Vendor: Red Hat, Inc.
Release : 19.el6 Build Date: Tue 17 Apr 2012 06:14:13 AM PDT
Install Date: Sun 04 Aug 2013 11:48:59 AM PDT Build Host: hs20-bc2-3.build.redhat.com
Group : System Environment/Base Source RPM: coreutils-8.4-19.el6.src.rpm
Size : 12847030 License: GPLv3+
Signature : RSA/8, Thu 19 Apr 2012 10:59:38 PM PDT, Key ID 199e2f91fd431d51
Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
URL : http://www.gnu.org/software/coreutils/
Summary : A set of basic GNU tools commonly used in shell scripts
Description :
These are the GNU core utilities. This package is the combination of
the old GNU fileutils, sh-utils, and textutils packages.


rm.c主函数main

rm命令函数调用流程: 
man() -> rm() -> rm_fts() -> excise() -> unlinkat()

int main (int argc, char **argv)
{
bool preserve_root = true;
struct rm_options x;
bool prompt_once = false;
int c;
 
initialize_main (&argc, &argv);//初始化输入参数
set_program_name (argv[0]);//设置程序名
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
 
/* 程序正常结束前调用的close_stdin函数,关闭标准输入;
* 一个程序最多可以用atexit()注册32个处理函数,这些处理函数的
* 调用顺序与其注册顺序相反。即就是先注册的最后调用,最后调用的最先调用*/
atexit (close_stdin);
 
rm_option_init (&x); //初始化rm选项,即就是rm命令的参数
 
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir (); /*试图去禁用删除目录的功能,Try to remove priv from the effective set*/
 
while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{
switch (c)//switch语句主要是根据输入参数选项设置删除选项
{
case 'f':
x.interactive = RMI_NEVER;
x.ignore_missing_files = true;
prompt_once = false;
break;
 
case 'i':
x.interactive = RMI_ALWAYS;
x.ignore_missing_files = false;
prompt_once = false;
break;
case 'r':
case 'R':
x.recursive = true;
break; 
......
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
diagnose_leading_hyphen (argc, argv);
usage (EXIT_FAILURE);
}
}
 
if (argc <= optind) /*参数小于等于optind,即就是没有输入要删除的filename*/
{
if (x.ignore_missing_files) /*指定了-f选项时,直接退出,否则输出信息提示用户为指定操作对象*/
exit (EXIT_SUCCESS);
else
{
error (0, 0, _("missing operand"));
usage (EXIT_FAILURE);
}
}
 
if (x.recursive && preserve_root)
{
static struct dev_ino dev_ino_buf;
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (x.root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quote ("/"));
}
 
size_t n_files = argc - optind; /*n_files存储命令行指定操作对象个数,即就是要删除文件个数(目录视为一个)*/
char **file = argv + optind; /*file存储操作对象的索引*/
 
if (prompt_once && (x.recursive || 3 < n_files))
{
fprintf (stderr,
(x.recursive
? _("%s: remove all arguments recursively? ")
: _("%s: remove all arguments? ")),
program_name);
if (!yesno ())
exit (EXIT_SUCCESS);
}
 
enum RM_status status = rm (file, &x); /*删除文件,返回删除状态*/
assert (VALID_STATUS (status));
exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS);
}

rm()函数

/* Remove FILEs, honoring options specified via X.
Return RM_OK if successful. */
enum RM_status
rm (char *const *file, struct rm_options const *x)
{
enum RM_status rm_status = RM_OK;
 
if (*file)
{
int bit_flags = (FTS_CWDFD
| FTS_NOSTAT
| FTS_PHYSICAL);
 
if (x->one_file_system)
bit_flags |= FTS_XDEV;
 
FTS *fts = xfts_open (file, bit_flags, NULL);//创建并填充FTS结构体部分成员信息,会调用fts_open函数,失败返回失败的诊断信息
 
while (1)
{
FTSENT *ent;
 
ent = fts_read (fts); //填充FTSENT结构体成员信息
if (ent == NULL)
{
if (errno != 0)
{
error (0, errno, _("fts_read failed"));
rm_status = RM_ERROR;
}
break;
}
 
enum RM_status s = rm_fts (fts, ent, x);
 
assert (VALID_STATUS (s));
UPDATE_STATUS (rm_status, s);
}
 
if (fts_close (fts) != 0)
{
error (0, errno, _("fts_close failed"));
rm_status = RM_ERROR;
}
}
 
return rm_status;
}
rn_fts()函数根据FTSENT结构体成员信息,做一些处理,然后调用excise()函数执行删除操作;

excise函数调用unlinkat系统函数

 
/* Remove the file system object specified by ENT. IS_DIR specifies
whether it is expected to be a directory or non-directory.
Return RM_OK upon success, else RM_ERROR. */
static enum RM_status
excise (FTS *fts, FTSENT *ent, struct rm_options const *x, bool is_dir)
{
int flag = is_dir ? AT_REMOVEDIR : 0;
if (unlinkat (fts->fts_cwd_fd, ent->fts_accpath, flag) == 0)
{
/* 使用了-v选项时,输出详细删除信息,是目录输出目录名,是文件输出文件名,
输出的是当前目录到删除文件的完全路径(" `/tmp/aa' "或者"`test/bb'") */
if (x->verbose)
{
printf ((is_dir
? _("removed directory: %s\n")
: _("removed %s\n")), quote (ent->fts_path)); /*quote是将输出用(`')反引号单引号括起来*/
}
return RM_OK;
}
 
//下边为unlinkat函数执行失败,做的一些处理
/* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
nonexistent files. When the file is indeed missing, map that to ENOENT,
so that rm -f ignores it, as required. Even without -f, this is useful
because it makes rm print the more precise diagnostic. */
if (errno == EROFS)
{
struct stat st;
if ( ! (lstatat (fts->fts_cwd_fd, ent->fts_accpath, &st)
&& errno == ENOENT))
errno = EROFS;
}
 
if (ignorable_missing (x, errno))
return RM_OK;
 
/* When failing to rmdir an unreadable directory, the typical
errno value is EISDIR, but that is not as useful to the user
as the errno value from the failed open (probably EPERM).
Use the earlier, more descriptive errno value. */
if (ent->fts_info == FTS_DNR)
errno = ent->fts_errno;
error (0, errno, _("cannot remove %s"), quote (ent->fts_path));
mark_ancestor_dirs (ent);
return RM_ERROR;
}
添加的覆盖文件内容的操作就是添加在unlinkat函数前边,因为上几步已经做好了rm命令选项的操作,使用不同参数的不同操作,unlinkat函数是最终执行删除操作的:
int flag = is_dir ? AT_REMOVEDIR : 0;
if(flag == 0) //根据flag标志判断是文件还是目录,如果是文件才执行覆盖操作,否则不做任何操作
{
//覆盖操作
}
if (unlinkat (fts->fts_cwd_fd, ent->fts_accpath, flag) == 0)


0 0
原创粉丝点击