LCOV - code coverage report
Current view: top level - tests - catch_rm_r.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 123 136 90.4 %
Date: 2023-05-29 16:11:08 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2018-2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : /** \file
      20             :  * \brief Verify that the rm_r() function works.
      21             :  *
      22             :  * This file implements tests for the rm_r() function.
      23             :  */
      24             : 
      25             : // self
      26             : //
      27             : #include    <snapdev/rm_r.h>
      28             : 
      29             : #include    <snapdev/as_root.h>
      30             : #include    <snapdev/raii_generic_deleter.h>
      31             : #include    <snapdev/mkdir_p.h>
      32             : #include    <snapdev/pathinfo.h>
      33             : #include    <snapdev/version.h>
      34             : 
      35             : #include    "catch_main.h"
      36             : 
      37             : 
      38             : // C
      39             : //
      40             : #include    <sys/socket.h>
      41             : #include    <sys/sysmacros.h>
      42             : #include    <sys/un.h>
      43             : #include    <unistd.h>
      44             : 
      45             : 
      46             : // last include
      47             : //
      48             : #include    <snapdev/poison.h>
      49             : 
      50             : 
      51             : namespace
      52             : {
      53             : 
      54             : 
      55             : // gcc on 22.04 generates this warning thinking that an strncpy() will fail
      56             : // but according to the bug report it is not an issue
      57             : // https://github.com/eworm-de/mkinitcpio-ykfde/issues/25
      58             : // the warning was removed in future versions of gcc, unfortunately, we'll
      59             : // be on 22.04 for a while...
      60             : #if SNAPDEV_CHECK_GCC_VERSION(11, 3, 0)
      61             : #pragma GCC diagnostic push
      62             : #pragma GCC diagnostic ignored "-Wstringop-truncation"
      63             : #endif
      64           7 : void create_files(std::string const & folder, int depth, int max_depth, bool special_files, std::vector<int> & sockets)
      65             : {
      66           7 :     int r(0);
      67             : 
      68             :     // first create this directory (unless it exists, then leave it alone)
      69             :     //
      70           7 :     struct stat st;
      71           7 :     if(stat(folder.c_str(), &st) == 0)
      72             :     {
      73             :         // if it already exists and it is a directory, go on
      74             :         //
      75           0 :         CATCH_REQUIRE(S_ISDIR(st.st_mode));
      76             :     }
      77             :     else
      78             :     {
      79           7 :         r = mkdir(folder.c_str(), 0700);
      80           7 :         CATCH_REQUIRE(r == 0);
      81             :     }
      82             : 
      83             :     // second create a few text files in this folder
      84             :     //
      85           7 :     int const max(rand() % 5 + 1);
      86          24 :     for(int i(1); i <= max; ++i)
      87             :     {
      88          17 :         std::string filename(folder);
      89          17 :         filename += '/';
      90          17 :         filename += "file";
      91          17 :         filename += std::to_string(i);
      92          17 :         filename += ".txt";
      93             : 
      94          17 :         std::ofstream out(filename);
      95          17 :         out << SNAP_CATCH2_NAMESPACE::random_string(10, 100);
      96          17 :         CATCH_REQUIRE(!!out);
      97          17 :     }
      98             : 
      99           7 :     if(special_files)
     100             :     {
     101             :         // also create special files
     102             :         //
     103           4 :         std::string fifo(folder);
     104           4 :         fifo += "/file.fifo";
     105           4 :         r = mkfifo(fifo.c_str(), 0700);
     106           4 :         CATCH_REQUIRE(r == 0);
     107             : 
     108           4 :         std::string socket_name(folder);
     109           4 :         socket_name += "/file.socket";
     110           4 :         struct sockaddr_un addr = {};
     111           4 :         addr.sun_family = AF_UNIX;
     112           4 :         CATCH_REQUIRE(socket_name.length() < sizeof(addr.sun_path));
     113           4 :         strncpy(addr.sun_path, socket_name.c_str(), sizeof(addr.sun_path));
     114           4 :         int s = socket(AF_UNIX, SOCK_STREAM, 0);
     115           4 :         CATCH_REQUIRE(s != -1);
     116           4 :         sockets.push_back(s);
     117           4 :         r = bind(s, reinterpret_cast<sockaddr const *>(&addr), sizeof(addr));
     118           4 :         CATCH_REQUIRE(r == 0);
     119             : 
     120             :         // creating device files is a root matter
     121             :         //
     122           4 :         snapdev::as_root root;
     123           4 :         if(root.is_switched())
     124             :         {
     125           0 :             std::string char_dev(folder);
     126           0 :             char_dev += "/char.dev";
     127           0 :             r = mknod(char_dev.c_str(), 0700 | S_IFCHR, makedev(1, 5));    // like /dev/zero
     128           0 :             CATCH_REQUIRE(r == 0);
     129             : 
     130           0 :             std::string block_dev(folder);
     131           0 :             block_dev += "/block.dev";
     132           0 :             r = mknod(block_dev.c_str(), 0700 | S_IFBLK, makedev(7, 1));   // like /dev/loop1
     133           0 :             CATCH_REQUIRE(r == 0);
     134           0 :         }
     135             :         else
     136             :         {
     137             :             static bool message_shown(false);
     138           4 :             if(!message_shown)
     139             :             {
     140           1 :                 message_shown = true;
     141             : 
     142             :                 std::cout << "--- note that the special file test cannot create block and character devices.\n"
     143             :                     << "--- if you want to test those, make sure to run as root:\n"
     144           1 :                     << "---    sudo unittest ... rm_r\n";
     145             :             }
     146             :         }
     147           4 :     }
     148             : 
     149             :     // third create sub-directories and recursively create files/dirs
     150             :     // unless we already reached the maximum depth
     151             :     //
     152           7 :     if(depth >= max_depth)
     153             :     {
     154           5 :         return;
     155             :     }
     156           2 :     int const max_foo(rand() % 3 + 2);
     157           7 :     for(int foo(1); foo <= max_foo; ++foo)
     158             :     {
     159           5 :         std::string sub_folder(folder);
     160           5 :         sub_folder += "/foo";
     161           5 :         sub_folder += std::to_string(foo);
     162             : 
     163           5 :         create_files(sub_folder, depth + 1, max_depth, special_files, sockets);
     164           5 :     }
     165             : }
     166             : #if SNAPDEV_CHECK_GCC_VERSION(11, 3, 0)
     167             : #pragma GCC diagnostic pop
     168             : #endif
     169             : 
     170             : 
     171             : 
     172             : }
     173             : 
     174             : 
     175           5 : CATCH_TEST_CASE("rm_r", "[os]")
     176             : {
     177           5 :     int r(0);
     178             : 
     179           5 :     CATCH_START_SECTION("rm_r: missing")
     180             :     {
     181           1 :         std::string path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     182           1 :         path += "/missing";
     183             : 
     184           1 :         r = snapdev::rm_r(path);
     185           1 :         CATCH_REQUIRE(r == 0);
     186           1 :     }
     187           5 :     CATCH_END_SECTION()
     188             : 
     189           5 :     CATCH_START_SECTION("rm_r: empty directory")
     190             :     {
     191           1 :         std::string path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     192           1 :         path += "/empty";
     193             : 
     194           1 :         struct stat st;
     195           1 :         if(stat(path.c_str(), &st) == 0)
     196             :         {
     197             :             // if it already exists and it is a directory, go on
     198             :             //
     199           0 :             CATCH_REQUIRE(S_ISDIR(st.st_mode));
     200             :         }
     201             :         else
     202             :         {
     203           1 :             r = mkdir(path.c_str(), 0700);
     204           1 :             CATCH_REQUIRE(r == 0);
     205             :         }
     206             : 
     207           1 :         r = snapdev::rm_r(path);
     208           1 :         CATCH_REQUIRE(r == 0);
     209           1 :     }
     210           5 :     CATCH_END_SECTION()
     211             : 
     212           5 :     CATCH_START_SECTION("rm_r: directory with a few files")
     213             :     {
     214           1 :         std::string path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     215           1 :         path += "/directory";
     216             : 
     217           1 :         struct stat st;
     218           1 :         if(stat(path.c_str(), &st) == 0)
     219             :         {
     220             :             // if it already exists and it is a directory, go on
     221             :             //
     222           0 :             CATCH_REQUIRE(S_ISDIR(st.st_mode));
     223             :         }
     224             :         else
     225             :         {
     226           1 :             r = mkdir(path.c_str(), 0700);
     227           1 :             CATCH_REQUIRE(r == 0);
     228             :         }
     229             : 
     230           1 :         int const max(rand() % 5 + 1);
     231           4 :         for(int i(1); i <= max; ++i)
     232             :         {
     233           3 :             std::string filename(path);
     234           3 :             filename += '/';
     235           3 :             filename += "file";
     236           3 :             filename += std::to_string(i);
     237           3 :             filename += ".txt";
     238             : 
     239           3 :             std::ofstream out(filename);
     240           3 :             out << SNAP_CATCH2_NAMESPACE::random_string(10, 100);
     241           3 :         }
     242             : 
     243           1 :         r = snapdev::rm_r(path);
     244           1 :         CATCH_REQUIRE(r == 0);
     245           1 :     }
     246           5 :     CATCH_END_SECTION()
     247             : 
     248           5 :     CATCH_START_SECTION("rm_r: directory tree")
     249             :     {
     250           1 :         std::string path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     251           1 :         path += "/tree";
     252             : 
     253           1 :         std::vector<int> sockets;
     254           1 :         int const max_depth(rand() % 3 + 1);
     255           1 :         create_files(path, 1, max_depth, false, sockets);
     256             :         // ignore the sockets since we do not create special files
     257             : 
     258           1 :         r = snapdev::rm_r(path);
     259           1 :         CATCH_REQUIRE(r == 0);
     260           1 :     }
     261           5 :     CATCH_END_SECTION()
     262             : 
     263           5 :     CATCH_START_SECTION("rm_r: directory tree with special files")
     264             :     {
     265           1 :         std::string path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     266           1 :         path += "/special";
     267             : 
     268           1 :         std::vector<int> sockets;
     269           1 :         int const max_depth(rand() % 3 + 1);
     270           1 :         create_files(path, 1, max_depth, true, sockets);
     271             : 
     272             :         // deleting as is must fail
     273             :         // (i.e. we detect special files early and don't delete anything)
     274             :         //
     275           1 :         r = snapdev::rm_r(path);
     276           1 :         if(r == 0)
     277             :         {
     278           0 :             CATCH_REQUIRE(r != 0);
     279             :         }
     280           1 :         CATCH_REQUIRE(errno == EPERM);
     281             : 
     282             :         // try with SPECIAL_FILE_IGNORE
     283             :         // (i.e. delete everything exception special files)
     284             :         //
     285           1 :         r = snapdev::rm_r(path, snapdev::special_file_t::SPECIAL_FILE_IGNORE);
     286           1 :         CATCH_REQUIRE(r == 0);
     287             : 
     288             :         // try with SPECIAL_FILE_KEEP_DEVICES
     289             :         // (i.e. delete everything exception devices)
     290             :         //
     291           1 :         r = snapdev::rm_r(path, snapdev::special_file_t::SPECIAL_FILE_KEEP_DEVICES);
     292           1 :         CATCH_REQUIRE(r == 0);
     293             : 
     294             :         // try with SPECIAL_FILE_REMOVE
     295             :         // (i.e. delete absolutely everything)
     296             :         //
     297             :         // note: this requires root permissions (we do not create if we
     298             :         //       can't become root)
     299             :         {
     300           1 :             snapdev::as_root root;
     301           1 :             r = snapdev::rm_r(path, snapdev::special_file_t::SPECIAL_FILE_REMOVE);
     302           1 :             CATCH_REQUIRE(r == 0);
     303           1 :         }
     304             : 
     305             :         // now close all the sockets (ignore errors)
     306             :         //
     307           5 :         for(auto const & s : sockets)
     308             :         {
     309           4 :             close(s);
     310             :         }
     311             : 
     312             :         // finally make sure the directory is gone
     313             :         //
     314           1 :         struct stat st;
     315           1 :         CATCH_REQUIRE(stat(path.c_str(), &st) != 0);
     316           1 :     }
     317           5 :     CATCH_END_SECTION()
     318           5 : }
     319             : 
     320             : 
     321             : 
     322             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14