The return type of readlink() per POSIX is ssize_t, but most existing
callers use int, so fix that. Also fix the return type of the Windows
implementation to match.
In _pglstat64(), we neglected to handle the case where the output
buffer is not large enough and the result would be truncated. This
case actually can't happen, because the Windows pgreadlink()
implementation doesn't ever return that case, but adding this seems
good for consistency with _pgstat64(), which already had this check,
and in case pgreadlink() ever changes in this regard.
Some callers of readlink(), in particular _pglstat64(), assume that
errno == EINVAL means that the file was not a symlink. But Windows
pgreadlink() also sets EINVAL in other cases, in particular if the
buffer was too small. This could result in incorrect behavior, so
pick a different errno. (There might be other cases where EINVAL is
set inappropriately, but they are outside the theme of this patch.)
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Discussion: https://www.postgresql.org/message-id/flat/
f9aab072-0078-49e4-ab93-
3b08086a4406@eisentraut.org
if (de_type == PGFILETYPE_LNK)
{
StringInfoData escapedpath;
- int rllen;
+ ssize_t rllen;
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
if (rllen < 0)
if (strcmp(path, "./pg_tblspc") == 0 && S_ISLNK(statbuf.st_mode))
{
char linkpath[MAXPGPATH];
- int rllen;
+ ssize_t rllen;
rllen = readlink(pathbuf, linkpath, sizeof(linkpath));
if (rllen < 0)
{
char sourcepath[MAXPGPATH];
char targetpath[MAXPGPATH];
- int rllen;
+ ssize_t rllen;
struct stat st;
/*
{
#ifdef HAVE_READLINK
char link_target[MAXPGPATH];
- int len;
+ ssize_t len;
const char *cur_name;
/*
Oid oid;
char tblspcdir[MAXPGPATH];
char link_target[MAXPGPATH];
- int link_length;
cb_tablespace *ts;
cb_tablespace *otherts;
PGFileType type;
*/
if (type == PGFILETYPE_LNK)
{
+ ssize_t link_length;
cb_tablespace_mapping *tsmap;
/* Read the link target. */
else if (S_ISLNK(fst.st_mode))
{
char link_target[MAXPGPATH];
- int len;
+ ssize_t len;
len = readlink(fullpath, link_target, sizeof(link_target));
if (len < 0)
*/
#if defined(WIN32) && !defined(__CYGWIN__)
extern int pgsymlink(const char *oldpath, const char *newpath);
-extern int pgreadlink(const char *path, char *buf, size_t size);
+extern ssize_t pgreadlink(const char *path, char *buf, size_t size);
#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath)
#define readlink(path, buf, size) pgreadlink(path, buf, size)
* Note: Some CYGWIN includes might #define WIN32.
*/
extern int pgsymlink(const char *oldpath, const char *newpath);
-extern int pgreadlink(const char *path, char *buf, size_t size);
+extern ssize_t pgreadlink(const char *path, char *buf, size_t size);
#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath)
#define readlink(path, buf, size) pgreadlink(path, buf, size)
/*
* pgreadlink - uses Win32 junction points
*/
-int
+ssize_t
pgreadlink(const char *path, char *buf, size_t size)
{
DWORD attr;
if (r <= 0)
{
- errno = EINVAL;
+ /*
+ * If the buffer is not sufficient, we must return a different errno
+ * than EINVAL, because callers will take EINVAL to mean it was not a
+ * symlink. The correct POSIX behavior would be to fill the buffer up
+ * to the size, but we can't easily do that here.
+ */
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ errno = ENAMETOOLONG;
+ else
+ errno = EINVAL;
+
return -1;
}
ret = -1;
}
}
+ else if (size >= sizeof(next))
+ {
+ errno = ENAMETOOLONG;
+ ret = -1;
+ }
else
{
/* It's a junction point, so report it as a symlink. */
}
return -1;
}
- if (size >= sizeof(next))
+ else if (size >= sizeof(next))
{
errno = ENAMETOOLONG;
return -1;