• 执行"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, "
    ", 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, "
    ", 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 /*" !!!

  • 相关阅读:
    OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别
    OpenGL FrameBufferCopy相关Api比较(glCopyPixels,glReadPixels,glCopyTexImage2D,glFramebufferTexture2D)
    OpenGL渲染流程
    GPU大百科全书索引(有助于理解openGL工作流程)
    二进制配置文件为什么比json等配置文件效率高
    游戏开发中的矩阵初探
    Objective-C 30分钟入门教程
    cocos2dx骨骼动画Armature源码分析(三)
    cocos2dx骨骼动画Armature源码分析(二)
    linux中df和du查看磁盘大小不一致解决方法(转载)
  • 原文地址:https://www.cnblogs.com/idorax/p/11199931.html
Copyright © 2020-2023  润新知