LCOV - code coverage report
Current view: top level - muse/framework/global/io/internal - filesystem.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 52.0 % 350 182
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "filesystem.h"
      23              : 
      24              : #include <QDir>
      25              : #include <QDirIterator>
      26              : #include <QFileInfo>
      27              : #include <QStorageInfo>
      28              : #include <QDirListing>
      29              : 
      30              : #ifdef Q_OS_WIN
      31              : #include <windows.h>
      32              : #endif
      33              : 
      34              : #include "../ioretcodes.h"
      35              : #include "log.h"
      36              : 
      37              : using namespace muse;
      38              : using namespace muse::io;
      39              : 
      40           92 : FileSystem::~FileSystem()
      41              : {
      42           46 :     std::lock_guard<std::mutex> lock(m_openStreamsMutex);
      43           46 :     for (auto& [id, file] : m_openStreams) {
      44            0 :         file->close();
      45              :     }
      46           46 :     m_openStreams.clear();
      47           92 : }
      48              : 
      49        15589 : Ret FileSystem::exists(const io::path_t& path) const
      50              : {
      51        15589 :     QFileInfo fileInfo(path.toQString());
      52        15589 :     if (!fileInfo.exists()) {
      53         2594 :         return make_ret(Err::FSNotExist);
      54              :     }
      55              : 
      56        12995 :     return make_ret(Err::NoError);
      57        15589 : }
      58              : 
      59         1191 : Ret FileSystem::remove(const io::path_t& path_, bool onlyIfEmpty)
      60              : {
      61         1191 :     QString path = path_.toQString();
      62         1191 :     QFileInfo fileInfo(path);
      63         1191 :     if (fileInfo.exists()) {
      64           65 :         return fileInfo.isDir() ? removeDir(path, onlyIfEmpty) : removeFile(path);
      65              :     }
      66              : 
      67         1126 :     return make_ret(Err::NoError);
      68         1191 : }
      69              : 
      70            4 : Ret FileSystem::clear(const io::path_t& path)
      71              : {
      72            4 :     const QString qPath = path.toQString();
      73            4 :     if (!QFileInfo::exists(qPath)) {
      74            0 :         return true;
      75              :     }
      76              : 
      77              :     using ItFlag = QDirListing::IteratorFlag;
      78            8 :     for (const auto& dirEntry : QDirListing{ qPath, ItFlag::IncludeHidden }) {
      79            2 :         if (dirEntry.isDir() && !dirEntry.isSymLink()) {
      80            0 :             const Ret didRemove = removeDir(dirEntry.filePath());
      81            0 :             if (!didRemove) {
      82            0 :                 return didRemove;
      83              :             }
      84              : 
      85            0 :             continue;
      86            0 :         }
      87              : 
      88            2 :         const Ret didRemove = removeFile(dirEntry.filePath());
      89            2 :         if (!didRemove) {
      90            0 :             return didRemove;
      91              :         }
      92            6 :     }
      93              : 
      94            4 :     return make_ok();
      95            4 : }
      96              : 
      97            0 : Ret FileSystem::copy(const io::path_t& src, const io::path_t& dst, bool replace)
      98              : {
      99            0 :     QFileInfo srcFileInfo(src.toQString());
     100            0 :     if (!srcFileInfo.exists()) {
     101            0 :         return make_ret(Err::FSNotExist);
     102              :     }
     103              : 
     104            0 :     QFileInfo dstFileInfo(dst.toQString());
     105            0 :     if (dstFileInfo.exists()) {
     106            0 :         if (!replace) {
     107            0 :             return make_ret(Err::FSAlreadyExists);
     108              :         }
     109              : 
     110            0 :         Ret ret = remove(dst);
     111            0 :         if (!ret) {
     112            0 :             return ret;
     113              :         }
     114            0 :     }
     115              : 
     116            0 :     Ret ret = copyRecursively(src, dst);
     117            0 :     return ret;
     118            0 : }
     119              : 
     120            0 : Ret FileSystem::move(const io::path_t& src, const io::path_t& dst, bool replace)
     121              : {
     122            0 :     QFileInfo srcFileInfo(src.toQString());
     123            0 :     if (!srcFileInfo.exists()) {
     124            0 :         return make_ret(Err::FSNotExist);
     125              :     }
     126              : 
     127            0 :     QFileInfo dstFileInfo(dst.toQString());
     128            0 :     if (dstFileInfo.exists()) {
     129            0 :         if (!replace) {
     130            0 :             return make_ret(Err::FSAlreadyExists);
     131              :         }
     132              : 
     133            0 :         Ret ret = remove(dst);
     134            0 :         if (!ret) {
     135            0 :             return ret;
     136              :         }
     137            0 :     }
     138              : 
     139            0 :     if (srcFileInfo.isDir()) {
     140            0 :         if (!QDir().rename(src.toQString(), dst.toQString())) {
     141            0 :             return make_ret(Err::FSMoveErrors);
     142              :         }
     143              :     } else {
     144            0 :         if (!QFile::rename(src.toQString(), dst.toQString())) {
     145            0 :             return make_ret(Err::FSMoveErrors);
     146              :         }
     147              :     }
     148              : 
     149            0 :     return make_ret(Ret::Code::Ok);
     150            0 : }
     151              : 
     152         3094 : RetVal<ByteArray> FileSystem::readFile(const io::path_t& filePath) const
     153              : {
     154         3094 :     RetVal<ByteArray> result;
     155         3094 :     Ret ret = exists(filePath);
     156         3094 :     if (!ret) {
     157            1 :         result.ret = ret;
     158            1 :         return result;
     159              :     }
     160              : 
     161         3093 :     QFile file(filePath.toQString());
     162         3093 :     if (!file.open(QIODevice::ReadOnly)) {
     163            0 :         result.ret = make_ret(Err::FSReadError);
     164            0 :         return result;
     165              :     }
     166              : 
     167         3093 :     qint64 size = file.size();
     168         3093 :     result.val.resize(static_cast<size_t>(size));
     169              : 
     170         3093 :     file.read(reinterpret_cast<char*>(result.val.data()), size);
     171         3093 :     file.close();
     172              : 
     173         3093 :     result.ret = make_ret(Err::NoError);
     174         3093 :     return result;
     175         3094 : }
     176              : 
     177         4799 : Ret FileSystem::readFile(const io::path_t& filePath, ByteArray& data) const
     178              : {
     179         4799 :     Ret ret = muse::make_ok();
     180              : 
     181         4799 :     QFile file(filePath.toQString());
     182         4799 :     if (!file.open(QIODevice::ReadOnly)) {
     183            1 :         ret = make_ret(Err::FSReadError);
     184            1 :         ret.setText(file.errorString().toStdString());
     185            1 :         return ret;
     186              :     }
     187              : 
     188         4798 :     qint64 size = file.size();
     189         4798 :     data.resize(static_cast<size_t>(size));
     190              : 
     191         4798 :     if (file.read(reinterpret_cast<char*>(data.data()), size) == -1) {
     192            0 :         ret = make_ret(Err::FSReadError);
     193            0 :         ret.setText(file.errorString().toStdString());
     194              :     }
     195              : 
     196         4798 :     file.close();
     197              : 
     198         4798 :     return ret;
     199         4799 : }
     200              : 
     201         3508 : Ret FileSystem::writeFile(const io::path_t& filePath, const ByteArray& data)
     202              : {
     203         3508 :     Ret ret = muse::make_ok();
     204              : 
     205         3508 :     QFile file(filePath.toQString());
     206         3508 :     if (!file.open(QIODevice::WriteOnly)) {
     207            0 :         ret = make_ret(Err::FSWriteError);
     208            0 :         ret.setText(file.errorString().toStdString());
     209            0 :         return ret;
     210              :     }
     211              : 
     212         3508 :     const auto numBytesToWrite = static_cast<qint64>(data.size());
     213         3508 :     const qint64 numBytesWritten = file.write(data.constChar(), numBytesToWrite);
     214         3508 :     if (numBytesWritten == -1) {
     215            0 :         ret = make_ret(Err::FSWriteError);
     216            0 :         ret.setText(file.errorString().toStdString());
     217            0 :         return ret;
     218              :     }
     219              : 
     220         3508 :     if (numBytesWritten != numBytesToWrite) {
     221            0 :         ret = make_ret(Err::FSWriteError);
     222            0 :         ret.setText("Failed to write entire file");
     223            0 :         return ret;
     224              :     }
     225              : 
     226         3508 :     file.close();
     227              : 
     228         3508 :     return ret;
     229         3508 : }
     230              : 
     231           67 : Ret FileSystem::makePath(const io::path_t& path) const
     232              : {
     233          134 :     if (!QDir().mkpath(path.toQString())) {
     234            0 :         return make_ret(Err::FSMakingError);
     235              :     }
     236              : 
     237           67 :     return make_ret(Err::NoError);
     238              : }
     239              : 
     240            0 : Ret FileSystem::makeLink(const io::path_t& targetPath, const path_t& linkPath) const
     241              : {
     242              :     // On Windows, if targetPath points to a directory rather than a file, the
     243              :     // path must be normalized otherwise the shortcut file won't work. On other
     244              :     // platforms, normalization makes symlink resolution slightly faster.
     245            0 :     QString target = QDir::cleanPath(targetPath.toQString());
     246            0 :     QString link = linkPath.toQString();
     247              : 
     248              : #if defined(Q_OS_WIN)
     249              :     // Also required to make the shortcut file valid...
     250              :     target = QDir::toNativeSeparators(target);
     251              :     link += ".lnk";
     252              : #endif
     253              : 
     254            0 :     return QFile().link(target, link) ? make_ret(Err::NoError) : make_ret(Err::FSMakingError);
     255            0 : }
     256              : 
     257            5 : EntryType FileSystem::entryType(const io::path_t& path) const
     258              : {
     259            5 :     QFileInfo fi(path.toQString());
     260            5 :     if (fi.isFile()) {
     261            0 :         return EntryType::File;
     262            5 :     } else if (fi.isDir()) {
     263            5 :         return EntryType::Dir;
     264              :     }
     265              : 
     266            0 :     return EntryType::Undefined;
     267            5 : }
     268              : 
     269            3 : RetVal<uint64_t> FileSystem::fileSize(const io::path_t& path) const
     270              : {
     271            3 :     RetVal<uint64_t> rv;
     272            3 :     rv.ret = exists(path);
     273            3 :     if (!rv.ret) {
     274            1 :         return rv;
     275              :     }
     276              : 
     277            2 :     QFileInfo fi(path.toQString());
     278            2 :     rv.val = static_cast<uint64_t>(fi.size());
     279            2 :     return rv;
     280            2 : }
     281              : 
     282            0 : RetVal<uint64_t> FileSystem::availableSpace(const io::path_t& path) const
     283              : {
     284            0 :     QStorageInfo storage(path.toQString());
     285            0 :     if (!storage.isValid() || !storage.isReady()) {
     286            0 :         return RetVal<uint64_t>(make_ret(Err::FSNotExist));
     287              :     }
     288              : 
     289            0 :     return RetVal<uint64_t>::make_ok(static_cast<uint64_t>(storage.bytesAvailable()));
     290            0 : }
     291              : 
     292          158 : RetVal<io::paths_t> FileSystem::scanFiles(const io::path_t& rootDir, const std::vector<std::string>& nameFilters, ScanMode mode) const
     293              : {
     294          158 :     RetVal<io::paths_t> result;
     295          158 :     Ret ret = exists(rootDir);
     296          158 :     if (!ret) {
     297            0 :         result.ret = ret;
     298            0 :         return result;
     299              :     }
     300              : 
     301          158 :     QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
     302          158 :     QDir::Filters filters = QDir::NoDotAndDotDot | QDir::Readable;
     303              : 
     304          158 :     switch (mode) {
     305           48 :     case ScanMode::FilesInCurrentDir:
     306           48 :         filters |= QDir::Files;
     307           48 :         break;
     308            0 :     case ScanMode::FoldersInCurrentDir:
     309            0 :         filters |= QDir::Dirs;
     310            0 :         break;
     311            0 :     case ScanMode::FilesAndFoldersInCurrentDir:
     312            0 :         filters |= QDir::Files | QDir::Dirs;
     313            0 :         break;
     314          110 :     case ScanMode::FilesInCurrentDirAndSubdirs:
     315          110 :         flags |= QDirIterator::Subdirectories;
     316          110 :         filters |= QDir::Files;
     317          110 :         break;
     318              :     }
     319              : 
     320          158 :     QStringList qnameFilters;
     321          216 :     for (const std::string& f : nameFilters) {
     322           58 :         qnameFilters << QString::fromStdString(f);
     323              :     }
     324              : 
     325          158 :     QDirIterator it(rootDir.toQString(), qnameFilters, filters, flags);
     326              : 
     327          736 :     while (it.hasNext()) {
     328          578 :         result.val.push_back(it.next());
     329              :     }
     330              : 
     331          158 :     result.ret = make_ret(Err::NoError);
     332          158 :     return result;
     333          158 : }
     334              : 
     335           59 : Ret FileSystem::removeFile(const io::path_t& path)
     336              : {
     337           59 :     QFile file(path.toQString());
     338           59 :     if (!file.remove()) {
     339              :         // Read-only files prevent directory deletion on Windows, retry with Write permission.
     340            0 :         LOGW() << "Failed to delete file. Setting write permission and trying again...";
     341            0 :         if (!file.setPermissions(file.permissions() | QFile::WriteUser)) {
     342            0 :             LOGE() << "Failed to set write permission for file";
     343            0 :             return make_ret(Err::FSRemoveError);
     344              :         }
     345            0 :         if (!file.remove()) {
     346            0 :             LOGE() << "Second delete attempt failed";
     347            0 :             return make_ret(Err::FSRemoveError);
     348              :         }
     349              : 
     350            0 :         return make_ok();
     351              :     }
     352              : 
     353           59 :     return make_ok();
     354           59 : }
     355              : 
     356            8 : Ret FileSystem::removeDir(const io::path_t& path, const bool onlyIfEmpty)
     357              : {
     358            8 :     const QString qPath = path.toQString();
     359            8 :     QDir dir(qPath);
     360              : 
     361            8 :     if (!onlyIfEmpty) {
     362              :         // clear directory for deletion
     363            4 :         const Ret didClear = clear(path);
     364            4 :         if (!didClear) {
     365            0 :             LOGE() << "Failed to clear directory for deletion";
     366              : 
     367            0 :             return didClear;
     368              :         }
     369            4 :     }
     370              : 
     371            8 :     if (!dir.isEmpty()) {
     372            1 :         return make_ret(Err::FSDirNotEmptyError);
     373              :     }
     374              : 
     375            7 :     const QString dirName = dir.dirName();
     376            7 :     if (!dir.cdUp()) {
     377            0 :         return make_ret(Err::FSNotExist);
     378              :     }
     379              : 
     380            7 :     if (!dir.rmdir(dirName)) {
     381              :         // OneDrive removes delete permission on Windows. try again with write permission
     382            0 :         LOGW() << "Failed to delete directory. Setting write permission and trying again...";
     383            0 :         if (!QFile::setPermissions(qPath, QFile::permissions(qPath) | QFile::WriteUser)) {
     384            0 :             LOGE() << "Failed to set write permission for directory";
     385            0 :             return make_ret(Err::FSRemoveError);
     386              :         }
     387            0 :         if (!dir.rmdir(dirName)) {
     388            0 :             LOGE() << "Second delete attempt failed";
     389            0 :             return make_ret(Err::FSRemoveError);
     390              :         }
     391              : 
     392            0 :         return make_ok();
     393              :     }
     394              : 
     395            7 :     return make_ok();
     396            8 : }
     397              : 
     398            0 : Ret FileSystem::copyRecursively(const io::path_t& src, const io::path_t& dst) const
     399              : {
     400            0 :     QString srcPath = src.toQString();
     401            0 :     QString dstPath = dst.toQString();
     402              : 
     403            0 :     QFileInfo srcFileInfo(srcPath);
     404            0 :     if (srcFileInfo.isDir()) {
     405            0 :         QDir dstDir(dstPath);
     406            0 :         dstDir.cdUp();
     407            0 :         if (!dstDir.mkdir(QFileInfo(dstPath).fileName())) {
     408            0 :             return make_ret(Err::FSMakingError);
     409              :         }
     410            0 :         QDir srcDir(srcPath);
     411            0 :         const QStringList fileNames = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
     412            0 :         for (const QString& fileName : fileNames) {
     413            0 :             const QString newSrcPath = srcPath + QLatin1Char('/') + fileName;
     414            0 :             const QString newDstPath = dstPath + QLatin1Char('/') + fileName;
     415            0 :             Ret ret = copyRecursively(newSrcPath, newDstPath);
     416            0 :             if (!ret) {
     417            0 :                 return ret;
     418              :             }
     419            0 :         }
     420            0 :     } else {
     421            0 :         if (!QFile::copy(srcPath, dstPath)) {
     422            0 :             return make_ret(Err::FSCopyError);
     423              :         }
     424              :     }
     425              : 
     426            0 :     return make_ret(Err::NoError);
     427            0 : }
     428              : 
     429            0 : void FileSystem::setAttribute(const io::path_t& path, Attribute attribute) const
     430              : {
     431            0 :     switch (attribute) {
     432            0 :     case Attribute::Hidden: {
     433              : #ifdef Q_OS_WIN
     434              :         const QString nativePath = QDir::toNativeSeparators(path.toQString());
     435              :         SetFileAttributes((LPCTSTR)nativePath.unicode(), FILE_ATTRIBUTE_HIDDEN);
     436              : #endif
     437            0 :     } break;
     438              :     }
     439              :     UNUSED(path);
     440            0 : }
     441              : 
     442            0 : bool FileSystem::setPermissionsAllowedForAll(const io::path_t& path) const
     443              : {
     444            0 :     return QFile::setPermissions(path.toQString(),
     445            0 :                                  QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
     446            0 :                                  | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
     447            0 :                                  | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
     448            0 :                                  | QFile::ReadOther | QFile::WriteOther
     449            0 :                                  | QFile::ExeOther);
     450              : }
     451              : 
     452            0 : io::path_t FileSystem::temporaryDirectoryPath() const
     453              : {
     454            0 :     return QDir::tempPath();
     455              : }
     456              : 
     457           15 : io::path_t FileSystem::canonicalFilePath(const io::path_t& filePath) const
     458              : {
     459           15 :     return QFileInfo(filePath.toQString()).canonicalFilePath();
     460              : }
     461              : 
     462            0 : io::path_t FileSystem::absolutePath(const io::path_t& filePath) const
     463              : {
     464            0 :     return QFileInfo(filePath.toQString()).absolutePath();
     465              : }
     466              : 
     467         1042 : path_t FileSystem::absoluteFilePath(const path_t& filePath) const
     468              : {
     469         1042 :     return QFileInfo(filePath.toQString()).absoluteFilePath();
     470              : }
     471              : 
     472            0 : io::path_t FileSystem::cleanPath(const io::path_t& filePath) const
     473              : {
     474            0 :     return QDir::cleanPath(filePath.toQString());
     475              : }
     476              : 
     477            0 : bool FileSystem::isSymLink(const io::path_t& filePath) const
     478              : {
     479            0 :     return QFileInfo(filePath.toQString()).isSymLink();
     480              : }
     481              : 
     482            0 : io::path_t FileSystem::symLinkTarget(const io::path_t& filePath) const
     483              : {
     484            0 :     return QFileInfo(filePath.toQString()).symLinkTarget();
     485              : }
     486              : 
     487            0 : DateTime FileSystem::birthTime(const io::path_t& filePath) const
     488              : {
     489            0 :     return DateTime::fromQDateTime(QFileInfo(filePath.toQString()).birthTime());
     490              : }
     491              : 
     492           33 : DateTime FileSystem::lastModified(const io::path_t& filePath) const
     493              : {
     494           33 :     return DateTime::fromQDateTime(QFileInfo(filePath.toQString()).lastModified());
     495              : }
     496              : 
     497           26 : RetVal<StreamId> FileSystem::openStream(const io::path_t& filePath, OpenMode mode)
     498              : {
     499           26 :     RetVal<StreamId> result;
     500              : 
     501           26 :     auto file = std::make_unique<QFile>(filePath.toQString());
     502              : 
     503           26 :     QIODevice::OpenMode qmode = (mode == OpenMode::Append) ? QIODevice::Append : QIODevice::WriteOnly;
     504              : 
     505           26 :     if (!file->open(qmode)) {
     506            1 :         result.ret = make_ret(Err::FSWriteError);
     507            1 :         result.ret.setText(file->errorString().toStdString());
     508            1 :         return result;
     509              :     }
     510              : 
     511           25 :     std::lock_guard<std::mutex> lock(m_openStreamsMutex);
     512           25 :     StreamId id = m_nextStreamId++;
     513           25 :     m_openStreams.emplace(id, std::move(file));
     514              : 
     515           25 :     result.ret = make_ret(Err::NoError);
     516           25 :     result.val = id;
     517           25 :     return result;
     518           26 : }
     519              : 
     520           23 : Ret FileSystem::writeToStream(StreamId fileId, const ByteArray& data, uint64_t offset)
     521              : {
     522           23 :     std::lock_guard<std::mutex> lock(m_openStreamsMutex);
     523           23 :     auto it = m_openStreams.find(fileId);
     524           23 :     if (it == m_openStreams.end()) {
     525            0 :         return make_ret(Err::FSWriteError);
     526              :     }
     527              : 
     528           23 :     QFile* file = it->second.get();
     529           23 :     IF_ASSERT_FAILED(file) {
     530            0 :         return make_ret(Err::FSWriteError);
     531              :     }
     532              : 
     533           23 :     if (offset != STREAM_POS_CURRENT) {
     534            0 :         if (!file->seek(static_cast<qint64>(offset))) {
     535            0 :             Ret ret = make_ret(Err::FSWriteError);
     536            0 :             ret.setText(file->errorString().toStdString());
     537            0 :             return ret;
     538            0 :         }
     539              :     }
     540              : 
     541           23 :     const auto numBytesToWrite = static_cast<qint64>(data.size());
     542           23 :     const qint64 written = file->write(data.constChar(), numBytesToWrite);
     543           23 :     if (written != numBytesToWrite) {
     544            0 :         Ret ret = make_ret(Err::FSWriteError);
     545            0 :         ret.setText(file->errorString().toStdString());
     546            0 :         return ret;
     547            0 :     }
     548              : 
     549           23 :     return make_ret(Err::NoError);
     550           23 : }
     551              : 
     552           25 : Ret FileSystem::closeStream(StreamId fileId)
     553              : {
     554           25 :     std::lock_guard<std::mutex> lock(m_openStreamsMutex);
     555              : 
     556           25 :     auto it = m_openStreams.find(fileId);
     557           25 :     if (it == m_openStreams.end()) {
     558            0 :         return make_ret(Err::FSWriteError);
     559              :     }
     560              : 
     561           25 :     std::unique_ptr<QFile> file = std::move(it->second);
     562           25 :     m_openStreams.erase(it);
     563              : 
     564           25 :     IF_ASSERT_FAILED(file) {
     565            0 :         return make_ret(Err::FSWriteError);
     566              :     }
     567              : 
     568           25 :     file->close();
     569              : 
     570           25 :     if (file->error() != QFileDevice::NoError) {
     571            0 :         Ret ret = make_ret(Err::FSWriteError);
     572            0 :         ret.setText(file->errorString().toStdString());
     573            0 :         return ret;
     574            0 :     }
     575              : 
     576           25 :     return make_ret(Err::NoError);
     577           25 : }
     578              : 
     579            6 : Ret FileSystem::isWritable(const io::path_t& filePath) const
     580              : {
     581            6 :     Ret ret = muse::make_ok();
     582              : 
     583            6 :     QFileInfo fileInfo(filePath.toQString());
     584              : 
     585            6 :     if (!fileInfo.exists()) {
     586            5 :         QFile file(filePath.toQString());
     587              : 
     588            5 :         if (!file.open(QFile::WriteOnly)) {
     589            0 :             ret = make_ret(Err::FSWriteError);
     590            0 :             ret.setText(file.errorString().toStdString());
     591              :         }
     592              : 
     593            5 :         file.close();
     594            5 :         file.remove();
     595            6 :     } else if (!fileInfo.isWritable()) {
     596            0 :         QFile file(filePath.toQString());
     597            0 :         if (!file.open(QFile::WriteOnly)) {
     598            0 :             ret = make_ret(Err::FSWriteError);
     599            0 :             ret.setText(file.errorString().toStdString());
     600              :         }
     601              : 
     602            0 :         file.close();
     603            0 :     }
     604              : 
     605            6 :     return ret;
     606            6 : }
        

Generated by: LCOV version 2.0-1