zos: fix readlink for mounts with system variables

On z/OS, fs mounts can have variables starting with '$'. The problem is
that following symlinks that contain these variables can lead to a loop
because readlink($SYSVAR) will return $SYSVAR.  To work around this, we
must resolve the $SYSVAR to an actual path using realpath.

More information about z/OS symlink resolution is available here:

https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/
com.ibm.zos.v2r1.bpxb100/sym.htm

PR-URL: https://github.com/libuv/libuv/pull/1472
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
John Barboza 2017-08-11 06:54:58 -04:00 committed by Ben Noordhuis
parent 3877a90e69
commit 8d3645a19b
3 changed files with 75 additions and 0 deletions

View File

@ -438,7 +438,12 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
return -1;
}
#if defined(__MVS__)
len = os390_readlink(req->path, buf, len);
#else
len = readlink(req->path, buf, len);
#endif
if (len == -1) {
uv__free(buf);

View File

@ -327,3 +327,72 @@ char* mkdtemp(char* path) {
return path;
}
ssize_t os390_readlink(const char* path, char* buf, size_t len) {
ssize_t rlen;
ssize_t vlen;
ssize_t plen;
char* delimiter;
char old_delim;
char* tmpbuf;
char realpathstr[PATH_MAX + 1];
tmpbuf = uv__malloc(len + 1);
if (tmpbuf == NULL) {
errno = ENOMEM;
return -1;
}
rlen = readlink(path, tmpbuf, len);
if (rlen < 0) {
uv__free(tmpbuf);
return rlen;
}
if (rlen < 3 || strncmp("/$", tmpbuf, 2) != 0) {
/* Straightforward readlink. */
memcpy(buf, tmpbuf, rlen);
uv__free(tmpbuf);
return rlen;
}
/*
* There is a parmlib variable at the beginning
* which needs interpretation.
*/
tmpbuf[rlen] = '\0';
delimiter = strchr(tmpbuf + 2, '/');
if (delimiter == NULL)
/* No slash at the end */
delimiter = strchr(tmpbuf + 2, '\0');
/* Read real path of the variable. */
old_delim = *delimiter;
*delimiter = '\0';
if (realpath(tmpbuf, realpathstr) == NULL) {
uv__free(tmpbuf);
return -1;
}
/* realpathstr is not guaranteed to end with null byte.*/
realpathstr[PATH_MAX] = '\0';
/* Reset the delimiter and fill up the buffer. */
*delimiter = old_delim;
plen = strlen(delimiter);
vlen = strlen(realpathstr);
rlen = plen + vlen;
if (rlen > len) {
uv__free(tmpbuf);
errno = ENAMETOOLONG;
return -1;
}
memcpy(buf, realpathstr, vlen);
memcpy(buf + vlen, delimiter, plen);
/* Done using temporary buffer. */
uv__free(tmpbuf);
return rlen;
}

View File

@ -65,5 +65,6 @@ int scandir(const char* maindir, struct dirent*** namelist,
int (*compar)(const struct dirent **,
const struct dirent **));
char *mkdtemp(char* path);
ssize_t os390_readlink(const char* path, char* buf, size_t len);
#endif /* UV_OS390_SYSCALL_H_ */