12 #include <ext/sha256/sha256.h>
13 #include <ext/zlib/zlib.h>
40 ArchiveFileSystem::ArchiveFileSystem(
const string& fileName): fileName(fileName), ifsMutex(
"afs-ifs-mutex")
43 ifs.open(std::filesystem::u8path(
fileName), ifstream::binary);
44 if (
ifs.is_open() ==
false) {
49 uint64_t fileInformationOffset;
50 ifs.seekg(-
static_cast<int64_t
>(
sizeof(fileInformationOffset)), ios::end);
51 ifs.read((
char*)&fileInformationOffset,
sizeof(fileInformationOffset));
52 ifs.seekg(fileInformationOffset, ios::beg);
55 while (
true ==
true) {
57 ifs.read((
char*)&nameSize,
sizeof(nameSize));
58 if (nameSize == 0)
break;
61 fileInformation.
name.resize(nameSize);
62 ifs.read(fileInformation.
name.data(), nameSize);
63 ifs.read((
char*)&fileInformation.
bytes,
sizeof(fileInformation.
bytes));
66 ifs.read((
char*)&fileInformation.
offset,
sizeof(fileInformation.
offset));
88 auto _pathName = pathName;
89 if (_pathName.empty() ==
false && StringTools::endsWith(pathName,
"/") ==
false) _pathName+=
"/";
91 const auto&
fileName = fileInformation.name;
92 if (StringTools::startsWith(
fileName, _pathName) ==
true) {
94 if (filter !=
nullptr && filter->
accept(
99 Console::println(
"StandardFileSystem::list(): Filter::accept(): " + pathName +
"/" +
fileName +
": " + exception.what());
102 files.push_back(StringTools::substring(
fileName, pathName.size()));
105 sort(files.begin(), files.end());
119 if (StringTools::startsWith(relativeFileName,
"./") ==
true) relativeFileName = StringTools::substring(relativeFileName, 2);
127 auto relativeFileName = pathName +
"/" +
fileName;
128 if (StringTools::startsWith(relativeFileName,
"./") ==
true) relativeFileName = StringTools::substring(relativeFileName, 2);
135 const auto& fileInformation = fileInformationIt->second;
137 return fileInformation.executable;
141 throw FileSystemException(
"ArchiveFileSystem::createPath(): This operation is not supported in archive file system");
146 auto relativeFileName = pathName +
"/" +
fileName;
147 if (StringTools::startsWith(relativeFileName,
"./") ==
true) relativeFileName = StringTools::substring(relativeFileName, 2);
154 const auto& fileInformation = fileInformationIt->second;
156 return fileInformation.bytes;
167 unsigned char in[
CHUNK];
168 unsigned char out[
CHUNK];
171 strm.zalloc = Z_NULL;
173 strm.opaque = Z_NULL;
175 strm.next_in = Z_NULL;
176 ret = inflateInit(&strm);
178 throw FileSystemException(
"ArchiveFileSystem::decompress(): Error while decompressing: inflate init");
182 size_t outPosition = 0;
183 size_t inPosition = 0;
184 size_t inBytes = inContent.size();
187 auto inStartPosition = inPosition;
188 for (
size_t i = 0; i <
CHUNK; i++) {
189 if (inPosition == inBytes)
break;
190 in[i] = inContent[inPosition];
193 strm.avail_in = inPosition - inStartPosition;
194 if (strm.avail_in == 0)
break;
199 strm.avail_out =
CHUNK;
201 ret = inflate(&strm, Z_NO_FLUSH);
202 assert(ret != Z_STREAM_ERROR);
205 throw FileSystemException(
"ArchiveFileSystem::decompress(): Error while decompressing: Z_NEED_DICT");
208 (void)inflateEnd(&strm);
209 throw FileSystemException(
"ArchiveFileSystem::decompress(): Error while decompressing: Z_DATA_ERROR | Z_MEM_ERROR");
211 have =
CHUNK - strm.avail_out;
212 for (
size_t i = 0; i < have; i++) {
213 outContent[outPosition++] = out[i];
215 }
while (strm.avail_out == 0);
218 }
while (ret != Z_STREAM_END);
221 (void) inflateEnd(&strm);
224 if (ret != Z_STREAM_END) {
225 throw FileSystemException(
"ArchiveFileSystem::decompress(): Error while decompressing: missing eof");
231 auto relativeFileName = pathName +
"/" +
fileName;
232 if (StringTools::startsWith(relativeFileName,
"./") ==
true) relativeFileName = StringTools::substring(relativeFileName, 2);
239 const auto& fileInformation = fileInformationIt->second;
245 ifs.seekg(fileInformation.offset, ios::beg);
249 if (fileInformation.compressed == 1) {
250 vector<uint8_t> compressedBuffer;
251 compressedBuffer.resize(fileInformation.bytesCompressed);
252 ifs.read((
char*)compressedBuffer.data(), fileInformation.bytesCompressed);
254 vector<uint8_t> decompressedBuffer;
255 decompressedBuffer.resize(fileInformation.bytes);
256 decompress(compressedBuffer, decompressedBuffer);
257 result.append((
char*)decompressedBuffer.data(), fileInformation.bytes);
259 vector<uint8_t> buffer;
260 buffer.resize(fileInformation.bytes);
261 ifs.read((
char*)buffer.data(), fileInformation.bytes);
263 result.append((
char*)buffer.data(), fileInformation.bytes);
271 throw FileSystemException(
"ArchiveFileSystem::setContentFromString(): This operation is not supported in archive file system");
277 auto relativeFileName = pathName +
"/" +
fileName;
278 if (StringTools::startsWith(relativeFileName,
"./") ==
true) relativeFileName = StringTools::substring(relativeFileName, 2);
285 const auto& fileInformation = fileInformationIt->second;
291 ifs.seekg(fileInformation.offset, ios::beg);
294 if (fileInformation.compressed == 1) {
295 vector<uint8_t> compressedContent;
296 compressedContent.resize(fileInformation.bytesCompressed);
297 ifs.read((
char*)compressedContent.data(), fileInformation.bytesCompressed);
299 content.resize(fileInformation.bytes);
302 content.resize(fileInformation.bytes);
303 ifs.read((
char*)content.data(), fileInformation.bytes);
309 throw FileSystemException(
"ArchiveFileSystem::setContent(): This operation is not supported in archive file system");
315 contentAsString = StringTools::replace(contentAsString,
"\r",
"");
319 content.push_back(StringTools::replace(t.
nextToken(),
"\r",
""));
325 throw FileSystemException(
"ArchiveFileSystem::setContentFromStringArray(): This operation is not supported in archive file system");
329 string unixPathName = StringTools::replace(pathName,
"\\",
"/");
330 string unixFileName = StringTools::replace(
fileName,
"\\",
"/");
333 auto pathString =
composeURI(unixPathName, unixFileName);
336 vector<string> pathComponents;
344 for (
auto i = 0; i < pathComponents.size(); i++) {
345 auto pathComponent = pathComponents[i];
346 if (pathComponent ==
".") {
347 pathComponents[i].clear();
349 if (pathComponent ==
"..") {
350 pathComponents[i].clear();
352 for (
int pathComponentReplaced = 0; pathComponentReplaced < 1 && j >= 0; ) {
353 if (pathComponents[j].empty() ==
false) {
354 pathComponents[j].clear();
355 pathComponentReplaced++;
363 string canonicalPath =
"";
364 bool slash = StringTools::startsWith(pathString,
"/");
365 for (
auto i = 0; i < pathComponents.size(); i++) {
366 auto pathComponent = pathComponents[i];
367 if (pathComponent.empty() ==
true) {
370 canonicalPath = canonicalPath + (slash ==
true?
"/":
"") + pathComponent;
376 auto canonicalPathString = canonicalPath.empty() ==
true?
"/":canonicalPath;
386 return canonicalPathString;
397 string unixFileName = StringTools::replace(
fileName, L
'\\', L
'/');
398 int32_t lastPathSeparator = StringTools::lastIndexOf(unixFileName, L
'/');
399 if (lastPathSeparator == -1)
return ".";
400 return StringTools::substring(unixFileName, 0, lastPathSeparator);
404 string unixFileName = StringTools::replace(
fileName, L
'\\', L
'/');
405 int32_t lastPathSeparator = StringTools::lastIndexOf(unixFileName, L
'/');
406 if (lastPathSeparator == -1)
return fileName;
407 return StringTools::substring(unixFileName, lastPathSeparator + 1, unixFileName.length());
412 if (idx == string::npos) {
420 throw FileSystemException(
"ArchiveFileSystem::createPath(): This operation is not supported in archive file system");
424 throw FileSystemException(
"ArchiveFileSystem::removePath(): This operation is not supported in archive file system");
428 throw FileSystemException(
"ArchiveFileSystem::removeFile(): This operation is not supported in archive file system");
432 throw FileSystemException(
"ArchiveFileSystem::rename(): This operation is not supported in archive file system");
436 throw FileSystemException(
"ArchiveFileSystem::getThumbnailAttachment(): This operation is not supported in archive file system");
440 throw FileSystemException(
"ArchiveFileSystem::getThumbnailAttachment(): This operation is not supported in archive file system");
444 ifs.seekg(0, ios::end);
445 auto bytesTotal =
ifs.tellg();
446 ifs.seekg(0, ios::beg);
448 uint8_t input[16384];
449 unsigned char digest[SHA256::DIGEST_SIZE];
450 memset(digest, 0, SHA256::DIGEST_SIZE);
454 int64_t bytesRead = 0LL;
455 while (bytesRead < bytesTotal) {
456 auto bytesToRead = Math::min(
static_cast<int64_t
>(bytesTotal) - bytesRead,
sizeof(input));
457 ifs.read((
char*)input, bytesToRead);
458 ctx.update((
const uint8_t*)input, bytesToRead);
459 bytesRead+= bytesToRead;
463 char buf[2 * SHA256::DIGEST_SIZE + 1];
464 buf[2 * SHA256::DIGEST_SIZE] = 0;
465 for (
int i = 0; i < SHA256::DIGEST_SIZE; i++) sprintf(buf + i * 2,
"%02x", digest[i]);
466 return std::string(buf);
Archive file system implementation.
const string removeFileExtension(const string &fileName)
Remove file extension, e.g.
bool isDrive(const string &uri)
Check if file is a drive (applies to Microsoft Windows only)
map< string, FileInformation > fileInformations
void setContentFromString(const string &pathName, const string &fileName, const string &content)
Set content from string.
virtual ~ArchiveFileSystem()
Public destructor.
const string composeURI(const string &pathName, const string &fileName)
Compose URI from path name and file name.
const string getPathName(const string &uri)
Get path name.
const string getCanonicalURI(const string &pathName, const string &fileName)
Get canonical URI from given path name and file name.
void createPath(const string &pathName)
Create path.
bool getThumbnailAttachment(const string &pathName, const string &fileName, vector< uint8_t > &thumbnailAttachmentContent)
Reads a thumbnail attachment from binary file.
void decompress(vector< uint8_t > &inContent, vector< uint8_t > &outContent)
Decompress from archive.
void list(const string &pathName, vector< string > &files, FileNameFilter *filter=nullptr, bool addDrives=false)
List files for given path and filter by a file name filter if not null.
void changePath(const string &pathName)
Change path.
void removePath(const string &pathName, bool recursive)
Remove path.
void setExecutable(const string &pathName, const string &fileName) override
Set up file to be an executable file.
void setContent(const string &pathName, const string &fileName, const vector< uint8_t > &content)
Set file content.
void getContent(const string &pathName, const string &fileName, vector< uint8_t > &content)
Get file content.
const string & getArchiveFileName()
void setContentFromStringArray(const string &pathName, const string &fileName, const vector< string > &content)
Set file content as string array.
const string computeSHA256Hash()
Compute SHA256 hash.
void removeFile(const string &pathName, const string &fileName)
Remove file.
uint64_t getFileSize(const string &pathName, const string &fileName)
Return file size of given file.
void getContentAsStringArray(const string &pathName, const string &fileName, vector< string > &content)
Get file content as string array.
const string getFileName(const string &uri)
Get file name.
bool isPath(const string &uri)
Check if file is a path.
bool exists(const string &uri)
Check if file exists.
const string getCurrentWorkingPathName()
Get current working path name.
bool isExecutable(const string &pathName, const string &fileName) override
Returns if file is a executable file.
const string getContentAsString(const string &pathName, const string &fileName)
Get content as string.
void rename(const string &fileNameFrom, const string &fileNameTo)
Rename file.
void unlock()
Unlocks this mutex.
void lock()
Locks the mutex, additionally mutex locks will block until other locks have been unlocked.
const string & nextToken()
void tokenize(const string &str, const string &delimiters, bool emptyTokens=false)
Tokenize.
std::exception Exception
Exception base class.
File system file name filter interface.
virtual bool accept(const string &path, const string &file)=0
Accept a file.