diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
| commit | d3261e64152397db2dca4d691a990c6bc2a6f4dd (patch) | |
| tree | fac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/launcher/FileSystem.h | |
| parent | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff) | |
| download | Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip | |
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/launcher/FileSystem.h')
| -rw-r--r-- | archived/projt-launcher/launcher/FileSystem.h | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/FileSystem.h b/archived/projt-launcher/launcher/FileSystem.h new file mode 100644 index 0000000000..208c459945 --- /dev/null +++ b/archived/projt-launcher/launcher/FileSystem.h @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/* === Upstream License Block (Do Not Modify) ============================== + * + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ======================================================================== */ + +#pragma once + +#include "Exception.h" +#include "Filter.h" + +#include <system_error> + +#include <QDir> +#include <QFlags> +#include <QLocalServer> +#include <QObject> +#include <QPair> +#include <QThread> + +namespace FS +{ + + class FileSystemException : public ::Exception + { + public: + FileSystemException(const QString& message) : Exception(message) + {} + }; + + /** + * write data to a file safely + */ + void write(const QString& filename, const QByteArray& data); + + /** + * append data to a file safely + */ + void appendSafe(const QString& filename, const QByteArray& data); + + /** + * append data to a file + */ + void append(const QString& filename, const QByteArray& data); + + /** + * read data from a file safely + */ + QByteArray read(const QString& filename); + + /** + * Update the last changed timestamp of an existing file + */ + bool updateTimestamp(const QString& filename); + + /** + * Creates all the folders in a path for the specified path + * last segment of the path is treated as a file name and is ignored! + */ + bool ensureFilePathExists(QString filenamepath); + + /** + * Creates all the folders in a path for the specified path + * last segment of the path is treated as a folder name and is created! + */ + bool ensureFolderPathExists(const QFileInfo folderPath); + + /** + * Creates all the folders in a path for the specified path + * last segment of the path is treated as a folder name and is created! + */ + bool ensureFolderPathExists(const QString folderPathName); + + /** + * @brief Copies a directory and it's contents from src to dest + */ + class copy : public QObject + { + Q_OBJECT + public: + copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent) + { + m_src.setPath(src); + m_dst.setPath(dst); + } + copy& followSymlinks(const bool follow) + { + m_followSymlinks = follow; + return *this; + } + copy& matcher(Filter filter) + { + m_matcher = std::move(filter); + return *this; + } + copy& whitelist(bool whitelist) + { + m_whitelist = whitelist; + return *this; + } + copy& overwrite(const bool overwrite) + { + m_overwrite = overwrite; + return *this; + } + + bool operator()(bool dryRun = false) + { + return operator()(QString(), dryRun); + } + + qsizetype totalCopied() + { + return m_copied; + } + qsizetype totalFailed() + { + return m_failedPaths.length(); + } + QStringList failed() + { + return m_failedPaths; + } + + signals: + void fileCopied(const QString& relativeName); + void copyFailed(const QString& relativeName); + // NOTE: A 'shouldCopy' signal could be added here in the future for granular control. + + private: + bool operator()(const QString& offset, bool dryRun = false); + + private: + bool m_followSymlinks = true; + Filter m_matcher = nullptr; + bool m_whitelist = false; + bool m_overwrite = false; + QDir m_src; + QDir m_dst; + qsizetype m_copied; + QStringList m_failedPaths; + }; + + struct LinkPair + { + QString src; + QString dst; + }; + + struct LinkResult + { + QString src; + QString dst; + QString err_msg; + int err_value; + }; + + class ExternalLinkFileProcess : public QThread + { + Q_OBJECT + public: + ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr) + : QThread(parent), + m_useHardLinks(useHardLinks), + m_server(server) + {} + + void run() override + { + runLinkFile(); + emit processExited(); + } + + signals: + void processExited(); + + private: + void runLinkFile(); + + bool m_useHardLinks = false; + + QString m_server; + }; + + /** + * @brief links (a file / a directory and it's contents) from src to dest + */ + class create_link : public QObject + { + Q_OBJECT + public: + create_link(const QList<LinkPair> path_pairs, QObject* parent = nullptr) : QObject(parent) + { + m_path_pairs.append(path_pairs); + } + create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent) + { + LinkPair pair = { src, dst }; + m_path_pairs.append(pair); + } + create_link& useHardLinks(const bool useHard) + { + m_useHardLinks = useHard; + return *this; + } + create_link& matcher(Filter filter) + { + m_matcher = std::move(filter); + return *this; + } + create_link& whitelist(bool whitelist) + { + m_whitelist = whitelist; + return *this; + } + create_link& linkRecursively(bool recursive) + { + m_recursive = recursive; + return *this; + } + create_link& setMaxDepth(int depth) + { + m_max_depth = depth; + return *this; + } + create_link& debug(bool d) + { + m_debug = d; + return *this; + } + + std::error_code getOSError() + { + return m_os_err; + } + + bool operator()(bool dryRun = false) + { + return operator()(QString(), dryRun); + } + + int totalLinked() + { + return m_linked; + } + int totalToLink() + { + return static_cast<int>(m_links_to_make.size()); + } + + void runPrivileged() + { + runPrivileged(QString()); + } + void runPrivileged(const QString& offset); + + QList<LinkResult> getResults() + { + return m_path_results; + } + + signals: + void fileLinked(const QString& srcName, const QString& dstName); + void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value); + void finished(); + void finishedPrivileged(bool gotResults); + + private: + bool operator()(const QString& offset, bool dryRun = false); + void make_link_list(const QString& offset); + bool make_links(); + + private: + bool m_useHardLinks = false; + Filter m_matcher = nullptr; + bool m_whitelist = false; + bool m_recursive = true; + + /// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc. + int m_max_depth = -1; + + QList<LinkPair> m_path_pairs; + QList<LinkResult> m_path_results; + QList<LinkPair> m_links_to_make; + + int m_linked; + bool m_debug = false; + std::error_code m_os_err; + + QLocalServer m_linkServer; + }; + + /** + * @brief moves a file by renaming it + * @param source source file path + * @param dest destination filepath + * + */ + bool move(const QString& source, const QString& dest); + + /** + * Delete a folder recursively + */ + bool deletePath(QString path); + + /** + * Trash a folder / file + */ + bool trash(QString path, QString* pathInTrash = nullptr); + + QString PathCombine(const QString& path1, const QString& path2); + QString PathCombine(const QString& path1, const QString& path2, const QString& path3); + QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4); + + QString AbsolutePath(const QString& path); + + /** + * @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc. + * + * @param path path to measure + * @return int number of components before base path + */ + int pathDepth(const QString& path); + + /** + * @brief cut off segments of path until it is a max of length depth + * + * @param path path to truncate + * @param depth max depth of new path + * @return QString truncated path + */ + QString pathTruncate(const QString& path, int depth); + + /** + * Resolve an executable + * + * Will resolve: + * single executable (by name) + * relative path + * absolute path + * + * @return absolute path to executable or null string + */ + QString ResolveExecutable(QString path); + + /** + * Normalize path + * + * Any paths inside the current directory will be normalized to relative paths (to current) + * Other paths will be made absolute + * + * Returns false if the path logic somehow filed (and normalizedPath in invalid) + */ + QString NormalizePath(QString path); + + QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-'); + + QString RemoveInvalidPathChars(QString string, QChar replaceWith = '-'); + + QString DirNameFromString(QString string, QString inDir = "."); + + /// Checks if the a given Path contains "!" + bool checkProblemticPathJava(QDir folder); + + // Get the Directory representing the User's Desktop + QString getDesktopDir(); + + // Get the Directory representing the User's Applications directory + QString getApplicationsDir(); + + // Overrides one folder with the contents of another, preserving items exclusive to the first folder + // Equivalent to doing QDir::rename, but allowing for overrides + bool overrideFolder(QString overwritten_path, QString override_path); + + /** + * Creates a shortcut to the specified target file at the specified destination path. + * Returns null QString if creation failed; otherwise returns the path to the created shortcut. + */ + QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon); + + enum class FilesystemType + { + FAT, + NTFS, + REFS, + EXT, + EXT_2_OLD, + EXT_2_3_4, + XFS, + BTRFS, + NFS, + ZFS, + APFS, + HFS, + HFSPLUS, + HFSX, + FUSEBLK, + F2FS, + BCACHEFS, + UNKNOWN + }; + + /** + * @brief Ordered Mapping of enum types to reported filesystem names + * this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use + * . all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup. + * + * QMap is ordered + * + */ + static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { + { FilesystemType::FAT, { "FAT" } }, + { FilesystemType::NTFS, { "NTFS" } }, + { FilesystemType::REFS, { "REFS" } }, + { FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } }, + { FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } }, + { FilesystemType::EXT, { "EXT" } }, + { FilesystemType::XFS, { "XFS" } }, + { FilesystemType::BTRFS, { "BTRFS" } }, + { FilesystemType::NFS, { "NFS" } }, + { FilesystemType::ZFS, { "ZFS" } }, + { FilesystemType::APFS, { "APFS" } }, + { FilesystemType::HFS, { "HFS" } }, + { FilesystemType::HFSPLUS, { "HFSPLUS" } }, + { FilesystemType::HFSX, { "HFSX" } }, + { FilesystemType::FUSEBLK, { "FUSEBLK" } }, + { FilesystemType::F2FS, { "F2FS" } }, + { FilesystemType::BCACHEFS, { "BCACHEFS" } }, + { FilesystemType::UNKNOWN, { "UNKNOWN" } } + }; + + /** + * @brief Get the string name of Filesystem enum object + * + * @param type + * @return QString + */ + QString getFilesystemTypeName(FilesystemType type); + + /** + * @brief Get the Filesystem enum object from a name + * Does a lookup of the type name and returns an exact match + * + * @param name + * @return FilesystemType + */ + FilesystemType getFilesystemType(const QString& name); + + /** + * @brief Get the Filesystem enum object from a name + * Does a fuzzy lookup of the type name and returns an apropreate match + * + * @param name + * @return FilesystemType + */ + FilesystemType getFilesystemTypeFuzzy(const QString& name); + + struct FilesystemInfo + { + FilesystemType fsType = FilesystemType::UNKNOWN; + QString fsTypeName; + int blockSize; + qint64 bytesAvailable; + qint64 bytesFree; + qint64 bytesTotal; + QString name; + QString rootPath; + }; + + /** + * @brief path to the near ancestor that exists + * + */ + QString nearestExistentAncestor(const QString& path); + + /** + * @brief colect information about the filesystem under a file + * + */ + FilesystemInfo statFS(const QString& path); + + static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, + FilesystemType::ZFS, FilesystemType::XFS, + FilesystemType::REFS, FilesystemType::BCACHEFS }; + + /** + * @brief if the Filesystem is reflink/clone capable + * + */ + bool canCloneOnFS(const QString& path); + bool canCloneOnFS(const FilesystemInfo& info); + bool canCloneOnFS(FilesystemType type); + + /** + * @brief if the Filesystems are reflink/clone capable and both are on the same device + * + */ + bool canClone(const QString& src, const QString& dst); + + /** + * @brief Copies a directory and it's contents from src to dest + */ + class clone : public QObject + { + Q_OBJECT + public: + clone(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent) + { + m_src.setPath(src); + m_dst.setPath(dst); + } + clone& matcher(Filter filter) + { + m_matcher = std::move(filter); + return *this; + } + clone& whitelist(bool whitelist) + { + m_whitelist = whitelist; + return *this; + } + + bool operator()(bool dryRun = false) + { + return operator()(QString(), dryRun); + } + + qsizetype totalCloned() + { + return m_cloned; + } + qsizetype totalFailed() + { + return m_failedClones.length(); + } + + QList<QPair<QString, QString>> failed() + { + return m_failedClones; + } + + signals: + void fileCloned(const QString& src, const QString& dst); + void cloneFailed(const QString& src, const QString& dst); + + private: + bool operator()(const QString& offset, bool dryRun = false); + + private: + Filter m_matcher = nullptr; + bool m_whitelist = false; + QDir m_src; + QDir m_dst; + qsizetype m_cloned; + QList<QPair<QString, QString>> m_failedClones; + }; + + /** + * @brief clone/reflink file from src to dst + * + */ + bool clone_file(const QString& src, const QString& dst, std::error_code& ec); + +#if defined(Q_OS_WIN) + bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, std::error_code& ec); +#elif defined(Q_OS_LINUX) + bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std::error_code& ec); +#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec); +#endif + + static const QList<FilesystemType> s_non_link_filesystems = { + FilesystemType::FAT, + }; + + /** + * @brief if the Filesystem is symlink capable + * + */ + bool canLinkOnFS(const QString& path); + bool canLinkOnFS(const FilesystemInfo& info); + bool canLinkOnFS(FilesystemType type); + + /** + * @brief if the Filesystem is symlink capable on both ends + * + */ + bool canLink(const QString& src, const QString& dst); + + uintmax_t hardLinkCount(const QString& path); + +#ifdef Q_OS_WIN + QString getPathNameInLocal8bit(const QString& file); +#endif + + QString getUniqueResourceName(const QString& filePath); + +} // namespace FS |
