summaryrefslogtreecommitdiff
path: root/gnulib/import/readlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib/import/readlink.c')
-rw-r--r--gnulib/import/readlink.c50
1 files changed, 40 insertions, 10 deletions
diff --git a/gnulib/import/readlink.c b/gnulib/import/readlink.c
index 4d392ef6995..c4b635ce712 100644
--- a/gnulib/import/readlink.c
+++ b/gnulib/import/readlink.c
@@ -1,5 +1,5 @@
-/* Stub for readlink().
- Copyright (C) 2003-2007, 2009-2020 Free Software Foundation, Inc.
+/* Read the contents of a symbolic link.
+ Copyright (C) 2003-2007, 2009-2021 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
such as DJGPP 2.03 and mingw32. */
ssize_t
-readlink (const char *name, char *buf _GL_UNUSED,
+readlink (char const *file, char *buf _GL_UNUSED,
size_t bufsize _GL_UNUSED)
{
struct stat statbuf;
@@ -37,7 +37,7 @@ readlink (const char *name, char *buf _GL_UNUSED,
/* In general we should use lstat() here, not stat(). But on platforms
without symbolic links, lstat() - if it exists - would be equivalent to
stat(), therefore we can use stat(). This saves us a configure check. */
- if (stat (name, &statbuf) >= 0)
+ if (stat (file, &statbuf) >= 0)
errno = EINVAL;
return -1;
}
@@ -51,24 +51,54 @@ readlink (const char *name, char *buf _GL_UNUSED,
for Solaris 9. */
ssize_t
-rpl_readlink (const char *name, char *buf, size_t bufsize)
+rpl_readlink (char const *file, char *buf, size_t bufsize)
{
# if READLINK_TRAILING_SLASH_BUG
- size_t len = strlen (name);
- if (len && name[len - 1] == '/')
+ size_t file_len = strlen (file);
+ if (file_len && file[file_len - 1] == '/')
{
- /* Even if name without the slash is a symlink to a directory,
+ /* Even if FILE without the slash is a symlink to a directory,
both lstat() and stat() must resolve the trailing slash to
the directory rather than the symlink. We can therefore
safely use stat() to distinguish between EINVAL and
ENOTDIR/ENOENT, avoiding extra overhead of rpl_lstat(). */
struct stat st;
- if (stat (name, &st) == 0)
+ if (stat (file, &st) == 0 || errno == EOVERFLOW)
errno = EINVAL;
return -1;
}
# endif /* READLINK_TRAILING_SLASH_BUG */
- return readlink (name, buf, bufsize);
+
+ ssize_t r = readlink (file, buf, bufsize);
+
+# if READLINK_TRUNCATE_BUG
+ if (r < 0 && errno == ERANGE)
+ {
+ /* Try again with a bigger buffer. This is just for test cases;
+ real code invariably discards short reads. */
+ char stackbuf[4032];
+ r = readlink (file, stackbuf, sizeof stackbuf);
+ if (r < 0)
+ {
+ if (errno == ERANGE)
+ {
+ /* Clear the buffer, which is good enough for real code.
+ Thankfully, no test cases try short reads of enormous
+ symlinks and what would be the point anyway? */
+ r = bufsize;
+ memset (buf, 0, r);
+ }
+ }
+ else
+ {
+ if (bufsize < r)
+ r = bufsize;
+ memcpy (buf, stackbuf, r);
+ }
+ }
+# endif
+
+ return r;
}
#endif /* HAVE_READLINK */