File size: 6,618 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
#!/bin/sh
# Test df's behavior when the mount list contains duplicate entries.
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ df
require_gcc_shared_
# We use --local here so as to not activate
# potentially very many remote mounts.
df --local --output=target >LOCAL_FS || skip_ 'df fails'
grep '^/$' LOCAL_FS || skip_ 'no root file system found'
# Get real targets to substitute for /NONROOT and /REMOTE below.
export CU_NONROOT_FS=$(grep /. LOCAL_FS | head -n1)
export CU_REMOTE_FS=$(grep /. LOCAL_FS | tail -n+2 | head -n1)
unique_entries=1
test -z "$CU_NONROOT_FS" || unique_entries=$(expr $unique_entries + 1)
test -z "$CU_REMOTE_FS" || unique_entries=$(expr $unique_entries + 2)
grep '^#define HAVE_GETMNTENT 1' $CONFIG_HEADER > /dev/null \
|| skip_ "getmntent is not used on this system"
# Simulate an mtab file to test various cases.
cat > k.c <<EOF || framework_failure_
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
#include <string.h>
#include <stdarg.h>
#include <dlfcn.h>
int open(const char *path, int flags, ...)
{
static int (*open_func)(const char *, int, ...);
/* get reference to original (libc provided) open */
if (!open_func)
{
open_func = (int(*)(const char *, int, ...))
dlsym(RTLD_NEXT, "open");
if (!open_func)
{
fprintf (stderr, "Failed to find open()\n");
errno = ESRCH;
return -1;
}
}
va_list ap;
va_start (ap, flags);
mode_t mode = (sizeof (mode_t) < sizeof (int)
? va_arg (ap, int)
: va_arg (ap, mode_t));
va_end (ap);
/* Returning ENOENT here will get read_file_system_list()
to fall back to using getmntent() below. */
if (streq (path, "/proc/self/mountinfo"))
{
errno = ENOENT;
return -1;
}
else
return open_func(path, flags, mode);
}
struct mntent *getmntent (FILE *fp)
{
static char *nonroot_fs;
static char *remote_fs;
static int done;
/* Prove that LD_PRELOAD works. */
if (!done)
{
fclose (fopen ("x", "w"));
++done;
}
static struct mntent mntents[] = {
{.mnt_fsname="/short", .mnt_dir="/invalid/mount/dir", .mnt_opts=""},
{.mnt_fsname="fsname", .mnt_dir="/", .mnt_opts=""},
{.mnt_fsname="/fsname", .mnt_dir="/.", .mnt_opts=""},
{.mnt_fsname="/fsname", .mnt_dir="/", .mnt_opts=""},
{.mnt_fsname="virtfs", .mnt_dir="/NONROOT", .mnt_type="t1", .mnt_opts=""},
{.mnt_fsname="virtfs2", .mnt_dir="/NONROOT", .mnt_type="t2", .mnt_opts=""},
{.mnt_fsname="netns", .mnt_dir="net:[1234567]", .mnt_opts=""},
{.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""},
{.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""},
{.mnt_fsname="rem:ote2",.mnt_dir="/REMOTE", .mnt_opts=""},
};
if (done == 1)
{
nonroot_fs = getenv ("CU_NONROOT_FS");
if (!nonroot_fs || !*nonroot_fs)
nonroot_fs = "/"; /* merge into / entries. */
remote_fs = getenv ("CU_REMOTE_FS");
}
if (done == 1 && !getenv ("CU_TEST_DUPE_INVALID"))
done++; /* skip the first entry. */
while (done++ <= 10)
{
if (!mntents[done-2].mnt_type)
mntents[done-2].mnt_type = "-";
if (!mntents[done-2].mnt_opts)
mntents[done-2].mnt_opts = "-";
if (streq (mntents[done-2].mnt_dir, "/NONROOT"))
mntents[done-2].mnt_dir = nonroot_fs;
if (streq (mntents[done-2].mnt_dir, "/REMOTE"))
{
if (!remote_fs || !*remote_fs)
continue;
else
mntents[done-2].mnt_dir = remote_fs;
}
return &mntents[done-2];
}
return NULL;
}
EOF
# Then compile/link it:
gcc_shared_ k.c k.so \
|| skip_ 'failed to build mntent shared library'
# Test if LD_PRELOAD works:
LD_PRELOAD=$LD_PRELOAD:./k.so df
test -f x || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
# The fake mtab file should only contain entries
# having the same device number; thus the output should
# consist of a header and unique entries.
LD_PRELOAD=$LD_PRELOAD:./k.so df -T >out || fail=1
test $(wc -l <out) -eq $(expr 1 + $unique_entries) || { fail=1; cat out; }
# With --total we should suppress the duplicate but separate remote file system
LD_PRELOAD=$LD_PRELOAD:./k.so df --total >out || fail=1
test "$CU_REMOTE_FS" && elide_remote=1 || elide_remote=0
test $(wc -l <out) -eq $(expr 2 + $unique_entries - $elide_remote) ||
{ fail=1; cat out; }
# Ensure we don't fail when unable to stat (currently) unavailable entries
LD_PRELOAD=$LD_PRELOAD:./k.so CU_TEST_DUPE_INVALID=1 df -T >out || fail=1
test $(wc -l <out) -eq $(expr 1 + $unique_entries) || { fail=1; cat out; }
# df should also prefer "/fsname" over "fsname"
if test "$unique_entries" = 2; then
test $(grep -c '/fsname' <out) -eq 1 || { fail=1; cat out; }
# ... and "/fsname" with '/' as Mounted on over '/.'
test $(grep -cF '/.' <out) -eq 0 || { fail=1; cat out; }
fi
# df should use the last seen devname (mnt_fsname) and devtype (mnt_type)
test $(grep -c 'virtfs2.*t2' <out) -eq 1 || { fail=1; cat out; }
# Ensure that filtering duplicates does not affect -a processing.
LD_PRELOAD=$LD_PRELOAD:./k.so df -a >out || fail=1
total_fs=6; test "$CU_REMOTE_FS" && total_fs=$(expr $total_fs + 3)
test $(wc -l <out) -eq $total_fs || { fail=1; cat out; }
# Ensure placeholder "-" values used for the eclipsed "virtfs"
test $(grep -c 'virtfs *-' <out) -eq 1 || { fail=1; cat out; }
# Ensure that filtering duplicates does not affect
# argument processing (now without the fake getmntent()).
df '.' '.' >out || fail=1
test $(wc -l <out) -eq 3 || { fail=1; cat out; }
Exit $fail
|