Cogs.Core
IO.Linux.cpp
1#if !defined( _GNU_SOURCE )
2 #define _GNU_SOURCE
3#endif
4
5#include "IO.h"
6
7#include "MemoryBufferBackedFileContents.h"
8
9#include "../Logging/Logger.h"
10
11#include <cinttypes>
12#include <dlfcn.h>
13#include <fcntl.h>
14#include <fstream>
15#include <regex>
16#include <unistd.h>
17#include <sys/stat.h>
18
19namespace
20{
21 Cogs::Logging::Log envLogger = Cogs::Logging::getLogger("PlatformIO");
22}
23
24void Cogs::IO::readFileAsync(const std::string & fileName, const FileContents::Callback& callback, uint64_t offset, uint64_t size, FileContentsHints hints)
25{
26 if (size == 0 && offset != 0) {
27 LOG_ERROR(envLogger, "readFileAsync: Illegal range, trying to read zero bytes from offset (%" PRIu64 ")", offset);
28 callback(std::unique_ptr<FileContents>());
29 return;
30 }
31
32 std::ifstream file(fileName, std::ios::binary | std::ios::in | std::ios::ate);
33 if (!file.is_open()) {
34 LOG_ERROR(envLogger, "readFileAsync: Failed to open file %s", fileName.c_str());
35 callback(std::unique_ptr<FileContents>());
36 return;
37 }
38
39 file.seekg(0, std::ios::end);
40 {
41 std::streampos fileSize = file.tellg();
42 if (fileSize < 0) {
43 LOG_ERROR(envLogger, "readFileAsync: tellg returned an error.");
44 goto error;
45 }
46 uint64_t readOffset = 0;
47 uint64_t readSize = static_cast<uint64_t>(fileSize);
48 if (size != 0) {
49 if (readSize < offset) {
50 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") is greater than filesize (%" PRIu64 ")", offset, static_cast<uint64_t>(fileSize));
51 goto error;
52 }
53 readOffset = offset;
54 readSize -= readOffset;
55 if (readSize < size) {
56 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") + size(%" PRIu64 ") is greater than filesize (%" PRIu64 ")", offset, size, static_cast<uint64_t>(fileSize));
57 goto error;
58 }
59 readSize = size;
60 }
61
62 if (static_cast<uint64_t>(std::numeric_limits<size_t>::max()) < static_cast<uint64_t>(readSize)) {
63 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") + size(%" PRIu64 ") is greater than max size_t (>4GB read on 32bit?)", offset, size);
64 goto error;
65 }
66
67 auto contents = std::make_unique<MemoryBufferBackedFileContents>(readSize, fileName, hints);
68 file.seekg(static_cast<std::streamoff>(readOffset), std::ios::beg);
69 file.read((char*)contents->bytes.data(), contents->bytes.size());
70 file.close();
71 callback(std::move(contents));
72 }
73 return;
74
75error:
76 file.close();
77 callback(std::unique_ptr<FileContents>());
78 return;
79}
80
81std::unique_ptr<Cogs::FileContents> Cogs::IO::readFileSync(const std::string & fileName, uint64_t offset, uint64_t size, FileContentsHints hints)
82{
83 if (size == 0 && offset != 0) {
84 LOG_ERROR(envLogger, "readFileAsync: Illegal range, trying to read zero bytes from offset (%" PRIu64 ")", offset);
85 return std::unique_ptr<FileContents>();
86 }
87
88 std::ifstream file(fileName, std::ios::binary | std::ios::in | std::ios::ate);
89 if (!file.is_open()) {
90 LOG_ERROR(envLogger, "readFileAsync: Failed to open file %s", fileName.c_str());
91 return std::unique_ptr<FileContents>();
92 }
93
94 file.seekg(0, std::ios::end);
95 std::streampos fileSize = file.tellg();
96 if (fileSize < 0) {
97 LOG_ERROR(envLogger, "readFileAsync: tellg returned an error.");
98 return std::unique_ptr<FileContents>();
99 }
100 uint64_t readOffset = 0;
101 uint64_t readSize = static_cast<uint64_t>(fileSize);
102 if (size != 0) {
103 if (readSize < offset) {
104 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") is greater than filesize (%" PRIu64 ")", offset, static_cast<uint64_t>(fileSize));
105 return std::unique_ptr<FileContents>();
106 }
107 readOffset = offset;
108 readSize -= readOffset;
109 if (readSize < size) {
110 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") + size(%" PRIu64 ") is greater than filesize (%" PRIu64 ")", offset, size, static_cast<uint64_t>(fileSize));
111 return std::unique_ptr<FileContents>();
112 }
113 readSize = size;
114 }
115
116 if (static_cast<uint64_t>(std::numeric_limits<size_t>::max()) < readSize) {
117 LOG_ERROR(envLogger, "readFileAsync: offset (%" PRIu64 ") + size(%" PRIu64 ") is greater than max size_t (>4GB read on 32bit?)", offset, size);
118 return std::unique_ptr<FileContents>();
119 }
120
121 auto contents = std::make_unique<MemoryBufferBackedFileContents>(readSize, fileName, hints);
122 file.seekg(static_cast<std::streamoff>(readOffset), std::ios::beg);
123 file.read((char*)contents->bytes.data(), contents->bytes.size());
124 file.close();
125 return contents;
126}
127
128bool Cogs::IO::readTextFile(std::string& content, const std::string & fileName)
129{
130 std::ifstream in(fileName);
131
132 if (in.good()) {
133 content = std::string((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
134 return true;
135 }
136 return false;
137}
138
139void Cogs::IO::autoExpandEnvironmentVariables(std::string & text)
140{
141 static std::regex env("%([0-9A-Za-z\\/]*)%");
142
143 std::smatch match;
144 while (std::regex_search(text, match, env)) {
145 const char * s = std::getenv(match[1].str().c_str());
146 const std::string environmentVariable(s == nullptr ? "" : s);
147
148 text.replace(match[0].first, match[0].second, environmentVariable);
149 }
150}
151
152std::string Cogs::IO::expandEnvironmentVariables(const std::string& input) {
153 std::string text = input;
154 autoExpandEnvironmentVariables(text);
155 return text;
156}
157
158std::string Cogs::IO::expandEnvironmentVariable(const std::string& name) {
159 const char *tmp = getenv(name.c_str());
160
161 return std::string(tmp ? tmp : "");
162}
163
164char Cogs::IO::pathSeparator() {
165 return '/';
166}
167
168bool Cogs::IO::exists(std::string_view path) {
169 return fs::exists(path);
170}
171
172bool Cogs::IO::isFile(std::string_view path) {
173 return fs::is_regular_file(path);
174}
175
176bool Cogs::IO::isDirectory(std::string_view path) {
177 return fs::is_directory(path);
178}
179
180std::string Cogs::IO::absolute(std::string_view path) {
181 return fs::absolute(fs::path(path)).string();
182}
183
184std::string Cogs::IO::canonical(std::string_view path) {
185 std::error_code ec;
186 return fs::canonical(fs::path(path), ec).string();
187}
188
189bool Cogs::IO::remove(std::string_view path) {
190 return fs::remove(path);
191}
192
193std::string Cogs::IO::combine(std::string_view path, std::string_view extensionPath) {
194 return (fs::path(path).append(extensionPath)).string();
195}
196
197std::string Cogs::IO::expandPath(std::string_view fileName) {
198 return expandEnvironmentVariables(fs::path(fileName).string());
199}
200
201std::string Cogs::IO::parentPath(std::string_view str) {
202 return fs::path(str.begin(), str.end()).parent_path().string();
203}
204
205std::string Cogs::IO::relativePath(std::string_view str) {
206 return fs::path(str).relative_path().string();
207}
208
209std::string Cogs::IO::relativePath(std::string_view str, std::string_view base) {
210 auto dir = fs::path(base);
211 auto p = fs::path(str);
212
213 dir = fs::absolute(dir);
214 p = fs::absolute(p);
215
216 auto beginA = p.begin();
217 auto beginB = dir.begin();
218
219 while (beginA != p.end() && beginB != dir.end()) {
220 if (*beginA != *beginB) break;
221
222 ++beginA;
223 ++beginB;
224 }
225
226 fs::path relative;
227 for (; beginB != dir.end(); ++beginB) {
228 relative /= "..";
229 }
230
231 for (; beginA != p.end(); ++beginA) {
232 relative /= *beginA;
233 }
234
235 return relative.string();
236}
237
238std::string Cogs::IO::getPathToExe() {
239 char path[200] = {};
240
241 readlink("/proc/self/exe", path, sizeof(path) - 1);
242 return path;
243}
244
245std::string Cogs::IO::getPathRelativeToDll(const std::string& fileName, void* handle) {
246 Dl_info info;
247 std::string fullPath;
248
249 if (!dladdr(handle, &info)) {
250 return fileName;
251 }
252 fullPath = info.dli_fname;
253 fullPath = fullPath.substr(0, fullPath.find_last_of('/') + 1);
254 fullPath += fileName;
255
256 return fullPath;
257}
258
259std::string Cogs::IO::getTempPath() {
260 return "/tmp/";
261}
262
263std::string Cogs::IO::getAppDataPath() {
264 return getOrgLocalDataPath() + "/Cogs/";
265}
266
267std::string Cogs::IO::getOrgLocalDataPath() {
268 if(getuid() == 0){
269 mkdir("/etc/Kongsberg", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
270 return std::string("/etc/Kongsberg Digital/");
271 }
272 else{
273 std::string path = expandEnvironmentVariable("HOME");
274 path += "/.local/share/Kongsberg Digital/";
275 return path;
276 }
277}
278
279bool Cogs::IO::isAbsolute(std::string_view str) {
280 return (str.size() >= 1) && (str[0] == '/');
281}
282
283bool Cogs::IO::isRelative(std::string_view str) {
284 return !isAbsolute(str);
285}
286
287std::string Cogs::IO::fileName(std::string_view str) {
288 size_t lastSep = str.find_last_of("/\\");
289 if (lastSep != std::string_view::npos) {
290 str = str.substr(lastSep + 1);
291 }
292
293 return std::string(str);
294}
295
296std::string Cogs::IO::extension(std::string_view str) {
297 size_t lastSep = str.find_last_of("/\\");
298 if (lastSep != std::string_view::npos) {
299 str = str.substr(lastSep + 1);
300 }
301
302 return fs::path(str).extension().string();
303}
304
305std::string Cogs::IO::stem(std::string_view str) {
306 size_t lastSep = str.find_last_of("/\\");
307 if (lastSep != std::string_view::npos) {
308 str = str.substr(lastSep + 1);
309 }
310
311 return fs::path(str.begin(), str.end()).stem().string();
312}
313
314void Cogs::IO::replaceAll(std::string& str, std::string_view from, std::string_view to) {
315 if (from.empty()) return;
316
317 size_t startPos = 0;
318 while ((startPos = str.find(from, startPos)) != std::string::npos) {
319 str.replace(startPos, from.length(), to);
320 startPos += to.length();
321 }
322}
323
324Cogs::IO::FileTimeType Cogs::IO::getFileTime(std::string_view path) {
325 std::error_code ec;
326 return fs::last_write_time(fs::path(path.begin(), path.end()), ec);
327}
328
329uint64_t Cogs::IO::getFileSize(std::string_view path) {
330 struct stat st;
331 if(::stat(std::string(path).c_str(), &st) != 0) {
332 return 0;
333 }
334 return static_cast<uint64_t>(st.st_size);
335}
336
337bool Cogs::IO::writeBinaryFile(const std::string & fileName, const void * content, size_t contentSize) {
338 fs::path path(fileName);
339
340 if (path.has_parent_path() && !fs::exists(path.parent_path()) && !fs::create_directories(path.parent_path())) {
341 return false;
342 }
343
344 int fd = open(path.string().c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0664);
345 if(fd == -1) return false;
346
347 const uint8_t *src = static_cast<const uint8_t*>(content);
348 size_t remaining = contentSize;
349 while(remaining){
350 size_t write_size = remaining;
351 ssize_t ret = write(fd, src, write_size);
352 if(ret == 0 || (ret == -1 && errno != EINTR)){
353 close(fd);
354 return false;
355 }
356 if(ret > 0){
357 size_t written = ret;
358 src += written;
359 remaining -= written;
360 }
361 }
362
363 close(fd);
364 return true;
365}
366
367bool Cogs::IO::createDirectories(std::string_view fileName) {
368 fs::path path(fileName);
369
370 if (path.has_parent_path() && !fs::exists(path.parent_path()) && !fs::create_directories(path.parent_path())) {
371 return false;
372 }
373 return true;
374}
375
376Cogs::IO::RecursiveDirIterator Cogs::IO::directoryIterator(std::string_view directory) {
377 return RecursiveDirIterator(directory);
378}
379
380std::string Cogs::IO::getPath(const DirectoryEntry& entry) {
381 return entry.path().string();
382}
383
384Cogs::FileHandle::Ptr Cogs::IO::openFile(std::string_view path, FileHandle::OpenMode openMode, FileHandle::AccessMode accessMode) {
385 return FileHandle::open(path, openMode, accessMode);
386}
387
388void Cogs::IO::closeFile(const FileHandle::Ptr& handle) {
389 if (handle) handle->close();
390}
391
392size_t Cogs::IO::fileSize(const FileHandle::Ptr& handle) {
393 return handle ? handle->getSize() : 0;
394}
395
396void* Cogs::IO::mapFile(const FileHandle::Ptr& handle, FileHandle::ProtectionMode protectionMode, size_t offset, size_t size) {
397 return handle ? handle->map(protectionMode, offset, size) : nullptr;
398}
399
400void Cogs::IO::unmapFile(const FileHandle::Ptr& handle) {
401 if (handle) handle->unmap();
402}
403
404#ifdef __linux__
405
406
407bool Cogs::IO::copyFile(std::string_view src, std::string_view dest)
408{
409 int fd_in = open(src.data(), O_RDONLY);
410 if(fd_in == -1){
411 return false;
412 }
413 struct stat s;
414 if(fstat(fd_in, &s) == -1){
415 close(fd_in);
416 return false;
417 }
418 int fd_out = open(dest.data(), O_CREAT | O_WRONLY | O_TRUNC, s.st_mode&0x777);
419 if(fd_out == -1){
420 close(fd_in);
421 return false;
422 }
423
424 size_t remaining = s.st_size;
425 while(remaining){
426 ssize_t ret = copy_file_range(fd_in, nullptr, fd_out, nullptr, remaining, 0);
427 if(ret == -1){
428 close(fd_in);
429 close(fd_out);
430 return false;
431 }
432 remaining -= ret;
433 }
434
435 close(fd_in);
436 close(fd_out);
437 return true;
438}
439
440#else
441#include <filesystem>
442bool Cogs::IO::copyFile(std::string_view src, std::string_view dest)
443{
444 std::error_code ec;
445 if(!std::filesystem::copy_file(src, dest, ec)) {
446 return false;
447 }
448 if(!ec) {
449 LOG_ERROR(envLogger, "Failed to copy %.*s to %.*s: %s",
450 StringViewFormat(src),
451 StringViewFormat(dest),
452 ec.message().c_str());
453 return false;
454 }
455 return true;
456}
457#endif
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180