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