From 23ba9a736af11ca355b337d0a3d51daef82be0ef Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 13 Dec 2020 17:16:01 -0800 Subject: [PATCH 4/5] canonicalize: fix AIX test failures Problem reported by Bruno Haible in: https://lists.gnu.org/r/bug-gnulib/2020-12/msg00109.html * lib/canonicalize.c (canonicalize_filename_mode): When testing a file name ending in '/', use stat rather than readlink, so that it does the right thing on AIX. * modules/canonicalize (Depends-on): Add readlink, to pull in the recent changes in the Gnulib readlink module. --- ChangeLog | 9 +++++++++ lib/canonicalize.c | 37 ++++++++++++++++++++++--------------- modules/canonicalize | 1 + 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1c5fd762d..b8213ada0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2020-12-13 Paul Eggert + canonicalize: fix AIX test failures + Problem reported by Bruno Haible in: + https://lists.gnu.org/r/bug-gnulib/2020-12/msg00109.html + * lib/canonicalize.c (canonicalize_filename_mode): + When testing a file name ending in '/', use stat rather than + readlink, so that it does the right thing on AIX. + * modules/canonicalize (Depends-on): Add readlink, to pull in the + recent changes in the Gnulib readlink module. + Assume readlink/readlinkat ERANGE fix * lib/areadlink-with-size.c (areadlink_with_size): * lib/areadlinkat-with-size.c (areadlinkat_with_size): diff --git a/lib/canonicalize.c b/lib/canonicalize.c index e44793d14..e50347b10 100644 --- a/lib/canonicalize.c +++ b/lib/canonicalize.c @@ -211,9 +211,13 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) for (end = start; *end && !ISSLASH (*end); ++end) /* Nothing. */; - if (end - start == 1 && start[0] == '.') + /* Length of this file name component; it can be zero if a file + name ends in '/'. */ + idx_t startlen = end - start; + + if (startlen == 1 && start[0] == '.') /* nothing */; - else if (end - start == 2 && start[0] == '.' && start[1] == '.') + else if (startlen == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rname + prefix_len + 1) @@ -228,27 +232,28 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) if (!ISSLASH (dest[-1])) *dest++ = '/'; - if (rname_limit - dest <= end - start) + if (rname_limit - dest <= startlen) { idx_t dest_offset = dest - rname; idx_t new_size = rname_limit - rname; - if (end - start + 1 > PATH_MAX) - new_size += end - start + 1; - else - new_size += PATH_MAX; + new_size = startlen + 1 <= PATH_MAX ? startlen + 1 : PATH_MAX; rname = xrealloc (rname, new_size); rname_limit = rname + new_size; dest = rname + dest_offset; } - dest = memcpy (dest, start, end - start); - dest += end - start; + dest = memcpy (dest, start, startlen); + dest += startlen; *dest = '\0'; + /* If STARTLEN == 0, RNAME ends in '/'; use stat rather than + readlink, because readlink might fail with EINVAL without + checking whether RNAME sans '/' is valid. */ char discard; - char *buf = logical ? NULL : areadlink (rname); + struct stat st; + char *buf = logical || startlen == 0 ? NULL : areadlink (rname); if (buf) { /* A physical traversal and RNAME is a symbolic link. */ @@ -262,15 +267,14 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) Get the device and inode of the parent directory, as pre-2017 POSIX says this info is not reliable for symlinks. */ - dest[- (end - start)] = '\0'; - struct stat st; + dest[- startlen] = '\0'; if (stat (*rname ? rname : ".", &st) != 0) { saved_errno = errno; free (buf); goto error; } - dest[- (end - start)] = *start; + dest[- startlen] = *start; /* Detect loops. We cannot use the cycle-check module here, since it's possible to encounter the same parent @@ -338,13 +342,16 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) free (buf); } else if (can_exist != CAN_MISSING - && (!logical || readlink (rname, &discard, 1) < 0)) + && (startlen == 0 + ? stat (rname, &st) < 0 + : !logical && readlink (rname, &discard, 1) < 0)) { saved_errno = errno; switch (saved_errno) { case EINVAL: - /* RNAME exists and is not symbolic link. */ + case EOVERFLOW: /* Possible with stat. */ + /* RNAME exists and is not a symbolic link. */ break; case ENOENT: diff --git a/modules/canonicalize b/modules/canonicalize index 65c5011f7..ae3fbd3ab 100644 --- a/modules/canonicalize +++ b/modules/canonicalize @@ -18,6 +18,7 @@ idx memmove nocrash pathmax +readlink stat sys_stat xalloc -- 2.27.0