1#!/bin/sh
2# ensure that ls -i works also for mount points
3
4# Copyright (C) 2009-2023 Free Software Foundation, Inc.
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. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
20print_ver_ ls
21
22# We use --local here so as to not activate
23# potentially very many remote mounts.
24df --local --out=target | sed -n '/^\/./p' > mount_points
25test -s mount_points ||
26  skip_ "this test requires a non-root mount point"
27
28# Given e.g., /dev/shm, produce the list of GNU ls options that
29# let us list just that entry using readdir data from its parent:
30# ls -i -I '[^s]*' -I 's[^h]*' -I 'sh[^m]*' -I 'shm?*' -I '.?*' \
31# -I '?' -I '??' /dev
32
33ls_ignore_options()
34{
35  name=$1
36  opts="-I '.?*' -I '$name?*'"
37  while :; do
38    glob=$(echo "$name"|sed 's/\(.*\)\(.\)$/\1[^\2]*/')
39    opts="$opts -I '$glob'"
40    name=$(echo "$name"|sed 's/.$//')
41    test -z "$name" && break
42    glob=$(echo "$name"|sed 's/./?/g')
43    opts="$opts -I '$glob'"
44  done
45  echo "$opts"
46}
47
48inode_via_readdir()
49{
50  mount_point=$1
51  base=$(basename "$mount_point")
52  case "$base" in
53    .*) skip_ 'mount point component starts with "."' ;;
54    *[*?]*) skip_ 'mount point component contains "?" or "*"' ;;
55  esac
56  opts=$(ls_ignore_options "$base")
57  parent_dir=$(dirname "$mount_point")
58  ls_out=$(eval "ls -i $opts '$parent_dir'")
59  test $? -eq 0 || \
60    skip_ "'$parent_dir' is not readable for current user"
61  echo $ls_out | sed 's/ .*//'
62}
63
64while read dir; do
65  readdir_inode=$(inode_via_readdir "$dir")
66  test $? = 77 && continue
67  stat_inode=$(timeout 1 stat --format=%i "$dir")
68  # If stat fails or says the inode is 0, skip $dir.
69  case $stat_inode in 0|'') continue;; esac
70  test "$readdir_inode" = "$stat_inode" || fail=1
71done < mount_points
72
73Exit $fail
74