Skip to content

Commit

Permalink
FileSystem: implement symlink support in zip archives, fix #83
Browse files Browse the repository at this point in the history
Most of the code is taken from DarkPlaces source code
  • Loading branch information
illwieckz committed Mar 3, 2019
1 parent 9322bda commit a875abe
Showing 1 changed file with 108 additions and 2 deletions.
110 changes: 108 additions & 2 deletions src/common/FileSystem.cpp
Expand Up @@ -909,6 +909,20 @@ class ZipArchive {
return fileInfo.uncompressed_size;
}

// Check if currently open file is a symbolic link
bool CheckSymlink(std::error_code& err) const
{
unz_file_info64 fileInfo;
int result = unzGetCurrentFileInfo64(zipFile, &fileInfo, nullptr, 0, nullptr, 0, nullptr, 0);
if (result != UNZ_OK) {
SetErrorCodeZlib(err, result);
return false;
}
ClearErrorCode(err);
uLong attr = fileInfo.external_fa >> 16;
return ( ( attr & 0120000 ) == 0120000 );
}

// Read from the currently open file
size_t ReadFile(void* buffer, size_t length, std::error_code& err) const
{
Expand Down Expand Up @@ -1244,7 +1258,71 @@ const std::vector<LoadedPakInfo>& GetLoadedPaks()
return loadedPaks;
}

std::string ReadFile(Str::StringRef path, std::error_code& err)
// expandSymlink
// code adapted from DarkPlaces fs.c FS_OpenReadFile
// commit 1c55fd95ff34d9aafb76c927215ea8fbab5210c6
// Copyright 2008 divverent
// GPLv2+
std::string expandSymlink(Str::StringRef path, Str::StringRef content)
{
const char *filename = path.c_str();

char linkbuf[MAX_QPATH];
strncpy(linkbuf, content.c_str(), MAX_QPATH);
int count = strlen(linkbuf);

const char *mergeslash;
char *mergestart;

// Now combine the paths...
mergeslash = strrchr(filename, '/');
mergestart = linkbuf;
if(!mergeslash) {
mergeslash = filename;
}
while(!strncmp(mergestart, "../", 3)) {
mergestart += 3;
while(mergeslash > filename) {
--mergeslash;
if(*mergeslash == '/') {
break;
}
}
}
// Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
if(mergeslash == filename) {
// Either mergeslash == filename, then we just replace the name (done below)
}
else {
// Or, we append the name after mergeslash;
// or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
int spaceNeeded = mergeslash - filename + 1;
int spaceRemoved = mergestart - linkbuf;
if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
{
Log::Debug("symlink: too long path rejected\n");
return "";
}
memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
memcpy(linkbuf, filename, spaceNeeded);
linkbuf[count - spaceRemoved + spaceNeeded] = 0;
mergestart = linkbuf;
}

Log::Debug("found symlink: %s → %s", path, mergestart);

/*
if(CheckNastyPath (mergestart, false))
{
Log::Debug("symlink: nasty path %s rejected\n", mergestart);
return "";
}
*/

return mergestart;
}

std::string ReadFile(Str::StringRef path, int symlinkLevels, std::error_code& err)
{
auto it = fileMap.find(path);
if (it == fileMap.end()) {
Expand Down Expand Up @@ -1298,17 +1376,45 @@ std::string ReadFile(Str::StringRef path, std::error_code& err)
if (err)
return "";

// Close file and check for CRC errors
bool isSymLink = zipFile.CheckSymlink(err);
if (err) {
return "";
}

zipFile.CloseFile(err);
if (err)
return "";

if (isSymLink) {
if (symlinkLevels <= 0) {
Log::Debug("symlink: %s: too many levels of symbolic links", path);
return "";
}
out = expandSymlink(path, out);
if (out[0] == '\0') {
return "";
}
out = ReadFile(out, symlinkLevels -1, err);
if (err) {
return "";
}
return out;
}

// Close file and check for CRC errors
return out;
}

ASSERT_UNREACHABLE();
}

std::string ReadFile(Str::StringRef path, std::error_code& err)
{
return ReadFile(path, 16, err);
if (err)
return "";
}

void CopyFile(Str::StringRef path, const File& dest, std::error_code& err)
{
auto it = fileMap.find(path);
Expand Down

0 comments on commit a875abe

Please sign in to comment.