1#!/bin/sh
2# Test rm's behavior when the directory cannot be read.
3# This test is skipped on systems that lack LD_PRELOAD support.
4
5# Copyright (C) 2016-2023 Free Software Foundation, Inc.
6
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
20. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
21print_ver_ rm
22require_gcc_shared_
23
24mkdir -p dir/notempty || framework_failure_
25
26# Simulate "readdir" failure.
27cat > k.c <<\EOF || framework_failure_
28#define _GNU_SOURCE
29
30/* Setup so we don't have to worry about readdir64.  */
31#ifndef __LP64__
32# define _FILE_OFFSET_BITS 64
33#endif
34
35#include <dlfcn.h>
36#include <dirent.h>
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40
41struct dirent *readdir (DIR *dirp)
42{
43  static int count = 1;
44
45#ifndef __LP64__
46  if (count == 1)
47    fclose (fopen ("32bit", "w"));
48  errno = ENOSYS;
49  return NULL;
50#endif
51
52  static struct dirent *(*real_readdir)(DIR *dirp);
53  if (! real_readdir && ! (real_readdir = dlsym (RTLD_NEXT, "readdir")))
54    {
55      fprintf (stderr, "Failed to find readdir()\n");
56      errno = ESRCH;
57      return NULL;
58    }
59  struct dirent* d;
60  if (! (d = real_readdir (dirp)))
61    {
62      fprintf (stderr, "Failed to get dirent\n");
63      errno = ENOENT;
64      return NULL;
65    }
66
67  /* Flag that LD_PRELOAD and above functions work.  */
68  if (count == 1)
69    fclose (fopen ("preloaded", "w"));
70
71  /* Return some entries to trigger partial read failure,
72     ensuring we don't return ignored '.' or '..'  */
73  char const *readdir_partial = getenv ("READDIR_PARTIAL");
74  if (readdir_partial && *readdir_partial && count <= 3)
75    {
76      count++;
77      d->d_name[0]='0'+count; d->d_name[1]='\0';
78#ifdef _DIRENT_HAVE_D_NAMLEN
79      d->d_namlen = 1;
80#endif
81      errno = 0;
82      return d;
83    };
84
85  /* Fail.  */
86  errno = ENOENT;
87  return NULL;
88}
89EOF
90
91# Then compile/link it:
92gcc_shared_ k.c k.so \
93  || framework_failure_ 'failed to build shared library'
94
95# Test if LD_PRELOAD works:
96export READDIR_PARTIAL
97for READDIR_PARTIAL in '' '1'; do
98  rm -f preloaded
99  (export LD_PRELOAD=$LD_PRELOAD:./k.so
100   returns_ 1 rm -Rf dir 2>>errt) || fail=1
101  if test -f 32bit; then
102    skip_ 'This test only supports 64 bit systems'
103  elif ! test -f preloaded; then
104    cat errt
105    skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
106  fi
107done
108
109# First case is failure to read any items from dir, then assume empty.
110# Generally that will be diagnosed when rm tries to rmdir().
111# Second case is more general error where we fail immediately
112# (with ENOENT in this case but it could be anything).
113cat <<EOF > exp
114rm: cannot remove 'dir'
115Failed to get dirent
116rm: traversal failed: dir
117EOF
118sed 's/\(rm:.*\):.*/\1/' errt > err || framework_failure_
119compare exp err || fail=1
120
121Exit $fail
122