LCOV - code coverage report
Current view: top level - test - test-mountpoint-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 123 162 75.9 %
Date: 2019-08-22 15:41:25 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <sys/mount.h>
       4             : #include <unistd.h>
       5             : 
       6             : #include "alloc-util.h"
       7             : #include "def.h"
       8             : #include "fd-util.h"
       9             : #include "fileio.h"
      10             : #include "hashmap.h"
      11             : #include "log.h"
      12             : #include "mountpoint-util.h"
      13             : #include "path-util.h"
      14             : #include "rm-rf.h"
      15             : #include "string-util.h"
      16             : #include "tests.h"
      17             : 
      18           7 : static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) {
      19             :         long unsigned flags;
      20             : 
      21           7 :         log_info("/* %s(%s) */", __func__, name);
      22             : 
      23           7 :         assert_se(mount_propagation_flags_from_string(name, &flags) == ret);
      24             : 
      25           7 :         if (ret >= 0) {
      26             :                 const char *c;
      27             : 
      28           5 :                 assert_se(flags == expected);
      29             : 
      30           5 :                 c = mount_propagation_flags_to_string(flags);
      31           5 :                 if (isempty(name))
      32           2 :                         assert_se(isempty(c));
      33             :                 else
      34           3 :                         assert_se(streq(c, name));
      35             :         }
      36           7 : }
      37             : 
      38           1 : static void test_mnt_id(void) {
      39           1 :         _cleanup_fclose_ FILE *f = NULL;
      40           1 :         _cleanup_hashmap_free_free_ Hashmap *h = NULL;
      41             :         Iterator i;
      42             :         char *p;
      43             :         void *k;
      44             :         int r;
      45             : 
      46           1 :         log_info("/* %s */", __func__);
      47             : 
      48           1 :         assert_se(f = fopen("/proc/self/mountinfo", "re"));
      49           1 :         assert_se(h = hashmap_new(&trivial_hash_ops));
      50             : 
      51          39 :         for (;;) {
      52          41 :                 _cleanup_free_ char *line = NULL, *path = NULL;
      53             :                 int mnt_id;
      54             : 
      55          40 :                 r = read_line(f, LONG_LINE_MAX, &line);
      56          40 :                 if (r == 0)
      57           1 :                         break;
      58          39 :                 assert_se(r > 0);
      59             : 
      60          39 :                 assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2);
      61             : #if HAS_FEATURE_MEMORY_SANITIZER
      62             :                 /* We don't know the length of the string, so we need to unpoison it one char at a time */
      63             :                 for (const char *c = path; ;c++) {
      64             :                         msan_unpoison(c, 1);
      65             :                         if (!*c)
      66             :                                 break;
      67             :                 }
      68             : #endif
      69          39 :                 log_debug("mountinfo: %s → %i", path, mnt_id);
      70             : 
      71          39 :                 assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0);
      72          39 :                 path = NULL;
      73             :         }
      74             : 
      75          40 :         HASHMAP_FOREACH_KEY(p, k, h, i) {
      76          39 :                 int mnt_id = PTR_TO_INT(k), mnt_id2;
      77             : 
      78          39 :                 r = path_get_mnt_id(p, &mnt_id2);
      79          39 :                 if (r < 0) {
      80           1 :                         log_debug_errno(r, "Failed to get the mnt id of %s: %m\n", p);
      81          38 :                         continue;
      82             :                 }
      83             : 
      84          38 :                 log_debug("mnt ids of %s are %i, %i\n", p, mnt_id, mnt_id2);
      85             : 
      86          38 :                 if (mnt_id == mnt_id2)
      87          37 :                         continue;
      88             : 
      89             :                 /* The ids don't match? If so, then there are two mounts on the same path, let's check if
      90             :                  * that's really the case */
      91           1 :                 char *t = hashmap_get(h, INT_TO_PTR(mnt_id2));
      92           1 :                 log_debug("the other path for mnt id %i is %s\n", mnt_id2, t);
      93           1 :                 assert_se(path_equal(p, t));
      94             :         }
      95           1 : }
      96             : 
      97           1 : static void test_path_is_mount_point(void) {
      98             :         int fd;
      99           1 :         char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
     100           1 :         _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
     101           1 :         _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
     102           1 :         _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
     103             : 
     104           1 :         log_info("/* %s */", __func__);
     105             : 
     106           1 :         assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0);
     107           1 :         assert_se(path_is_mount_point("/", NULL, 0) > 0);
     108           1 :         assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0);
     109           1 :         assert_se(path_is_mount_point("//", NULL, 0) > 0);
     110             : 
     111           1 :         assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0);
     112           1 :         assert_se(path_is_mount_point("/proc", NULL, 0) > 0);
     113           1 :         assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0);
     114           1 :         assert_se(path_is_mount_point("/proc/", NULL, 0) > 0);
     115             : 
     116           1 :         assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0);
     117           1 :         assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0);
     118           1 :         assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
     119           1 :         assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
     120             : 
     121           1 :         assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
     122           1 :         assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
     123           1 :         assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
     124           1 :         assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
     125             : 
     126             :         /* we'll create a hierarchy of different kinds of dir/file/link
     127             :          * layouts:
     128             :          *
     129             :          * <tmp>/file1, <tmp>/file2
     130             :          * <tmp>/link1 -> file1, <tmp>/link2 -> file2
     131             :          * <tmp>/dir1/
     132             :          * <tmp>/dir1/file
     133             :          * <tmp>/dirlink1 -> dir1
     134             :          * <tmp>/dirlink1file -> dirlink1/file
     135             :          * <tmp>/dir2/
     136             :          * <tmp>/dir2/file
     137             :          */
     138             : 
     139             :         /* file mountpoints */
     140           1 :         assert_se(mkdtemp(tmp_dir) != NULL);
     141           1 :         file1 = path_join(tmp_dir, "file1");
     142           1 :         assert_se(file1);
     143           1 :         file2 = path_join(tmp_dir, "file2");
     144           1 :         assert_se(file2);
     145           1 :         fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
     146           1 :         assert_se(fd > 0);
     147           1 :         close(fd);
     148           1 :         fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
     149           1 :         assert_se(fd > 0);
     150           1 :         close(fd);
     151           1 :         link1 = path_join(tmp_dir, "link1");
     152           1 :         assert_se(link1);
     153           1 :         assert_se(symlink("file1", link1) == 0);
     154           1 :         link2 = path_join(tmp_dir, "link2");
     155           1 :         assert_se(link1);
     156           1 :         assert_se(symlink("file2", link2) == 0);
     157             : 
     158           1 :         assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0);
     159           1 :         assert_se(path_is_mount_point(file1, NULL, 0) == 0);
     160           1 :         assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0);
     161           1 :         assert_se(path_is_mount_point(link1, NULL, 0) == 0);
     162             : 
     163             :         /* directory mountpoints */
     164           1 :         dir1 = path_join(tmp_dir, "dir1");
     165           1 :         assert_se(dir1);
     166           1 :         assert_se(mkdir(dir1, 0755) == 0);
     167           1 :         dirlink1 = path_join(tmp_dir, "dirlink1");
     168           1 :         assert_se(dirlink1);
     169           1 :         assert_se(symlink("dir1", dirlink1) == 0);
     170           1 :         dirlink1file = path_join(tmp_dir, "dirlink1file");
     171           1 :         assert_se(dirlink1file);
     172           1 :         assert_se(symlink("dirlink1/file", dirlink1file) == 0);
     173           1 :         dir2 = path_join(tmp_dir, "dir2");
     174           1 :         assert_se(dir2);
     175           1 :         assert_se(mkdir(dir2, 0755) == 0);
     176             : 
     177           1 :         assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0);
     178           1 :         assert_se(path_is_mount_point(dir1, NULL, 0) == 0);
     179           1 :         assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0);
     180           1 :         assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0);
     181             : 
     182             :         /* file in subdirectory mountpoints */
     183           1 :         dir1file = path_join(dir1, "file");
     184           1 :         assert_se(dir1file);
     185           1 :         fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
     186           1 :         assert_se(fd > 0);
     187           1 :         close(fd);
     188             : 
     189           1 :         assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0);
     190           1 :         assert_se(path_is_mount_point(dir1file, NULL, 0) == 0);
     191           1 :         assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0);
     192           1 :         assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0);
     193             : 
     194             :         /* these tests will only work as root */
     195           1 :         if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
     196             :                 int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t;
     197             :                 const char *file2d;
     198             : 
     199             :                 /* files */
     200             :                 /* capture results in vars, to avoid dangling mounts on failure */
     201           0 :                 log_info("%s: %s", __func__, file2);
     202           0 :                 rf = path_is_mount_point(file2, NULL, 0);
     203           0 :                 rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW);
     204             : 
     205           0 :                 file2d = strjoina(file2, "/");
     206           0 :                 log_info("%s: %s", __func__, file2d);
     207           0 :                 rdf = path_is_mount_point(file2d, NULL, 0);
     208           0 :                 rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW);
     209             : 
     210           0 :                 log_info("%s: %s", __func__, link2);
     211           0 :                 rlf = path_is_mount_point(link2, NULL, 0);
     212           0 :                 rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW);
     213             : 
     214           0 :                 assert_se(umount(file2) == 0);
     215             : 
     216           0 :                 assert_se(rf == 1);
     217           0 :                 assert_se(rt == 1);
     218           0 :                 assert_se(rdf == -ENOTDIR);
     219           0 :                 assert_se(rdt == -ENOTDIR);
     220           0 :                 assert_se(rlf == 0);
     221           0 :                 assert_se(rlt == 1);
     222             : 
     223             :                 /* dirs */
     224           0 :                 dir2file = path_join(dir2, "file");
     225           0 :                 assert_se(dir2file);
     226           0 :                 fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
     227           0 :                 assert_se(fd > 0);
     228           0 :                 close(fd);
     229             : 
     230           0 :                 assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
     231             : 
     232           0 :                 log_info("%s: %s", __func__, dir1);
     233           0 :                 rf = path_is_mount_point(dir1, NULL, 0);
     234           0 :                 rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW);
     235           0 :                 log_info("%s: %s", __func__, dirlink1);
     236           0 :                 rlf = path_is_mount_point(dirlink1, NULL, 0);
     237           0 :                 rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW);
     238           0 :                 log_info("%s: %s", __func__, dirlink1file);
     239             :                 /* its parent is a mount point, but not /file itself */
     240           0 :                 rl1f = path_is_mount_point(dirlink1file, NULL, 0);
     241           0 :                 rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW);
     242             : 
     243           0 :                 assert_se(umount(dir1) == 0);
     244             : 
     245           0 :                 assert_se(rf == 1);
     246           0 :                 assert_se(rt == 1);
     247           0 :                 assert_se(rlf == 0);
     248           0 :                 assert_se(rlt == 1);
     249           0 :                 assert_se(rl1f == 0);
     250           0 :                 assert_se(rl1t == 0);
     251             : 
     252             :         } else
     253           1 :                 printf("Skipping bind mount file test: %m\n");
     254             : 
     255           1 :         assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
     256           1 : }
     257             : 
     258           1 : int main(int argc, char *argv[]) {
     259           1 :         test_setup_logging(LOG_DEBUG);
     260             : 
     261           1 :         test_mount_propagation_flags("shared", 0, MS_SHARED);
     262           1 :         test_mount_propagation_flags("slave", 0, MS_SLAVE);
     263           1 :         test_mount_propagation_flags("private", 0, MS_PRIVATE);
     264           1 :         test_mount_propagation_flags(NULL, 0, 0);
     265           1 :         test_mount_propagation_flags("", 0, 0);
     266           1 :         test_mount_propagation_flags("xxxx", -EINVAL, 0);
     267           1 :         test_mount_propagation_flags(" ", -EINVAL, 0);
     268             : 
     269           1 :         test_mnt_id();
     270           1 :         test_path_is_mount_point();
     271             : 
     272           1 :         return 0;
     273             : }

Generated by: LCOV version 1.14