执行rm -rf /之后世界安静了吗

2024-03-24 21:20
文章标签 世界 执行 之后 rm rf 安静

本文主要是介绍执行rm -rf /之后世界安静了吗,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于Unix/Linux程序员来说,"rm -rf /"一直被认为是一个极度危险的操作,因为直接把根目录给删除了,整个操作系统也就崩溃了。但实际上会是这样的吗?呵呵,请看图:

啊哈,世界并没有安静,一如既往地喧嚣。怎么回事儿?让我们来扒一扒源代码,

01 - 下载源代码(coreutils-8.30)

root# cd /tmp
root# wget https://download.fedoraproject.org/pub/fedora/linux/updates/29/Everything/SRPMS/Packages/c/coreutils-8.30-7.fc29.src.rpm
root# mkdir /tmp/coreutils
root# rpm -ivh --define '_topdir /tmp/coreutils' coreutils-8.30-7.fc29.src.rpm
root# cd /tmp/coreutils/SOURCES
root# ls -l *.xz
-rw-rw-r--. 1 root root 5359532 Jul  2  2018 coreutils-8.30.tar.xz
root# tar Jxf coreutils-8.30.tar.xz

02 - 查看rm -rf /的运行轨迹

 1 root@intel-sharkbay-dh-02:/# strace rm -rf /
 2 execve("/usr/bin/rm", ["rm", "-rf", "/"], 0x7ffde6de8960 /* 43 vars */) = 0
 3 brk(NULL)                               = 0x558b0cb0f000
 4 ...<snip>...
 5 lstat("/", {st_mode=S_IFDIR|0555, st_size=224, ...}) = 0
 6 newfstatat(AT_FDCWD, "/", {st_mode=S_IFDIR|0555, st_size=224, ...}, AT_SYMLINK_NOFOLLOW) = 0
 7 openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
 8 ...<snip>...
 9 openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
10 write(2, "rm: ", 4rm: )                     = 4
11 write(2, "it is dangerous to operate recur"..., 45it is dangerous to operate recursively on '/') = 45
12 write(2, "\n", 1
13 )                       = 1
14 write(2, "rm: ", 4rm: )                     = 4
15 write(2, "use --no-preserve-root to overri"..., 48use --no-preserve-root to override this failsafe) = 48
16 write(2, "\n", 1
17 )                       = 1
18 lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
19 close(0)                                = 0
20 close(1)                                = 0
21 close(2)                                = 0
22 exit_group(1)                           = ?
23 +++ exited with 1 +++

注意第5行和11行:

 5 lstat("/", {st_mode=S_IFDIR|0555, st_size=224, ...}) = 0
11 write(2, "it is dangerous to operate recur"..., 45it is dangerous to operate recursively on '/') = 45

 

03 - 查看rm对应的源代码

03.01 main() in rm.c

/* coreutils-8.30/src/rm.c#209 */208  int
209  main (int argc, char **argv)
210  {
211    bool preserve_root = true;
...
229    while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
230      {
231        switch (c)
232          {
...
237          case 'f':
238            x.interactive = RMI_NEVER;
239            x.ignore_missing_files = true;
240            prompt_once = false;
241            break;
...
255          case 'r':
256          case 'R':
257            x.recursive = true;
258            break;
...
341      }
342
343    if (x.recursive && preserve_root)
344      {
345        static struct dev_ino dev_ino_buf;
346        x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
347        if (x.root_dev_ino == NULL)
348          die (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
349               quoteaf ("/"));
350      }
...
370    enum RM_status status = rm (file, &x);
371    assert (VALID_STATUS (status));
372    return status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS;
373  }

在第346行,调用了函数get_root_dev_info(), 用以获取 root设备信息。

346        x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);

其中,get_root_dev_info()实现如下:

/* coreutils-8.30/lib/root-dev-ino.c */25  /* Call lstat to get the device and inode numbers for '/'.
26     Upon failure, return NULL.  Otherwise, set the members of
27     *ROOT_D_I accordingly and return ROOT_D_I.  */
28  struct dev_ino *
29  get_root_dev_ino (struct dev_ino *root_d_i)
30  {
31    struct stat statbuf;
32    if (lstat ("/", &statbuf))
33      return NULL;
34    root_d_i->st_ino = statbuf.st_ino;
35    root_d_i->st_dev = statbuf.st_dev;
36    return root_d_i;
37  }

第32行调用了lstat(),  这和我们在前面看到的strace rm -rf /的输出正好对应。

 5 lstat("/", {st_mode=S_IFDIR|0555, st_size=224, ...}) = 0

rm.c的第370行调用了函数rm(), 这是我们接下来要重点分析的函数。

370    enum RM_status status = rm (file, &x);

03.02 rm() in remove.c

/* coreutils-8.30/src/remove.c#576 */574  /* Remove FILEs, honoring options specified via X.
575     Return RM_OK if successful.  */
576  enum RM_status
577  rm (char *const *file, struct rm_options const *x)
578  {
579    enum RM_status rm_status = RM_OK;
580
581    if (*file)
582      {
...
590        FTS *fts = xfts_open (file, bit_flags, NULL);
591
592        while (1)
593          {
...
596            ent = fts_read (fts);
...
607            enum RM_status s = rm_fts (fts, ent, x);
...
610            UPDATE_STATUS (rm_status, s);
611          }
...
618      }
619
620    return rm_status;
621  }

在第607行,函数rm()调用了函数rm_fts()。

03.03 rm_fts() in remove.c

/* coreutils-8.30/src/remove.c#418 */417  static enum RM_status
418  rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
419  {
420    switch (ent->fts_info)
421      {
...
438        /* Perform checks that can apply only for command-line arguments.  */
439        if (ent->fts_level == FTS_ROOTLEVEL)
440          {
...
454            /* POSIX also says:
455               If a command line argument resolves to "/" (and --preserve-root
456               is in effect -- default) diagnose and skip it.  */
457            if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp))
458              {
459                ROOT_DEV_INO_WARN (ent->fts_path);
460                fts_skip_tree (fts, ent);
461                return RM_ERROR;
462              }
...
571      }
572  }

在第457行和459行,分别调用了宏函数。其中,L457的宏是把当前操作目录与根(/)做比较。

457            if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp))

而L459是打印警告信息。

459                ROOT_DEV_INO_WARN (ent->fts_path);

03.04 ROOT_DEV_INO_WARN() in lib/root-dev-info.h

/* coreutils-8.30/lib/root-dev-ino.h#33 */33  # define ROOT_DEV_INO_WARN(Dirname)                                 \
34    do                                                                        \
35      {                                                                       \
36        if (STREQ (Dirname, "/"))                                             \
37          error (0, 0, _("it is dangerous to operate recursively on %s"),     \
38                 quoteaf (Dirname));                                  \
39        else                                                          \
40          error (0, 0,                                                        \
41                 _("it is dangerous to operate recursively on %s (same as %s)"), \
42                 quoteaf_n (0, Dirname), quoteaf_n (1, "/"));         \
43        error (0, 0, _("use --no-preserve-root to override this failsafe")); \
44      }                                                                       \
45    while (0)

第37行和43行打印的信息,和我们执行rm -rf /之后看到的信息完全一致。

root@intel-sharkbay-dh-02:/# rm -rf /
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe

03.05 ROOT_DEV_INO_CHECK() in lib/root-dev-info.h

/* coreutils-8.30/lib/root-dev-ino.h#30 */30  # define ROOT_DEV_INO_CHECK(Root_dev_ino, Dir_statbuf) \
31      (Root_dev_ino && SAME_INODE (*Dir_statbuf, *Root_dev_ino))

宏SAME_INODE的实现如下:

/* coreutils-8.30/lib/same-inode.h#42 */42  #  define SAME_INODE(a, b)    \
43      ((a).st_ino == (b).st_ino \
44       && (a).st_dev == (b).st_dev)

到此为止,我们就完全看明白了为什么"rm -rf /"会被拒绝执行,因为现代版的rm命令已经已经针对野蛮的"rm -rf /"行为做了安全性检查。

结论:

  • 在现代的Unix/Linux系统中,"rm -rf /"不再危险,因为rm命令本身做了相应的安全检查。也就是说,不但"rm -rf /"不危险,而且"rm -rf //"和"rm -rf /tmp/../"之类的也不危险。

  • 虽然"rm -rf /"已经不再危险,但是"rm -rf /*"还是很危险滴,因为通配符'*'的存在。如好奇,请在你的虚拟机中予以尝试 。但是,本人强烈不推荐这么做! 否则,如下类似的输出就会泛洪于你的终端之上,即使你立即按CTRL+C也于事无补,至少常见的系统命令诸如ls, rm是立马不见了。。。
root@hp-moonshot-03-c31:/# id -un
root
root@hp-moonshot-03-c31:/# rm -rf /*
rm: cannot remove '/boot/efi': Device or resource busy
rm: cannot remove '/dev/mqueue': Device or resource busy
rm: cannot remove '/dev/hugepages': Device or resource busy
rm: cannot remove '/dev/pts/1': Operation not permitted
...<snip>...
rm: cannot remove '/proc/63/mountstats': Operation not permitted
rm: cannot remove '/proc/63/clear_refs': Operation not permitted
^C
root@hp-moonshot-03-c31:/# 
root@hp-moonshot-03-c31:/# ls
bash: /usr/bin/ls: No such file or directory

 

因为

root# rm -rf /*
等价于
root# rm -rf /bin /boot /dev /etc /home /lib /lib64 /lost+found /media /misc /mnt /net /nfs /opt /proc /root /run /sbin /srv /sys /tmp /usr /var

所以

!!! NEVER TRY "rm -rf /*" !!!

转载于:https://www.cnblogs.com/idorax/p/11199931.html

这篇关于执行rm -rf /之后世界安静了吗的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/842820

相关文章

揭秘世界上那些同时横跨两大洲的国家

我们在《世界人口过亿的一级行政区分布》盘点全球是那些人口过亿的一级行政区。 现在我们介绍五个横跨两州的国家,并整理七大洲和这些国家的KML矢量数据分析分享给大家,如果你需要这些数据,请在文末查看领取方式。 世界上横跨两大洲的国家 地球被分为七个大洲分别是亚洲、欧洲、北美洲、南美洲、非洲、大洋洲和南极洲。 七大洲示意图 其中,南极洲是无人居住的大陆,而其他六个大洲则孕育了众多国家和

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

Smarty模板执行原理

为了实现程序的业务逻辑和内容表现页面的分离从而提高开发速度,php 引入了模板引擎的概念,php 模板引擎里面最流行的可以说是smarty了,smarty因其功能强大而且速度快而被广大php web开发者所认可。本文将记录一下smarty模板引擎的工作执行原理,算是加深一下理解。 其实所有的模板引擎的工作原理是差不多的,无非就是在php程序里面用正则匹配将模板里面的标签替换为php代码从而将两者

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

Java程序到CPU上执行 的步骤

相信很多的小伙伴在最初学习编程的时候会容易产生一个疑惑❓,那就是编写的Java代码究竟是怎么一步一步到CPU上去执行的呢?CPU又是如何执行的呢?今天跟随小编的脚步去化解开这个疑惑❓。 在学习这个过程之前,我们需要先讲解一些与本内容相关的知识点 指令 指令是指导CPU运行的命令,主要由操作码+被操作数组成。 其中操作码用来表示要做什么动作,被操作数是本条指令要操作的数据,可能是内存地址,也

Android下执行linux命令

最近在开发过程中 使用了几个命令来对   手机的文件的权限进行修改;现在记录一下: 用到的方法: 1:判断是否有Root权限;  /**      * 判断当前手机是否有ROOT权限      * @return      */     public static boolean isRoot(){         boolean bool = false;         try{

简单的Q-learning|小明的一维世界(3)

简单的Q-learning|小明的一维世界(1) 简单的Q-learning|小明的一维世界(2) 一维的加速度世界 这个世界,小明只能控制自己的加速度,并且只能对加速度进行如下三种操作:增加1、减少1、或者不变。所以行动空间为: { u 1 = − 1 , u 2 = 0 , u 3 = 1 } \{u_1=-1, u_2=0, u_3=1\} {u1​=−1,u2​=0,u3​=1}

简单的Q-learning|小明的一维世界(2)

上篇介绍了小明的一维世界模型 、Q-learning的状态空间、行动空间、奖励函数、Q-table、Q table更新公式、以及从Q值导出策略的公式等。最后给出最简单的一维位置世界的Q-learning例子,从给出其状态空间、行动空间、以及稠密与稀疏两种奖励函数的设置方式。下面将继续深入,GO! 一维的速度世界 这个世界,小明只能控制自己的速度,并且只能对速度进行如下三种操作:增加1、减