nftw: fill in stat buf for dangling links [BZ #23501]

As per Austin Group interpretation, "the object" wrt a
dangling symlink is the symlink itself, despite FTW_PHYS.

Reviewed-by: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
This commit is contained in:
DJ Delorie 2019-06-19 17:07:41 -04:00
parent 7444810387
commit 6ba205b2c3
4 changed files with 252 additions and 3 deletions

View File

@ -1,3 +1,10 @@
2019-07-08 DJ Delorie <dj@redhat.com>
[BZ #23501]
* io/ftw.c (process_entry): Fill in statbuf for dangling links.
* io/tst-ftw-lnk.c: New test.
* io/Makefile: Run it.
2019-07-08 Adhemerval Zanella <adhemerval.zanella@linaro.org>
* sysdeps/powerpc/power7/fpu/s_logb.c: Move to ...

View File

@ -73,7 +73,8 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
tst-fts tst-fts-lfs tst-open-tmpfile \
tst-copy_file_range tst-getcwd-abspath tst-lockf
tst-copy_file_range tst-getcwd-abspath tst-lockf \
tst-ftw-lnk
# Likewise for statx, but we do not need static linking here.
tests-internal += tst-statx

View File

@ -423,10 +423,12 @@ process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
result = -1;
else if (data->flags & FTW_PHYS)
flag = FTW_NS;
else if (d_type == DT_LNK)
flag = FTW_SLN;
else
{
/* Old code left ST undefined for dangling DT_LNK without
FTW_PHYS set; a clarification at the POSIX level suggests
it should contain information about the link (ala lstat).
We do our best to fill in what data we can. */
if (dir->streamfd != -1)
statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
AT_SYMLINK_NOFOLLOW);

239
io/tst-ftw-lnk.c Normal file
View File

@ -0,0 +1,239 @@
/* Test for ftw function related to symbolic links for BZ #23501
Copyright (C) 2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <ftw.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <support/support.h>
#include <support/check.h>
#define TSTDIR "tst-ftw-lnk.d"
static void
un (const char *file)
{
struct stat st;
/* Does the file exist? */
if (lstat (file, &st) < 0
&& errno == ENOENT)
return;
/* If so, try to remove it. */
if (unlink (file) < 0)
FAIL_EXIT1 ("Unable to unlink %s", file);
}
static void
debug_cb (const char *which, const char *fpath,
const struct stat *sb, int typeflags)
{
const char *sb_type = "???";
const char *ftw_type = "???";
/* Coding style here is intentionally "wrong" to increase readability. */
if (S_ISREG (sb->st_mode)) sb_type = "REG";
if (S_ISDIR (sb->st_mode)) sb_type = "DIR";
if (S_ISLNK (sb->st_mode)) sb_type = "LNK";
if (typeflags == FTW_F) ftw_type = "F";
if (typeflags == FTW_D) ftw_type = "D";
if (typeflags == FTW_DNR) ftw_type = "DNR";
if (typeflags == FTW_DP) ftw_type = "DP";
if (typeflags == FTW_NS) ftw_type = "NS";
if (typeflags == FTW_SL) ftw_type = "SL";
if (typeflags == FTW_SLN) ftw_type = "SLN";
printf ("%s %5d %-3s %-3s %s\n", which, (int)(sb->st_ino % 100000), sb_type, ftw_type, fpath);
}
int good_cb = 0;
#define EXPECTED_GOOD 12
/* See if the stat buffer SB refers to the file AS_FNAME. */
static void
check_same_stats (const struct stat *sb, const char *as_fname)
{
struct stat as;
if (lstat (as_fname, &as) < 0)
FAIL_EXIT1 ("unable to stat %s for comparison", as_fname);
if (as.st_mode == sb->st_mode
&& as.st_ino == sb->st_ino
&& as.st_size == sb->st_size)
good_cb ++;
else
printf ("statbuf data doesn't match %s\n", as_fname);
}
static int
callback_phys (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
{
debug_cb ("P", fpath, sb, typeflags);
/* This callback is for when the FTW_PHYS flag is set. The results
should reflect the physical filesystem entry, not what it might
point to. */
/* link1-bad is a dangling symlink, but we're reporting on the link
anyway (ala lstat ()). */
if (strcmp (fpath, "./link1-bad") == 0)
{
if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
good_cb ++;
else
printf ("link1-bad had wrong phys stats\n");
check_same_stats (sb, "link1-bad");
}
/* link2-ok is a regular non-dangling symlink. */
if (strcmp (fpath, "./link2-ok") == 0)
{
if (S_ISLNK (sb->st_mode) && typeflags == FTW_SL)
good_cb ++;
else
printf ("link2-ok had wrong phys stats\n");
check_same_stats (sb, "link2-ok");
}
/* This is the file link2-ok points to. */
if (strcmp (fpath, "./link2-tgt") == 0)
{
if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
good_cb ++;
else
printf ("link2-tgt had wrong phys stats\n");
check_same_stats (sb, "link2-tgt");
}
return 0;
}
static int
callback_log (const char *fpath, const struct stat *sb, int typeflags, struct FTW *ftwbuf)
{
debug_cb ("L", fpath, sb, typeflags);
/* This callback is for when the FTW_PHYS flags is NOT set. The
results should reflect the logical file, i.e. symlinks should be
followed. */
/* We would normally report what link1-bad links to, but link1-bad
is a dangling symlink. This is an exception to FTW_PHYS in that
we report FTW_SLN (dangling symlink) but the stat data is
correctly set to the link itself (ala lstat ()). */
if (strcmp (fpath, "./link1-bad") == 0)
{
if (S_ISLNK (sb->st_mode) && typeflags == FTW_SLN)
good_cb ++;
else
printf ("link1-bad had wrong logical stats\n");
check_same_stats (sb, "link1-bad");
}
/* link2-ok points to link2-tgt, so we expect data reflecting
link2-tgt (ala stat ()). */
if (strcmp (fpath, "./link2-ok") == 0)
{
if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
good_cb ++;
else
printf ("link2-ok had wrong logical stats\n");
check_same_stats (sb, "link2-tgt");
}
/* This is the file link2-ok points to. */
if (strcmp (fpath, "./link2-tgt") == 0)
{
if (S_ISREG (sb->st_mode) && typeflags == FTW_F)
good_cb ++;
else
printf ("link2-tgt had wrong logical stats\n");
check_same_stats (sb, "link2-tgt");
}
return 0;
}
static int
do_test (void)
{
struct stat st;
if (chdir (support_objdir_root) < 0)
FAIL_EXIT1 ("cannot chdir to objdir root");
if (chdir ("io") < 0)
FAIL_EXIT1 ("cannot chdir to objdir/io subdir");
if (stat (TSTDIR, &st) >= 0)
{
/* Directory does exist, delete any potential conflicts. */
if (chdir (TSTDIR) < 0)
FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
un ("link1-bad");
un ("link1-tgt");
un ("link2-ok");
un ("link2-tgt");
}
else
{
/* Directory does not exist, create it. */
mkdir (TSTDIR, 0777);
if (chdir (TSTDIR) < 0)
FAIL_EXIT1 ("cannot chdir to %s\n", TSTDIR);
}
/* At this point, we're inside our test directory, and need to
prepare it. */
if (symlink ("link1-tgt", "link1-bad") < 0)
FAIL_EXIT1 ("symlink link1-bad failed");
if (symlink ("link2-tgt", "link2-ok") < 0)
FAIL_EXIT1 ("symlink link2-ok failed");
if (open ("link2-tgt", O_RDWR|O_CREAT, 0777) < 0)
FAIL_EXIT1 ("create of link2-tgt failed");
/* Now we run the tests. */
nftw (".", callback_phys, 10, FTW_PHYS);
nftw (".", callback_log, 10, 0);
/* Did we see the expected number of correct callbacks? */
if (good_cb != EXPECTED_GOOD)
{
FAIL_EXIT1 ("Saw %d good callbacks, expected %d\n",
good_cb, EXPECTED_GOOD);
}
return 0;
}
#include <support/test-driver.c>