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:
parent
3877a90e69
commit
8d3645a19b
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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_ */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user