Buffer-overflow safe readlink() in C++
Problem:
You want to use readlink()
to get the file or directory a symbolic link points to, but you don’t know the buffer size that is required to store the symlink destination. You don’t want to allocate an incredibly large amount of memory (whatever amount you choose, it could always be insufficient), but you won’t risk buffer overflows.
Solution:
readlink()
returns -1 with errno ==
EINVAL
if the buffer is too small. Therefore, you can simply call readlink iteratively with increasing buffer sizes.
This implementation uses std::string
and returns an empty string in case of error (errno
is still set in that case). If the file is no symlink, the filename
argument is returned.
//Copyright (c) 2013 Uli Köhler
//License: Apache2.0
#include <unistd.h>
/**
* A buffer-overflow-safe readlink() wrapper for C++.
* @return A string containing the readlink()ed filename, or
* an empty string with errno being set to the appropriate error.
* See the readlink() man(2) for errno details.
*/
static std::string safeReadlink(const std::string& filename) {
size_t bufferSize = 255;
//Increase buffer size until the buffer is large enough
while (1) {
char* buffer = new char[bufferSize];
size_t rc = readlink (filename.c_str(), buffer, bufferSize);
if (rc == -1) {
delete[] buffer;
if(errno == EINVAL) {
//We know that bufsize is positive, so
// the file is not a symlink.
errno = 0;
return filename;
} else if(errno == ENAMETOOLONG) {
bufferSize += 255;
} else {
//errno still contains the error code
return "";
}
} else {
//Success! rc == number of valid chars in buffer
errno = 0;
return string(buffer, rc);
}
}
}