LCOV - code coverage report
Current view: top level - core/filesystem - SPFilesystem.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 226 366 61.7 %
Date: 2024-05-12 00:16:13 Functions: 33 41 80.5 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPFilesystem.h"
      25             : 
      26             : #if WIN32
      27             : #include "SPLog.h"
      28             : #else
      29             : #include <unistd.h>
      30             : #endif
      31             : 
      32             : namespace STAPPLER_VERSIONIZED stappler::filepath {
      33             : 
      34             : SPUNUSED static bool inAppBundle(StringView path);
      35             : 
      36             : }
      37             : 
      38             : namespace STAPPLER_VERSIONIZED stappler::filesystem {
      39             : 
      40         300 : File File::open_tmp(StringView prefix, bool delOnClose) {
      41         300 :         if (prefix.empty()) {
      42           0 :                 prefix = StringView("sa.tmp");
      43             :         }
      44             : 
      45             : #if WIN32
      46             :         log::warn("filesystem", "File::open_tmp unavailable on win32");
      47             : #else
      48         300 :         char buf[256] = { 0 };
      49         300 :         const char *tmp = P_tmpdir;
      50         300 :         size_t len = strlen(tmp);
      51         300 :         strcpy(&buf[0], tmp);
      52         300 :         strcpy(&buf[len], "/");
      53         300 :         strncpy(&buf[len + 1], prefix.data(), prefix.size());
      54         300 :         len += prefix.size();
      55         300 :         strcpy(&buf[len + 1], "XXXXXX");
      56             : 
      57         300 :         if (auto fd = ::mkstemp(buf)) {
      58         300 :                 if (auto f = ::fdopen(fd, "wb+")) {
      59         300 :                         auto ret = File(f, delOnClose ? Flags::DelOnClose : Flags::None);
      60         300 :                         ret.set_tmp_path(buf);
      61         300 :                         return ret;
      62         300 :                 }
      63             :         }
      64             : #endif
      65             : 
      66           0 :         return File();
      67             : }
      68             : 
      69         325 : File::File() : _isBundled(false), _nativeFile(nullptr) { }
      70       22859 : File::File(FILE *f, Flags flags) : _isBundled(false), _flags(flags), _nativeFile(f) {
      71       22859 :         if (is_open()) {
      72       22859 :                 auto pos = seek(0, io::Seek::Current);
      73       22859 :                 auto size = seek(0, io::Seek::End);
      74       22859 :                 if (pos != maxOf<size_t>()) {
      75       22859 :                         seek(pos, io::Seek::Set);
      76             :                 }
      77       22859 :                 _size = (size != maxOf<size_t>())?size:0;
      78             :         }
      79       22859 : }
      80           0 : File::File(void *f) : _isBundled(true), _platformFile(f) {
      81           0 :         if (is_open()) {
      82           0 :                 auto pos = seek(0, io::Seek::Current);
      83           0 :                 auto size = seek(0, io::Seek::End);
      84           0 :                 if (pos != maxOf<size_t>()) {
      85           0 :                         seek(pos, io::Seek::Set);
      86             :                 }
      87           0 :                 _size = (size != maxOf<size_t>())?size:0;
      88             :         }
      89           0 : }
      90             : 
      91           0 : File::File(void *f, size_t s) : _isBundled(true), _size(s), _platformFile(f) { }
      92             : 
      93         450 : File::File(File && f) : _isBundled(f._isBundled), _size(f._size) {
      94         450 :         if (_isBundled) {
      95           0 :                 _platformFile = f._platformFile;
      96           0 :                 f._platformFile = nullptr;
      97             :         } else {
      98         450 :                 _nativeFile = f._nativeFile;
      99         450 :                 f._nativeFile = nullptr;
     100             :         }
     101         450 :         f._size = 0;
     102         450 :         if (f._buf[0] != 0) {
     103         425 :                 memcpy(_buf, f._buf, 256);
     104             :         }
     105         450 : }
     106             : 
     107         175 : File & File::operator=(File && f) {
     108         175 :         _isBundled = f._isBundled;
     109         175 :         _size = f._size;
     110         175 :         if (_isBundled) {
     111           0 :                 _platformFile = f._platformFile;
     112           0 :                 f._platformFile = nullptr;
     113             :         } else {
     114         175 :                 _nativeFile = f._nativeFile;
     115         175 :                 f._nativeFile = nullptr;
     116             :         }
     117         175 :         f._size = 0;
     118         175 :         if (f._buf[0] != 0) {
     119         175 :                 memcpy(_buf, f._buf, 256);
     120             :         }
     121         175 :         return *this;
     122             : }
     123             : 
     124       23634 : File::~File() {
     125       23634 :         close();
     126       23634 : }
     127             : 
     128      660809 : size_t File::read(uint8_t *buf, size_t nbytes) {
     129      660809 :         if (is_open()) {
     130      660809 :                 if (!_isBundled) {
     131      660809 :                         size_t remains = _size - ftell(_nativeFile);
     132      660809 :                         if (nbytes > remains) {
     133         975 :                                 nbytes = remains;
     134             :                         }
     135      660809 :                         if (fread(buf, nbytes, 1, _nativeFile) == 1) {
     136      660809 :                                 return nbytes;
     137             :                         }
     138             :                 } else {
     139           0 :                         return filesystem::platform::_read(_platformFile, buf, nbytes);
     140             :                 }
     141             :         }
     142           0 :         return 0;
     143             : }
     144             : 
     145       92511 : size_t File::seek(int64_t offset, io::Seek s) {
     146       92511 :         if (is_open()) {
     147       92511 :                 if (!_isBundled) {
     148       92511 :                         int whence = SEEK_SET;
     149       92511 :                         switch (s) {
     150       46793 :                         case io::Seek::Set: whence = SEEK_SET; break;
     151       22859 :                         case io::Seek::Current: whence = SEEK_CUR; break;
     152       22859 :                         case io::Seek::End: whence = SEEK_END; break;
     153             :                         }
     154             : 
     155       92511 :                         if (offset != 0 || s != io::Seek::Current) {
     156       69652 :                                 if (fseek(_nativeFile, long(offset), whence) != 0) {
     157           0 :                                         return maxOf<size_t>();
     158             :                                 }
     159             :                         }
     160       92511 :                         auto p = ftell(_nativeFile);
     161       92511 :                         if (p >= 0) {
     162       92511 :                                 return static_cast<size_t>(p);
     163             :                         } else {
     164           0 :                                 return maxOf<size_t>();
     165             :                         }
     166             :                 } else {
     167           0 :                         return filesystem::platform::_seek(_platformFile, offset, s);
     168             :                 }
     169             :         }
     170           0 :         return maxOf<size_t>();
     171             : }
     172             : 
     173           0 : size_t File::tell() const {
     174           0 :         if (!_isBundled) {
     175           0 :                 auto p = ftell(_nativeFile);
     176           0 :                 if (p >= 0) {
     177           0 :                         return static_cast<size_t>(p);
     178             :                 } else {
     179           0 :                         return maxOf<size_t>();
     180             :                 }
     181             :         } else {
     182           0 :                 return filesystem::platform::_tell(_platformFile);
     183             :         }
     184             : }
     185             : 
     186       19490 : size_t File::size() const {
     187       19490 :         return _size;
     188             : }
     189             : 
     190           0 : typename File::int_type File::xsgetc() {
     191           0 :         int_type ret = traits_type::eof();
     192           0 :         if (is_open()) {
     193           0 :                 if (!_isBundled) {
     194           0 :                         ret = fgetc(_nativeFile);
     195             :                 } else {
     196           0 :                         uint8_t buf = 0;
     197           0 :                         if (read(&buf, 1) == 1) {
     198           0 :                                 ret = buf;
     199             :                         }
     200             :                 }
     201             :         }
     202           0 :         return ret;
     203             : }
     204             : 
     205           0 : typename File::int_type File::xsputc(int_type c) {
     206           0 :         int_type ret = traits_type::eof();
     207           0 :         if (is_open() && !_isBundled) {
     208           0 :                 ret = fputc(c, _nativeFile);
     209             :         }
     210           0 :         ++ _size;
     211           0 :         return ret;
     212             : }
     213             : 
     214        7850 : typename File::streamsize File::xsputn(const char* s, streamsize n) {
     215        7850 :         streamsize ret = -1;
     216        7850 :         if (is_open() && !_isBundled) {
     217        7850 :                 if (fwrite(s, n, 1, _nativeFile) == 1) {
     218        7850 :                         ret = n;
     219             :                 }
     220             :         }
     221        7850 :         _size += n;
     222        7850 :         return ret;
     223             : }
     224             : 
     225          50 : typename File::streamsize File::xsgetn(char* s, streamsize n) {
     226          50 :         streamsize ret = -1;
     227          50 :         if (is_open()) {
     228          50 :                 ret = read(reinterpret_cast<uint8_t *>(s), n);
     229             :         }
     230          50 :         return ret;
     231             : }
     232             : 
     233           0 : bool File::eof() const {
     234           0 :         if (is_open()) {
     235           0 :                 if (!_isBundled) {
     236           0 :                         return feof(_nativeFile) != 0;
     237             :                 } else {
     238           0 :                         return filesystem::platform::_eof(_platformFile);
     239             :                 }
     240             :         }
     241           0 :         return true;
     242             : }
     243             : 
     244       43224 : void File::close() {
     245       43224 :         if (is_open()) {
     246       22684 :                 if (!_isBundled) {
     247       22684 :                         fclose(_nativeFile);
     248       22684 :                         if (_flags != Flags::DelOnClose && _buf[0] != 0) {
     249         125 :                                 native::unlink_fn(_buf);
     250             :                         }
     251       22684 :                         memset(_buf, 0, 256);
     252       22684 :                         _nativeFile = nullptr;
     253             :                 } else {
     254           0 :                         filesystem::platform::_close(_platformFile);
     255           0 :                         _platformFile = nullptr;
     256             :                 }
     257             :         }
     258       43224 : }
     259             : 
     260         475 : void File::close_remove() {
     261         475 :         if (is_open()) {
     262          75 :                 if (!_isBundled) {
     263          75 :                         fclose(_nativeFile);
     264          75 :                         if (_buf[0] != 0) {
     265          75 :                                 native::unlink_fn(_buf);
     266             :                         }
     267          75 :                         memset(_buf, 0, 256);
     268          75 :                         _nativeFile = nullptr;
     269             :                 } else {
     270           0 :                         filesystem::platform::_close(_platformFile);
     271           0 :                         _platformFile = nullptr;
     272             :                 }
     273             :         }
     274         475 : }
     275             : 
     276         100 : bool File::close_rename(StringView path) {
     277         100 :         if (is_open()) {
     278         100 :                 if (!_isBundled && _buf[0] != 0) {
     279         100 :                         fclose(_nativeFile);
     280         100 :                         _nativeFile = nullptr;
     281         100 :                         if (move(_buf, path)) {
     282         100 :                                 memset(_buf, 0, 256);
     283         100 :                                 return true;
     284             :                         } else {
     285           0 :                                 _nativeFile = native::fopen_fn(_buf, "wb+");
     286             :                         }
     287             :                 }
     288             :         }
     289           0 :         return false;
     290             : }
     291             : 
     292      848443 : bool File::is_open() const {
     293      848443 :         return _nativeFile != nullptr || _platformFile != nullptr;
     294             : }
     295             : 
     296         325 : const char *File::path() const {
     297         325 :         if (_buf[0] == 0) {
     298           0 :                 return nullptr;
     299             :         } else {
     300         325 :                 return _buf;
     301             :         }
     302             : }
     303             : 
     304         300 : void File::set_tmp_path(const char *buf) {
     305         300 :         memcpy(_buf, buf, 256);
     306         300 : }
     307             : 
     308        1463 : bool exists(StringView ipath) {
     309        1463 :         if (filepath::isAbsolute(ipath)) {
     310        1369 :                 return filesystem::native::access_fn(ipath, Access::Exists);
     311          94 :         } else if (filepath::isBundled(ipath)) {
     312           0 :                 return filesystem::platform::_exists(ipath);
     313          94 :         } else if (!filepath::isAboveRoot(ipath) && filesystem::platform::_exists(ipath)) {
     314           0 :                 return true;
     315             :         } else {
     316          94 :                 auto path = filepath::absolute<memory::StandartInterface>(ipath);
     317          94 :                 return filesystem::native::access_fn(path, Access::Exists);
     318          94 :         }
     319             : }
     320             : 
     321         950 : bool stat(StringView ipath, Stat &stat) {
     322         950 :         if (filepath::isAbsolute(ipath)) {
     323         950 :                 return filesystem::native::stat_fn(ipath, stat);
     324           0 :         } else if (filepath::isBundled(ipath)) {
     325           0 :                 return filesystem::platform::_stat(ipath, stat);
     326           0 :         } else if (!filepath::isAboveRoot(ipath) && filesystem::platform::_stat(ipath, stat)) {
     327           0 :                 return true;
     328             :         } else {
     329           0 :                 auto path = filepath::absolute<memory::StandartInterface>(ipath);
     330           0 :                 return filesystem::native::stat_fn(path, stat);
     331           0 :         }
     332             : }
     333             : 
     334        2320 : bool remove(StringView ipath, bool recursive, bool withDirs) {
     335        2320 :         if (filepath::inAppBundle(ipath)) {
     336           0 :                 return false;
     337             :         }
     338             : 
     339        2320 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     340        2320 :         if (!recursive) {
     341        2245 :                 return filesystem::native::remove_fn(path);
     342             :         } else {
     343         150 :                 return ftw_b(path, [withDirs] (const StringView &fpath, bool isFile) -> bool {
     344         570 :                         if (isFile || withDirs) {
     345         570 :                                 return remove(fpath);
     346             :                         }
     347           0 :                         return true;
     348          75 :                 });
     349             :         }
     350        2320 : }
     351             : 
     352          50 : bool touch(StringView ipath) {
     353          50 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     354          50 :         if (filepath::isBundled(path)) {
     355           0 :                 return false;
     356             :         }
     357          50 :         return filesystem::native::touch_fn(path);
     358          50 : }
     359             : 
     360         475 : bool mkdir(StringView ipath) {
     361         475 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     362         950 :         return filesystem::native::mkdir_fn(path);
     363         475 : }
     364             : 
     365          25 : bool mkdir_recursive(StringView ipath, bool appWide) {
     366          25 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     367             : 
     368          25 :         memory::StandartInterface::StringType appWideLimit;
     369          25 :         if (appWide) {
     370             :                 do {
     371           0 :                         auto testPath = cachesPath<memory::StandartInterface>();
     372           0 :                         if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
     373           0 :                                 appWideLimit = std::move(testPath);
     374           0 :                                 break;
     375             :                         }
     376             : 
     377           0 :                         testPath = writablePath<memory::StandartInterface>();
     378           0 :                         if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
     379           0 :                                 appWideLimit = std::move(testPath);
     380           0 :                                 break;
     381             :                         }
     382             : 
     383           0 :                         testPath = documentsPath<memory::StandartInterface>();
     384           0 :                         if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
     385           0 :                                 appWideLimit = std::move(testPath);
     386           0 :                                 break;
     387             :                         }
     388             : 
     389           0 :                         testPath = currentDir<memory::StandartInterface>();
     390           0 :                         if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
     391           0 :                                 appWideLimit = std::move(testPath);
     392           0 :                                 break;
     393             :                         }
     394           0 :                 } while (0);
     395             : 
     396           0 :                 if (appWideLimit.empty()) {
     397           0 :                         return false;
     398             :                 }
     399             :         }
     400             : 
     401          25 :         auto components = filepath::split<memory::StandartInterface>(path);
     402          25 :         if (!components.empty()) {
     403          25 :                 bool control = false;
     404          25 :                 memory::StandartInterface::StringType construct("/");
     405         325 :                 for (auto &it : components) {
     406         300 :                         construct.append(it.data(), it.size());
     407         300 :                         if (!appWide || construct.compare(0, std::min(construct.size(), appWideLimit.size()), appWideLimit) == 0) {
     408         300 :                                 Stat stat;
     409         300 :                                 if (control || !filesystem::native::stat_fn(path, stat)) {
     410           0 :                                         control = true;
     411           0 :                                         filesystem::native::mkdir_fn(construct);
     412         300 :                                 } else if (!stat.isDir) {
     413           0 :                                         return false;
     414             :                                 }
     415             :                         }
     416         300 :                         construct.append("/");
     417             :                 }
     418          25 :         }
     419          25 :         return true;
     420          25 : }
     421             : 
     422         275 : void ftw(StringView ipath, const Callback<void(StringView path, bool isFile)> &callback, int depth, bool dir_first) {
     423         275 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     424         275 :         if (filepath::isBundled(path)) {
     425           0 :                 filesystem::platform::_ftw(path, callback, depth, dir_first);
     426             :         } else {
     427         275 :                 filesystem::native::ftw_fn(path, callback, depth, dir_first);
     428             :         }
     429         275 : }
     430             : 
     431          75 : bool ftw_b(StringView ipath, const Callback<bool(StringView path, bool isFile)> &callback, int depth, bool dir_first) {
     432          75 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     433          75 :         if (filepath::isBundled(path)) {
     434           0 :                 return filesystem::platform::_ftw_b(path, callback, depth, dir_first);
     435             :         } else {
     436          75 :                 return filesystem::native::ftw_b_fn(path, callback, depth, dir_first);
     437             :         }
     438          75 : }
     439             : 
     440         225 : bool move(StringView isource, StringView idest) {
     441         225 :         auto source = filepath::absolute<memory::StandartInterface>(isource, true);
     442         225 :         auto dest = filepath::absolute<memory::StandartInterface>(idest, true);
     443             : 
     444         225 :         if (!filesystem::native::rename_fn(source, dest)) {
     445         225 :                 if (copy(source, dest)) {
     446         225 :                         return remove(source);
     447             :                 }
     448           0 :                 return false;
     449             :         }
     450           0 :         return true;
     451         225 : }
     452             : 
     453         225 : static bool performCopy(StringView source, StringView dest) {
     454         225 :         if (stappler::filesystem::exists(dest)) {
     455           0 :                 stappler::filesystem::remove(dest);
     456             :         }
     457         225 :         if (!stappler::filesystem::exists(dest)) {
     458         225 :                 std::ofstream destStream(dest.data(), std::ios::binary);
     459         225 :                 auto f = openForReading(source);
     460         225 :                 if (f && destStream.is_open()) {
     461         225 :                         if (io::read(f, io::Consumer(destStream)) > 0) {
     462         225 :                                 return true;
     463             :                         }
     464             :                 }
     465           0 :                 destStream.close();
     466         450 :         }
     467           0 :         return false;
     468             : }
     469             : 
     470         450 : static bool isdir(StringView path) {
     471         450 :         Stat stat;
     472         450 :         native::stat_fn(path, stat);
     473         450 :         return stat.isDir;
     474             : }
     475             : 
     476         225 : bool copy(StringView isource, StringView idest, bool stopOnError) {
     477         225 :         auto source = filepath::absolute<memory::StandartInterface>(isource, true);
     478         225 :         auto dest = filepath::absolute<memory::StandartInterface>(idest, true);
     479         225 :         if (dest.back() == '/') {
     480           0 :                 dest = filepath::merge<memory::StandartInterface>(dest, filepath::lastComponent(source));
     481         225 :         } else if (isdir(dest) && filepath::lastComponent(source) != filepath::lastComponent(dest)) {
     482           0 :                 dest = filepath::merge<memory::StandartInterface>(dest, filepath::lastComponent(source));
     483             :         }
     484         225 :         if (!isdir(source)) {
     485         225 :                 return performCopy(source, dest);
     486             :         } else {
     487           0 :                 return ftw_b(source, [source, dest, stopOnError] (StringView path, bool isFile) -> bool {
     488           0 :                         if (!isFile) {
     489           0 :                                 if (path == source) {
     490           0 :                                         mkdir(filepath::replace<memory::StandartInterface>(path, source, dest));
     491           0 :                                         return true;
     492             :                                 }
     493           0 :                                 bool ret = mkdir(filepath::replace<memory::StandartInterface>(path, source, dest));
     494           0 :                                 if (stopOnError) {
     495           0 :                                         return ret;
     496             :                                 } else {
     497           0 :                                         return true;
     498             :                                 }
     499             :                         } else {
     500           0 :                                 bool ret = performCopy(path, filepath::replace<memory::StandartInterface>(path, source, dest));
     501           0 :                                 if (stopOnError) {
     502           0 :                                         return ret;
     503             :                                 } else {
     504           0 :                                         return true;
     505             :                                 }
     506             :                         }
     507           0 :                 }, -1, true);
     508             :         }
     509         225 : }
     510             : 
     511         275 : bool write(StringView ipath, const unsigned char *data, size_t len) {
     512         275 :         auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
     513         550 :         return filesystem::native::write_fn(path, data, len);
     514         275 : }
     515             : 
     516       22684 : File openForReading(StringView ipath) {
     517       22684 :         if (filepath::inAppBundle(ipath)) {
     518           0 :                 return filesystem::platform::_openForReading(ipath);
     519             :         }
     520       22684 :         auto path = filepath::absolute<memory::StandartInterface>(ipath);
     521       22684 :         auto f = filesystem::native::fopen_fn(path, "rb");
     522       22684 :         if (f) {
     523       22559 :                 return File(f);
     524             :         }
     525         125 :         return File();
     526       22684 : }
     527             : 
     528         250 : bool readIntoBuffer(uint8_t *buf, StringView ipath, size_t off, size_t size) {
     529         250 :         auto f = openForReading(ipath);
     530         250 :         if (f) {
     531         250 :                 size_t fsize = f.size();
     532         250 :                 if (fsize <= off) {
     533           0 :                         f.close();
     534           0 :                         return false;
     535             :                 }
     536         250 :                 if (fsize - off < size) {
     537         250 :                         size = fsize - off;
     538             :                 }
     539             : 
     540         250 :                 bool ret = true;
     541         250 :                 f.seek(off, io::Seek::Set);
     542         250 :                 if (f.read(buf, size) != size) {
     543           0 :                         ret = false;
     544             :                 }
     545         250 :                 f.close();
     546         250 :                 return ret;
     547             :         }
     548           0 :         return false;
     549         250 : }
     550             : 
     551           0 : bool readWithConsumer(const io::Consumer &stream, uint8_t *buf, size_t bsize, StringView ipath, size_t off, size_t size) {
     552           0 :         auto f = openForReading(ipath);
     553           0 :         if (f) {
     554           0 :                 size_t fsize = f.size();
     555           0 :                 if (fsize <= off) {
     556           0 :                         f.close();
     557           0 :                         return false;
     558             :                 }
     559           0 :                 if (fsize - off < size) {
     560           0 :                         size = fsize - off;
     561             :                 }
     562             : 
     563           0 :                 bool ret = true;
     564           0 :                 f.seek(off, io::Seek::Set);
     565           0 :                 while (size > 0) {
     566           0 :                         auto read = min(size, bsize);
     567           0 :                         if (f.read(buf, read) == read) {
     568           0 :                                 stream.write(buf, read);
     569             :                         } else {
     570           0 :                                 ret = false;
     571           0 :                                 break;
     572             :                         }
     573           0 :                         size -= read;
     574             :                 }
     575           0 :                 f.close();
     576           0 :                 return ret;
     577             :         }
     578           0 :         return false;
     579           0 : }
     580             : 
     581             : }

Generated by: LCOV version 1.14