/*
 * Copyright(C) 2014-2016, Krisztián Kende <nemh@freemail.hu>
 *
 * This file is part of the Systemback.
 *
 * The Systemback 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; either version 3 of the License, or (at your option) any later
 * version.
 *
 * The Systemback 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 the
 * Systemback. If not, see <http://www.gnu.org/licenses>.
 */

#include "sblib.hpp"
#include <QProcess>
#include <QTime>
#include <QDir>
#include <parted/parted.h>
#include <sys/resource.h>
#include <sys/sendfile.h>
#include <blkid/blkid.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/fs.h>
#include <dirent.h>
#include <utime.h>
#include <fcntl.h>

#ifdef C_MNT_LIB
#include "libmount.hpp"
#else
#include <libmount/libmount.h>
#endif

#ifdef bool
#undef bool
#endif

sb sb::SBThrd;
QTrn *sb::SBtr(nullptr);
QSL *sb::ThrdSlst;
QStr sb::ThrdStr[3], sb::eout, sb::sdir[3], sb::schdlr[2], sb::pnames[15], sb::lang, sb::style, sb::wsclng;
ullong sb::ThrdLng[]{0, 0};
int sb::sblock[4];
uchar sb::ThrdType, sb::ThrdChr, sb::dbglev, sb::pnumber(0), sb::ismpnt(sb::Empty), sb::schdle[]{sb::Empty, sb::Empty, sb::Empty, sb::Empty, sb::Empty, sb::Empty}, sb::waot(sb::Empty), sb::incrmtl(sb::Empty), sb::xzcmpr(sb::Empty), sb::autoiso(sb::Empty), sb::ecache(sb::Empty);
schar sb::Progress(-1);
bool sb::ThrdBool, sb::ExecKill(true), sb::ThrdKill(true), sb::ThrdRslt;

sb::sb()
{
    qputenv("PATH", "/usr/lib/systemback:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"),
    setlocale(LC_ALL, "C.UTF-8"), chdir("/"), umask(0);

    dbglev = qEnvironmentVariableIsEmpty("DBGLEV") ? Nulldbg : [] {
            bool ok;

            switch(qgetenv("DBGLEV").toUShort(&ok)) {
            case Errdbg:
                return Nulldbg;
            case Alldbg:
                return Alldbg;
            case Extdbg:
                return Extdbg;
            case Nodbg:
                if(ok) return Nodbg;
            default:
                return Falsedbg;
            }
        }();
}

sb::~sb()
{
    if(SBtr) delete SBtr;
}

void sb::ldtltr()
{
    QTrn *tltr(new QTrn);
    cfgread();

    if(lang == "auto")
    {
        if(QLocale::system().name() != "en_EN") tltr->load(QLocale::system(), "systemback", "_", "/usr/share/systemback/lang");
    }
    else if(lang != "en_EN")
        tltr->load("systemback_" % lang, "/usr/share/systemback/lang");

    if(tltr->isEmpty())
        delete tltr;
    else
        qApp->installTranslator(SBtr = tltr);

    switch(dbglev) {
    case Falsedbg:
        error("\n " % tr("The specified debug level is invalid!") % "\n\n " % tr("The default level (1) will be used.") % "\n\n"),
        dbglev = Nulldbg;
        break;
    case Extdbg:
        QTS(stderr) << (isatty(fileno(stderr)) ? "\033[1;31m" % dbginf() % "\033[0m" : right(dbginf().replace("\n ", "\n"), -1));
    }
}

void sb::print(cQStr &txt)
{
    QTS(stdout) << (isatty(fileno(stdout)) ? "\033[1m" % txt % "\033[0m" : QStr(txt).replace("\n ", "\n"));
}

bool sb::error(QStr txt, bool dbg)
{
    auto splt([](cQStr &stxt) {
            if(stxt.length() > 80)
            {
                QStr ftxt;
                QSL llst(stxt.split('\n'));

                for(uchar a(1) ; a < llst.count() ; ++a)
                {
                    cQStr &line(llst.at(a));
                    ftxt.append('\n' % (line.length() > 79 && ! like(line, {"*: /*", "*： /*"}) ? llst.value(a).replace(line.left(79).lastIndexOf(' '), 0, '\n') : line));
                }

                return ftxt;
            }
            else
                return stxt;
        });

    auto pfix([&txt]{ if(txt.contains("\n\n  ./")) txt.replace("\n\n  ./", "\n\n  /"); });
    if(! dbg) goto print;

    switch(dbglev) {
    case Errdbg:
    case Cextdbg:
        pfix(), eout.append(isatty(fileno(stderr)) ? splt(txt) : splt(txt).replace("\n ", "\n"));
        break;
    case Alldbg:
    case Extdbg:
        pfix();
print:
        QTS(stderr) << (isatty(fileno(stderr)) ? "\033[1;31m" % splt(txt) % "\033[0m" : splt(txt).replace("\n ", "\n"));
    }

    return false;
}

QStr sb::appver()
{
    QFile file(":version");
    fopen(file);
    QStr vrsn(qVersion());

    return file.readLine().trimmed() % "_Qt" % (vrsn == QT_VERSION_STR ? vrsn : vrsn % '(' % QT_VERSION_STR % ')') % '_' %
#ifdef __clang__
        "Clang" % QStr::number(__clang_major__) % '.' % QStr::number(__clang_minor__) % '.' % QStr::number(__clang_patchlevel__)
#elif defined(__INTEL_COMPILER) || ! defined(__GNUC__)
        "compiler?"
#elif defined(__GNUC__)
        "GCC" % QStr::number(__GNUC__) % '.' % QStr::number(__GNUC_MINOR__) % '.' % QStr::number(__GNUC_PATCHLEVEL__)
#endif
        % '_' %
#ifdef __amd64__
        "amd64";
#elif defined(__i386__)
        "i386";
#else
        "arch?";
#endif
}

QStr sb::dbginf()
{
    QStr txt("\n Systemback\n\n " % tr("Version:") % ' ' % appver() % "\n " % tr("Compilation date and time:") % ' ' % __DATE__ % ' ' % __TIME__ % "\n " % tr("Installed files:") % [] {
            QStr fls;
            QSL lst{"/etc/xdg/autostart/sbschedule-kde.desktop", "/etc/xdg/autostart/sbschedule.desktop", "/usr/bin/systemback", "/usr/bin/systemback-cli", "/usr/bin/systemback-sustart", "/usr/lib/systemback/libsystemback.so", "/usr/lib/systemback/libsystemback.so.1", "/usr/lib/systemback/libsystemback.so.1.0", "/usr/lib/systemback/libsystemback.so.1.0.0", "/usr/lib/systemback/sbscheduler", "/usr/lib/systemback/sbsustart", "/usr/lib/systemback/sbsysupgrade", "/usr/share/applications/systemback-kde.desktop", "/usr/share/applications/systemback.desktop", "/usr/share/systemback/efi-amd64.bootfiles"};
            uchar ind(tr("Installed files:").length() + 2);

            if(isdir("/usr/share/systemback/lang"))
                for(cQStr &file : QDir("/usr/share/systemback/lang").entryList(QDir::Files))
                    if(file.endsWith(".qm")) lst.append("/usr/share/systemback/lang/" % file);

            for(cQStr &file : lst)
                if(isfile(file)) fls.append((fls.isEmpty() ? " " : QStr('\n' % QStr(ind, ' '))) % file);

            return fls;
        }() % "\n " % tr("Operating system:") % []() -> QStr {
                QFile file("/etc/os-release");

                if(fopen(file))
                    while(! file.atEnd())
                    {
                        QStr cline(file.readLine().trimmed());
                        if(cline.startsWith("PRETTY_NAME=\"")) return ' ' % mid(cline, 14, cline.length() - 14);
                    }

                return nullptr;
            }() % "\n " % tr("Mounted filesystems:") % [] {
                    uchar ind(tr("Mounted filesystems:").length() + 2);
                    QStr mpts, spcs('\n' % QStr(ind++, ' '));
                    QBA mnts(fload("/proc/self/mounts"));
                    QTS in(&mnts, QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        QStr cline(in.readLine());

                        if(cline.startsWith("/dev/")) mpts.append((mpts.isEmpty() ? " " : spcs) % [&, ind] {
                                if((cline.contains("\\040") ? cline.replace("\\040", " ") : cline).length() > 81 - ind && isatty(fileno(stderr)))
                                {
                                    ushort clen(cline.length());
                                    uchar mlen(80 - ind);
                                    for(struct{ushort a; uchar b;} a{mlen, 0} ; a.a < clen ; a.a += mlen, ++a.b) cline.replace(a.a + a.b * ind, 0, spcs);
                                }

                                return cline;
                            }());
                    }

                    return mpts;
                }() % "\n " % tr("System language:") % ' ' % QLocale::system().name() % "\n " % tr("Translation:") % ' ' % (SBtr ? lang : QStr('-')) % "\n\n");

    return txt;
}

QStr sb::fdbg(cQStr &path1, cQStr &path2)
{
    switch(dbglev) {
    default:
        return "\n\n";
    case Extdbg:
    case Cextdbg:
        QStr path, txt;
        int cerrno(errno);

        for(uchar a(0) ; a < 2 && ! (a && path2.isEmpty()) ; ++a)
        {
            txt.append('\n'), path = a ? path2 : path1;
            bool cut(false);

            do {
                txt.append("\n " % (cut ? path = path.left(rinstr(path, "/") - 1) : path) % "\n  ");
                struct stat istat;

                if(lstat(bstr(path), &istat))
                    txt.append('-');
                else
                {
                    auto perm([&istat] {
                            QStr prm, rwx("rwxrwxrwx");
                            ushort ugo[]{S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH};
                            for(uchar b(0) ; b < 9 ; ++b) prm.append((istat.st_mode & ugo[b]) ? rwx.at(b) : '-');
                            if(istat.st_mode & S_ISUID) prm.replace(2, 1, (prm.at(2) == '-' ? 'S' : 's'));
                            if(istat.st_mode & S_ISGID) prm.replace(5, 1, (prm.at(5) == '-' ? 'S' : 's'));
                            if(istat.st_mode & S_ISVTX) prm.replace(8, 1, (prm.at(8) == '-' ? 'T' : 't'));
                            return prm;
                        });

                    switch(istat.st_mode & S_IFMT) {
                    case S_IFREG:
                        txt.append("f " % perm() % ' ' % QStr::number(istat.st_nlink) % ' ' % QStr::number(istat.st_uid) % ' ' % QStr::number(istat.st_gid) % ' ' % QStr::number(istat.st_dev) % ' ' % hunit(istat.st_size).replace(' ', nullptr));
                        break;
                    case S_IFDIR:
                        txt.append("d " % perm() % " - " % QStr::number(istat.st_uid) % ' ' % QStr::number(istat.st_gid) % ' ' % QStr::number(istat.st_dev) % " -");
                        break;
                    case S_IFLNK:
                    {
                        QStr lpath(rlink(path, istat.st_size));

                        txt.append("l " % perm() % ' ' % QStr::number(istat.st_nlink) % ' ' % QStr::number(istat.st_uid) % ' ' % QStr::number(istat.st_gid) % ' ' % QStr::number(istat.st_dev) % " -\n  " % lpath % "\n   " % [&lpath]() -> QStr {
                                switch(stype(lpath)) {
                                case Notexist:
                                    return "-";
                                case Isfile:
                                    return "f";
                                case Isdir:
                                    return "d";
                                case Islink:
                                    return "l / " % [&lpath]() -> QChar {
                                            switch(stype(lpath, true)) {
                                            case Notexist:
                                                return '-';
                                            case Isfile:
                                                return 'f';
                                            case Isdir:
                                                return 'd';
                                            case Isblock:
                                                return 'b';
                                            default:
                                                return '?';
                                            }
                                        }();
                                case Isblock:
                                    return "b";
                                default:
                                    return "?";
                                }
                            }());

                        break;
                    }
                    case S_IFBLK:
                        txt.append("b " % perm() % " - " % QStr::number(istat.st_uid) % ' ' % QStr::number(istat.st_gid) % ' ' % QStr::number(istat.st_dev) % ' ' % hunit(devsize(path)).replace(' ', nullptr));
                        break;
                    default:
                        txt.append('?');
                    }
                }

                if(! cut) cut = true;
            } while(path.count('/') > 1);
        }

        return (txt.contains("\n ./") ? txt.replace("\n ./", "\n /") : txt) % "\n\n Errno: " % QStr::number(cerrno) % "\n\n";
    }
}

bool sb::fopen(QFile &file)
{
    return file.open(QIODevice::ReadOnly) ? true : error("\n " % tr("An error occurred while opening the following file:") % "\n\n  " % file.fileName() % fdbg(file.fileName()), true);
}

bool sb::like(cQStr &txt, cQSL &lst, uchar mode)
{
    switch(mode) {
    default:
        for(cQStr &stxt : lst)
            if(stxt.startsWith('*'))
            {
                if(stxt.endsWith('*'))
                {
                    if(txt.contains(stxt.mid(1, stxt.length() - 2))) return true;
                }
                else if(txt.endsWith(stxt.mid(1, stxt.length() - 2)))
                    return true;
            }
            else if(stxt.endsWith('*'))
            {
                if(txt.startsWith(stxt.mid(1, stxt.length() - 2))) return true;
            }
            else if(txt == stxt.mid(1, stxt.length() - 2))
                return true;

        return false;
    case All:
        for(cQStr &stxt : lst)
            if(stxt.startsWith('*'))
            {
                if(stxt.endsWith('*'))
                {
                    if(! txt.contains(stxt.mid(1, stxt.length() - 2))) return false;
                }
                else if(! txt.endsWith(stxt.mid(1, stxt.length() - 2)))
                    return false;
            }
            else if(stxt.endsWith('*'))
            {
                if(! txt.startsWith(stxt.mid(1, stxt.length() - 2))) return false;
            }
            else if(txt != stxt.mid(1, stxt.length() - 2))
                return false;

        return true;
    case Mixed:
        QSL alst, nlst;

        for(cQStr &stxt : lst)
            switch(stxt.at(0).toLatin1()) {
            case '+':
                alst.append(right(stxt, -1));
                break;
            case '-':
                nlst.append(right(stxt, -1));
                break;
            default:
                return false;
            }

        return like(txt, alst, All) && like(txt, nlst);
    }
}

QStr sb::rndstr(uchar vlen)
{
    QStr val, chrs("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./");
    val.reserve(vlen), qsrand(QTime::currentTime().msecsSinceStartOfDay());
    uchar clen(vlen == 16 ? 64 : 62), num(255);

    do {
        uchar prev(num);
        while((num = qrand() % clen) == prev);
        val.append(chrs.at(num));
    } while(val.length() < vlen);

    return val;
}

bool sb::access(cQStr &path, uchar mode)
{
    switch(mode) {
    case Read:
        return QFileInfo(path).isReadable();
    case Write:
        return QFileInfo(path).isWritable();
    case Exec:
        return QFileInfo(path).isExecutable();
    default:
        return false;
    }
}

QStr sb::fload(cQStr &path, bool ascnt)
{
    QBA ba;
    { QFile file(path);
    if(! fopen(file)) return nullptr;
    ba = file.readAll(); }
    if(! ascnt || ba.isEmpty()) return ba;
    QSL lst;
    lst.reserve(100);
    QTS in(&ba, QIODevice::ReadOnly);
    while(! in.atEnd()) lst.append(in.readLine());
    QStr str;
    str.reserve(ba.size() + 1), ba.clear();
    for(ushort a(lst.count()) ; a ; --a) str.append(lst.at(a - 1) % '\n');
    return str;
}

QBA sb::fload(cQStr &path)
{
    QFile file(path);
    if(! fopen(file)) return nullptr;
    return file.readAll();
}

bool sb::islnxfs(cQStr &path)
{
    QTemporaryFile file(path % "/.sbdirtestfile_" % rndstr());
    return file.open() && file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther) && file.permissions() == 30548;
}

bool sb::cerr(uchar type, cQStr &str1, cQStr &str2)
{
    return error("\n " % [&, type]() -> QStr {
            switch(type) {
            case Crtdir:
                return tr("An error occurred while creating the following directory:");
            case Rmfile:
                return tr("An error occurred while removing the following file:");
            default:
                return tr("An error occurred while creating the following hard link:") % "\n\n  " % str2 % "\n\n " % tr("Reference file:");
            }
        }() % "\n\n  " % str1 % fdbg(str1, str2), true);
}

bool sb::crtfile(cQStr &path, cQStr &txt)
{
    auto err([&] { return error("\n " % tr("An error occurred while creating the following file:") % "\n\n  " % path % fdbg(path), true); });
    uchar otp(stype(path));
    if(! (like(otp, {Notexist, Isfile}) && isdir(left(path, rinstr(path, "/") - 1)))) return err();
    QFile file(path);
    if(! file.open(QFile::WriteOnly | QFile::Truncate) || file.write(txt.toUtf8()) == -1) return err();
    file.flush();
    return otp == Isfile || file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther) ? true : err();
}

bool sb::rename(cQStr &opath, cQStr &npath)
{
    return QFile::rename(opath, npath) ? true : error("\n " % tr("An error occurred while renaming the following item:") % "\n\n  " % opath % "\n\n " % tr("New path:") % "\n\n  " % npath % fdbg(opath, npath), true);
}

template<typename T1, typename T2> inline bool sb::crthlnk(const T1 &srclnk, const T2 &newlnk)
{
    return link(bstr(srclnk), bstr(newlnk)) ? cerr(Crthlnk, srclnk, newlnk) : true;
}

bool sb::lock(uchar type)
{
    return (sblock[type] = open([type] {
            switch(type) {
            case Sblock:
                return isdir("/run") ? "/run/systemback.lock" : "/var/run/systemback.lock";
            case Dpkglock:
                return "/var/lib/dpkg/lock";
            case Aptlock:
                return "/var/lib/apt/lists/lock";
            default:
                return isdir("/run") ? "/run/sbscheduler.lock" : "/var/run/sbscheduler.lock";
            }
        }(), O_RDWR | O_CREAT, 0644)) > -1 && ! lockf(sblock[type], F_TLOCK, 0);
}

void sb::unlock(uchar type)
{
    close(sblock[type]);
}

void sb::delay(ushort msec)
{
    QTime time;
    time.start();
    do msleep(10), qApp->processEvents();
    while(ushort(time.elapsed()) < msec);
}

void sb::thrdelay()
{
    while(SBThrd.isRunning()) msleep(10), qApp->processEvents();
}

bool sb::cfgwrite(cQStr &file)
{
    QStr cdir(file.startsWith("/.") ? isdir("/.sbsystemcopy" % sdir[0]) ? sdir[0] : "/home" : nullptr);

    return crtfile(file, "# Restore points settings\n#  storage_directory=<path>\n#  storage_dir_is_mount_point=[true/false]\n#  max_temporary_restore_points=[3-10]\n#  use_incremental_backup_method=[true/false]\n\n"
        "storage_directory=" % (cdir.isEmpty() ? sdir[0] : cdir) %
        "\nstorage_dir_is_mount_point=" % (cdir.isEmpty() ? ismpnt ? "true" : "false" : issmfs("/.sbsystemcopy" % cdir, cdir.count('/') == 1 ? "/.sbsystemcopy" : QStr("/.sbsystemcopy" % left(cdir, rinstr(cdir, "/") - 1))) ? "false" : "true") %
        "\nmax_temporary_restore_points=" % QStr::number(pnumber) %
        "\nuse_incremental_backup_method=" % (incrmtl ? "true" : "false") %
        "\n\n\n# Live system settings\n#  working_directory=<path>\n#  use_xz_compressor=[true/false]\n#  auto_iso_images=[true/false]\n\n"
        "working_directory=" % sdir[2] %
        "\nuse_xz_compressor=" % (xzcmpr ? "true" : "false") %
        "\nauto_iso_images=" % (autoiso ? "true" : "false") %
        "\n\n\n# Scheduler settigns\n#  enabled=[true/false]\n#  schedule=[0-7]:[0-23]:[0-59]:[10-99]\n#  silent=[true/false]\n#  window_position=[topleft/topright/center/bottomleft/bottomright]\n#  disable_starting_for_users=[false/everyone/:<username,list>]\n\n"
        "enabled=" % (cdir.isEmpty() && schdle[0] ? "true" : "false") %
        "\nschedule=" % QStr::number(schdle[1]) % ':' % QStr::number(schdle[2]) % ':' % QStr::number(schdle[3]) % ':' % QStr::number(schdle[4]) %
        "\nsilent=" % (schdle[5] ? "true" : "false") %
        "\nwindow_position=" % schdlr[0] %
        "\ndisable_starting_for_users=" % schdlr[1] %
        "\n\n\n# User interface settings\n#  language=[auto/<language_COUNTRY>]\n\n"
        "language=" % lang %
        "\n\n\n# Graphical user interface settings\n#  style=[auto/<name>]\n#  window_scaling_factor=[auto/1/1.5/2]\n#  always_on_top=[true/false]\n\n"
        "style=" % style %
        "\nwindow_scaling_factor=" % wsclng %
        "\nalways_on_top=" % (waot ? "true" : "false") %
        "\n\n\n# Host system settings\n#  disable_cache_emptying=[true/false]\n\n" %
        "disable_cache_emptying=" % (ecache ? "false" : "true") % '\n');
}

void sb::cfgread()
{
    if(! isdir("/etc/systemback"))
        crtdir("/etc/systemback");
    else if(isfile(cfgfile))
    {
        QFile file(cfgfile);

        if(fopen(file))
            while(! file.atEnd())
            {
                QStr cline(file.readLine().trimmed()), cval(right(cline, -instr(cline, "=")));

                if(! (cval.isEmpty() || cval.startsWith('#')))
                {
                    if(cline.startsWith("storage_directory="))
                        sdir[0] = cval;
                    else if(cline.startsWith("storage_dir_is_mount_point="))
                    {
                        if(cval == "true")
                            ismpnt = True;
                        else if(cval == "false")
                            ismpnt = False;
                    }
                    else if(cline.startsWith("max_temporary_restore_points="))
                        pnumber = cval.toUShort();
                    else if(cline.startsWith("use_incremental_backup_method="))
                    {
                        if(cval == "true")
                            incrmtl = True;
                        else if(cval == "false")
                            incrmtl = False;
                    }
                    else if(cline.startsWith("working_directory="))
                        sdir[2] = cval;
                    else if(cline.startsWith("use_xz_compressor="))
                    {
                        if(cval == "true")
                            xzcmpr = True;
                        else if(cval == "false")
                            xzcmpr = False;
                    }
                    else if(cline.startsWith("auto_iso_images="))
                    {
                        if(cval == "true")
                            autoiso = True;
                        else if(cval == "false")
                            autoiso = False;
                    }
                    else if(cline.startsWith("enabled="))
                    {
                        if(cval == "true")
                            schdle[0] = True;
                        else if(cval == "false")
                            schdle[0] = False;
                    }
                    else if(cline.startsWith("schedule="))
                    {
                        QSL vals(cval.split(':'));

                        if(vals.count() == 4)
                            for(uchar a(0) ; a < 4 ; ++a)
                            {
                                bool ok;
                                uchar num(vals.at(a).toUShort(&ok));
                                if(ok) schdle[a + 1] = num;
                            }
                    }
                    else if(cline.startsWith("silent="))
                    {
                        if(cval == "true")
                            schdle[5] = True;
                        else if(cval == "false")
                            schdle[5] = False;
                    }
                    else if(cline.startsWith("window_position="))
                        schdlr[0] = cval;
                    else if(cline.startsWith("disable_starting_for_users="))
                        schdlr[1] = cval;
                    else if(cline.startsWith("language="))
                        lang = cval;
                    else if(cline.startsWith("style="))
                        style = cval;
                    else if(cline.startsWith("window_scaling_factor="))
                        wsclng = cval;
                    else if(cline.startsWith("always_on_top="))
                    {
                        if(cval == "true")
                            waot = True;
                        else if(cval == "false")
                            waot = False;
                    }
                    else if(cline.startsWith("disable_cache_emptying="))
                    {
                        if(cval == "true")
                            ecache = False;
                        else if(cval == "false")
                            ecache = True;
                    }
                }
            }
    }

    bool cfgupdt(false);

    if(sdir[0].isEmpty())
    {
        sdir[0] = "/home", cfgupdt = true;
        if(ismpnt != Empty) ismpnt = Empty;
        if(! isdir("/home/Systemback")) crtdir("/home/Systemback");
        if(! isfile("/home/Systemback/.sbschedule")) crtfile("/home/Systemback/.sbschedule");
    }
    else
    {
        if(! isdir(sdir[0] % "/Systemback") && isdir(sdir[0]) && ! (ismpnt == True && issmfs(sdir[0], sdir[0].count('/') == 1 ? "/" : left(sdir[0], rinstr(sdir[0], "/") - 1))) && crtdir(sdir[0] % "/Systemback")) crtfile(sdir[0] % "/Systemback/.sbschedule");
        QStr cpath(QDir::cleanPath(sdir[0]));
        if(sdir[0] != cpath) sdir[0] = cpath, cfgupdt = true;
    }

    if(sdir[2].isEmpty())
    {
        sdir[2] = "/home";
        if(! cfgupdt) cfgupdt = true;
    }
    else
    {
        QStr cpath(QDir::cleanPath(sdir[2]));

        if(sdir[2] != cpath)
        {
            sdir[2] = cpath;
            if(! cfgupdt) cfgupdt = true;
        }
    }

    if(ismpnt == Empty)
    {
        QStr pdir(sdir[0].count('/') == 1 ? "/" : left(sdir[0], rinstr(sdir[0], "/") - 1));
        ismpnt = sdir[0] != pdir && isdir(pdir) && ! issmfs(sdir[0], pdir);
        if(! cfgupdt) cfgupdt = true;
    }

    if(incrmtl == Empty)
    {
        incrmtl = True;
        if(! cfgupdt) cfgupdt = true;
    }

    if(xzcmpr == Empty)
    {
        xzcmpr = False;
        if(! cfgupdt) cfgupdt = true;
    }

    if(autoiso == Empty)
    {
        autoiso = False;
        if(! cfgupdt) cfgupdt = true;
    }

    if(schdle[0] == Empty)
    {
        schdle[0] = False;
        if(! cfgupdt) cfgupdt = true;
    }

    if(schdle[1] > 7 || schdle[2] > 23 || schdle[3] > 59 || schdle[4] < 10 || schdle[4] > 99)
    {
        schdle[1] = 1, schdle[2] = schdle[3] = 0, schdle[4] = 10;
        if(! cfgupdt) cfgupdt = true;
    }
    else if(schdle[3] < 30 && ! (schdle[1] || schdle[2]))
    {
        schdle[3] = 30;
        if(! cfgupdt) cfgupdt = true;
    }

    if(schdle[5] == Empty)
    {
        schdle[5] = False;
        if(! cfgupdt) cfgupdt = true;
    }

    if(! like(schdlr[0], {"_topleft_", "_topright_", "_center_", "_bottomleft_", "_bottomright_"}))
    {
        schdlr[0] = "topright";
        if(! cfgupdt) cfgupdt = true;
    }

    if(! like(schdlr[1], {"_false_", "_everyone_", "_:*"}))
    {
        schdlr[1] = "false";
        if(! cfgupdt) cfgupdt = true;
    }

    if(pnumber < 3 || pnumber > 10)
    {
        pnumber = 5;
        if(! cfgupdt) cfgupdt = true;
    }

    if(lang.isEmpty() || ! (lang == "auto" || (lang.length() == 5 && lang.at(2) == '_' && lang.at(0).isLower() && lang.at(1).isLower() && lang.at(3).isUpper() && lang.at(4).isUpper())))
    {
        lang = "auto";
        if(! cfgupdt) cfgupdt = true;
    }

    if(style.isEmpty() || style.contains(' '))
    {
        style = "auto";
        if(! cfgupdt) cfgupdt = true;
    }

    if(! like(wsclng, {"_auto_", "_1_", "_1.5_", "_2_"}))
    {
        wsclng = "auto";
        if(! cfgupdt) cfgupdt = true;
    }

    if(waot == Empty)
    {
        waot = False;
        if(! cfgupdt) cfgupdt = true;
    }

    if(ecache == Empty)
    {
        ecache = True;
        if(! cfgupdt) cfgupdt = true;
    }

    sdir[1] = sdir[0] % "/Systemback";
    if(cfgupdt) cfgwrite();

    for(cQStr &file : {excfile, incfile})
        if(! isfile(file)) crtfile(file);
}

bool sb::execsrch(cQStr &fname, cQStr &ppath)
{
    for(cQStr &path : qgetenv("PATH").split(':'))
    {
        QStr fpath(ppath % path % '/' % fname);
        if(isfile(fpath)) return access(fpath, Exec);
    }

    return false;
}

uchar sb::exec(cQStr &cmd, uchar flag, cQStr &envv)
{
    auto exit([&cmd](uchar rv) -> uchar {
            if(! ExecKill && rv && ! like(cmd, {"_apt*", "_dpkg*", "_sbscheduler*"})) error("\n " % tr("An error occurred while executing the following command:") % "\n\n  " % cmd % "\n\n " % tr("Exit code:") % ' ' % QStr::number(rv) % "\n\n", true);
            return rv;
        });

    if(ExecKill) ExecKill = false;
    bool silent, bckgrnd, wait;
    uchar rprcnt;

    if(flag == Noflag)
        silent = bckgrnd = wait = false, rprcnt = 0;
    else
    {
        silent = flag != (flag & ~Silent), wait = flag != (flag & ~Wait);
        if((rprcnt = (bckgrnd = flag != (flag & ~Bckgrnd)) || wait || flag == (flag & ~Prgrss) ? 0 : cmd.startsWith("mksquashfs") ? 1 : cmd.startsWith("xorriso") ? 2 : cmd.startsWith("tar -cf") ? 3 : cmd.startsWith("tar -xf") ? 4 : 0)) Progress = 0;
    }

    QProcess proc;

    if(silent)
        proc.setStandardOutputFile("/dev/null"), proc.setStandardErrorFile("/dev/null");
    else if(! rprcnt)
        proc.setProcessChannelMode(QProcess::ForwardedChannels), proc.setInputChannelMode(QProcess::ForwardedInputChannel);

    if(! envv.isEmpty())
    {
        QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
        env.insert(left(envv, instr(envv, "=") - 1), right(envv, envv.length() - instr(envv, "="))), proc.setProcessEnvironment(env);
    }

    if(bckgrnd) return proc.startDetached(cmd) ? 0 : exit(255);
    proc.start(cmd, QProcess::ReadOnly);
    while(proc.state() == QProcess::Starting) msleep(10), qApp->processEvents();
    if(proc.error() == QProcess::FailedToStart) return exit(255);

    if(wait)
        proc.waitForFinished(-1);
    else
    {
        if(rprcnt == 1) setpriority(0, proc.pid(), 10);
        ullong inum(0);
        uchar cperc;

        while(proc.state() == QProcess::Running)
        {
            msleep(10), qApp->processEvents();
            if(ExecKill) proc.kill();

            switch(rprcnt) {
            case 1:
                if(Progress < (cperc = ((inum += proc.readAllStandardOutput().count('\n')) * 100 + 50) / ThrdLng[0])) Progress = cperc;
                QTS(stderr) << proc.readAllStandardError();

                if(dfree(sdir[2]) < 104857600)
                {
                    proc.kill();
                    return exit(255);
                }

                break;
            case 2:
            {
                QStr pout(proc.readAllStandardError());
                if(Progress < (cperc = mid(pout, rinstr(pout, "%") - 5, 2).toUShort())) Progress = cperc;
                break;
            }
            case 3:
                if(! ThrdLng[0])
                {
                    QBA itms;
                    QUCL itmst;
                    itms.reserve(10000), itmst.reserve(500);
                    ushort lcnt(0);
                    rodir(itms, itmst, sdir[2] % "/.sblivesystemcreate");

                    if(! itmst.isEmpty())
                    {
                        QTS in(&itms, QIODevice::ReadOnly);

                        while(! in.atEnd())
                        {
                            QStr item(in.readLine());
                            if(itmst.at(lcnt++) == Isfile) ThrdLng[0] += fsize(sdir[2] % "/.sblivesystemcreate/" % item);
                        }
                    }
                }
                else if(isfile(ThrdStr[0]) && Progress < (cperc = (fsize(ThrdStr[0]) * 100 + 50) / ThrdLng[0]))
                    Progress = cperc;

                break;
            case 4:
                QBA itms;
                QUCL itmst;
                itms.reserve(10000), itmst.reserve(500);
                ushort lcnt(0);
                rodir(itms, itmst, ThrdStr[0]);

                if(! itmst.isEmpty())
                {
                    QTS in(&itms, QIODevice::ReadOnly);
                    ullong size(0);

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine());
                        if(itmst.at(lcnt++) == Isfile) size += fsize(ThrdStr[0] % '/' % item);
                    }

                    if(Progress < (cperc = (size * 100 + 50) / ThrdLng[0])) Progress = cperc;
                }
            }
        }
    }

    return exit(proc.exitStatus() == QProcess::CrashExit ? 255 : proc.exitCode());
}

uchar sb::exec(cQSL &cmds)
{
    uchar rv;

    for(cQStr &cmd : cmds)
        if((rv = exec(cmd))) return rv;

    return 0;
}

bool sb::mcheck(cQStr &item, cQStr &mnts)
{
    cQStr &itm(item.contains(' ') ? bstr(item).rplc(" ", "\\040") : item);

    if(! itm.startsWith("/dev/"))
        return itm.endsWith('/') && itm.length() > 1 ? like(mnts, {"* " % left(itm, -1) % " *", "* " % itm % "*"}) : mnts.contains(' ' % itm % ' ');
    else if(QStr('\n' % mnts).contains('\n' % itm % (itm.length() > (item.contains("mmc") ? 12 : 8) ? " " : nullptr)))
        return true;
    else
    {
        blkid_probe pr(blkid_new_probe_from_filename(bstr(itm)));
        cchar *val(nullptr);
        blkid_do_probe(pr), blkid_probe_lookup_value(pr, "UUID", &val, nullptr);
        QStr uuid(val);
        blkid_free_probe(pr);
        return ! uuid.isEmpty() && QStr('\n' % mnts).contains("\n/dev/disk/by-uuid/" % uuid % ' ');
    }
}

QStr sb::gdetect(cQStr rdir)
{
    QStr mnts(fload("/proc/self/mounts", true));
    QTS in(&mnts, QIODevice::ReadOnly);
    QSL incl[]{{"* " % rdir % " *", "* " % rdir % (rdir.endsWith('/') ? nullptr : "/") % "boot *"}, {"_/dev/sd*", "_/dev/hd*", "_/dev/vd*"}};

    while(! in.atEnd())
    {
        QStr cline(in.readLine());

        if(like(cline, incl[0]))
        {
            if(like(cline, incl[1]))
                return left(cline, 8);
            else if(cline.startsWith("/dev/mmcblk"))
                return left(cline, 12);
            else if(cline.startsWith("/dev/disk/by-uuid"))
            {
                QStr uid(right(left(cline, instr(cline, " ") - 1), -18));

                if(islink("/dev/disk/by-uuid/" % uid))
                {
                    QStr dev(QFile("/dev/disk/by-uuid/" % uid).readLink());
                    return left(dev, dev.contains("mmc") ? 12 : 8);
                }
            }

            break;
        }
    }

    error("\n " % tr("Failed to detect the device for installing the GRUB!") % "\n\n", true);
    return nullptr;
}

void sb::pupgrade()
{
    bool rerun;

    do {
        rerun = false;

        for(uchar a(0) ; a < 15 ; ++a)
            if(! pnames[a].isEmpty()) pnames[a].clear();

        if(isdir(sdir[1]) && access(sdir[1], Write))
        {
            for(cQStr &item : QDir(sdir[1]).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
                if(! item.contains(' '))
                {
                    QStr pre(left(item, 4));

                    if(pre.at(1).isDigit() && pre.at(2).isDigit() && pre.at(3) == '_')
                    {
                        if(pre.at(0) == 'S')
                        {
                            if(pre.at(1) == '0' || mid(pre, 2, 2) == "10") pnames[mid(pre, 2, 2).toUShort() - 1] = right(item, -4);
                        }
                        else if(pre.at(0) == 'H' && pre.at(1) == '0' && like(pre.at(2), {"_1_", "_2_", "_3_", "_4_", "_5_"}))
                            pnames[9 + mid(pre, 3, 1).toUShort()] = right(item, -4);
                    }
                }

            for(uchar a(14) ; a ; --a)
                if(! (a == 10 || pnames[a].isEmpty()) && pnames[a - 1].isEmpty())
                {
                    rename(sdir[1] % (a > 10 ? QStr("/H0" % QStr::number(a - 9)) : (a < 9 ? "/S0" : "/S") % QStr::number(a + 1)) % '_' % pnames[a], sdir[1] % (a > 10 ? ("/H0" % QStr::number(a - 10)) : "/S0" % QStr::number(a)) % '_' % pnames[a]);
                    if(! rerun) rerun = true;
                }
        }
    } while(rerun);
}

void sb::supgrade()
{
    exec("apt-get update");

    QStr fyes([] {
            QProcess proc;
            proc.start("apt -v", QProcess::ReadOnly), proc.waitForFinished(-1);
            QStr sout(proc.readAllStandardOutput());
            return sout.length() < 7 || mid(sout, 5, 3).replace('.', nullptr).toUShort() < 12 ? "--force-yes" : "--allow-downgrades --allow-change-held-packages";
        }());

    forever
    {
        if(! exec({"apt-get install -fym " % fyes, "dpkg --configure -a", "apt-get dist-upgrade --no-install-recommends -ym " % fyes, "apt-get autoremove --purge -y"}))
        {
            QStr rklist;

            {
                QSL dlst(QDir("/boot").entryList(QDir::Files, QDir::Reversed));

                for(cQStr &item : dlst)
                    if(item.startsWith("vmlinuz-"))
                    {
                        QStr vmlinuz(right(item, -8)), kernel(left(vmlinuz, instr(vmlinuz, "-") - 1)), kver(mid(vmlinuz, kernel.length() + 2, instr(vmlinuz, "-", kernel.length() + 2) - kernel.length() - 2));

                        if(isnum(kver) && vmlinuz.startsWith(kernel % '-' % kver % '-') && ! rklist.contains(kernel % '-' % kver % "-*"))
                        {
                            for(ushort a(1) ; a < 101 ; ++a)
                            {
                                QStr subk(kernel % '-' % QStr::number(kver.toUShort() - a));

                                for(cQStr &ritem : dlst)
                                    if(ritem.startsWith("vmlinuz-" % subk % '-') && ! rklist.contains(' ' % subk % "-*")) rklist.append(' ' % subk % "-*");
                            }
                        }
                    }
            }

            uchar cproc(rklist.isEmpty() ? 0 : exec("apt-get autoremove --purge " % rklist));

            if(like(cproc, {0, 1}))
            {
                {
                    QProcess proc;
                    proc.start("dpkg -l", QProcess::ReadOnly), proc.waitForFinished(-1);
                    QBA sout(proc.readAllStandardOutput());
                    QStr iplist;
                    QTS in(&sout, QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        QStr cline(in.readLine());
                        if(cline.startsWith("rc")) iplist.append(' ' % mid(cline, 5, instr(cline, " ", 5) - 5));
                    }

                    if(! iplist.isEmpty()) exec("dpkg --purge " % iplist);
                }

                exec("apt-get clean");

                {
                    QSL dlst(QDir("/var/cache/apt").entryList(QDir::Files));

                    for(uchar a(0) ; a < dlst.count() ; ++a)
                    {
                        cQStr &item(dlst.at(a));
                        if(item.contains(".bin.")) rmfile("/var/cache/apt/" % item);
                    }
                }

                for(cQStr &item : QDir("/lib/modules").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
                    if(! exist("/boot/vmlinuz-" % item)) QDir("/lib/modules/" % item).removeRecursively();

                break;
            }
        }
        else
            exec("dpkg --configure -a");

        exec({"tput reset", "tput civis"});

        for(uchar a(3) ; a ; --a) error("\n " % tr("An error occurred while upgrading the system!") % '\n'),
                                  print("\n " % tr("Restart upgrade ...") % ' ' % QStr::number(a)),
                                  sleep(1), exec("tput cup 0 0");

        exec("tput reset");
    }
}

ullong sb::devsize(cQStr &dev)
{
    ullong bsize;
    int odev;
    bool err;

    if(! (err = (odev = open(bstr(dev), O_RDONLY)) == -1))
    {
        if(ioctl(odev, BLKGETSIZE64, &bsize) == -1) err = true;
        close(odev);
    }

    return err ? 0 : bsize;
}

bool sb::copy(cQStr &srcfile, cQStr &newfile)
{
    if(! isfile(srcfile)) return error("\n " % tr("This file could not be copied because it does not exist:") % "\n\n  " % srcfile % fdbg(srcfile), true);
    ThrdType = Copy,
    ThrdStr[0] = srcfile,
    ThrdStr[1] = newfile,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

QStr sb::ruuid(cQStr &part)
{
    ThrdType = Ruuid,
    ThrdStr[0] = part,
    SBThrd.start(), thrdelay();
    if(ThrdStr[1].isEmpty()) error("\n " % tr("The following partition has no UUID:") % "\n\n  " % part % "\n\n", true);
    return ThrdStr[1];
}

bool sb::srestore(uchar mthd, cQStr &usr, cQStr &srcdir, cQStr &trgt, bool sfstab)
{
    ThrdType = Srestore,
    ThrdChr = mthd,
    ThrdStr[0] = usr,
    ThrdStr[1] = srcdir,
    ThrdStr[2] = trgt,
    ThrdBool = sfstab,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

bool sb::mkpart(cQStr &dev, ullong start, ullong len, uchar type)
{
    auto err([&dev] { return error("\n " % tr("An error occurred while creating a new partition on the following device:") % "\n\n  " % dev % fdbg(dev), true); });
    if(dev.length() > (dev.contains("mmc") ? 12 : 8) || stype(dev) != Isblock) return err();
    ThrdType = Mkpart,
    ThrdStr[0] = dev,
    ThrdLng[0] = start,
    ThrdLng[1] = len,
    ThrdChr = type,
    SBThrd.start(), thrdelay();
    return ThrdRslt ? true : err();
}

bool sb::mount(cQStr &dev, cQStr &mpoint, cQStr &moptns)
{
    auto err([&dev] { return error("\n " % tr("An error occurred while mounting the following partition/image:") % "\n\n  " % dev % fdbg(dev), true); });
#ifdef C_MNT_LIB
    if(moptns == "loop") return exec("mount -o loop " % dev % ' ' % mpoint) ? err() : true;
#endif
    ThrdType = Mount,
    ThrdStr[0] = dev,
    ThrdStr[1] = mpoint,
    ThrdStr[2] = moptns,
    SBThrd.start(), thrdelay();
    return ThrdRslt ? true : err();
}

bool sb::scopy(uchar mthd, cQStr &usr, cQStr &srcdir)
{
    ThrdType = Scopy,
    ThrdChr = mthd,
    ThrdStr[0] = usr,
    ThrdStr[1] = srcdir,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

bool sb::crtrpoint(cQStr &pname)
{
    ThrdType = Crtrpoint,
    ThrdStr[0] = "/.S00_" % pname,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

bool sb::setpflag(cQStr &part, cQStr &flags)
{
    auto err([&] { return error("\n " % tr("An error occurred while setting one or more flags on the following partition:") % "\n\n  " % part % "\n\n " % tr("Flag(s):") % ' ' % flags % fdbg(part), true); });
    { bool ismmc(part.contains("mmc"));
    if(part.length() < (ismmc ? 14 : 9) || stype(part) != Isblock || stype(left(part, (ismmc ? 12 : 8))) != Isblock) return err(); }
    ThrdType = Setpflag,
    ThrdStr[0] = part,
    ThrdStr[1] = flags,
    SBThrd.start(), thrdelay();
    return ThrdRslt ? true : err();
}

bool sb::lvprpr(bool iudata)
{
    ThrdType = Lvprpr,
    ThrdBool = iudata,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

bool sb::mkptable(cQStr &dev, cQStr &type)
{
    auto err([&dev] { return error("\n " % tr("An error occurred while creating the partition table on the following device:") % "\n\n  " % dev % fdbg(dev), true); });
    if(dev.length() > (dev.contains("mmc") ? 12 : 8) || stype(dev) != Isblock) return err();
    ThrdType = Mkptable,
    ThrdStr[0] = dev,
    ThrdStr[1] = type,
    SBThrd.start(), thrdelay();
    return ThrdRslt ? true : err();
}

bool sb::remove(cQStr &path)
{
    ThrdType = Remove,
    ThrdStr[0] = path,
    SBThrd.start(), thrdelay();
    return ThrdRslt;
}

bool sb::umount(cQStr &dev)
{
    ThrdType = Umount,
    ThrdStr[0] = dev,
    SBThrd.start(), thrdelay();
    return ThrdRslt ? true : error("\n " % tr("An error occurred while unmounting the following partition/image/mount point:") % "\n\n  " % dev % fdbg(dev), true);
}

void sb::readlvdevs(QSL &strlst)
{
    ThrdType = Readlvdevs,
    ThrdSlst = &strlst,
    SBThrd.start(), thrdelay();
}

void sb::readprttns(QSL &strlst)
{
    ThrdType = Readprttns,
    ThrdSlst = &strlst,
    SBThrd.start(), thrdelay();
}

void sb::fssync()
{
    ThrdType = Sync,
    SBThrd.start(), thrdelay();
}

void sb::delpart(cQStr &part)
{
    if(stype(part) == Isblock)
    {
        ThrdType = Delpart,
        ThrdStr[0] = part,
        SBThrd.start(), thrdelay();
    }
}

inline QStr sb::rlink(cQStr &path, ushort blen)
{
    char rpath[blen];
    short rlen(readlink(bstr(path), rpath, blen));
    return rlen > 0 ? QStr(rpath).left(rlen) : nullptr;
}

uchar sb::fcomp(cQStr &file1, cQStr &file2)
{
    struct stat fstat[2];

    return stat(bstr(file1), &fstat[0]) || stat(bstr(file2), &fstat[1]) ? 0
        : fstat[0].st_size == fstat[1].st_size && fstat[0].st_mtim.tv_sec == fstat[1].st_mtim.tv_sec ? fstat[0].st_mode == fstat[1].st_mode && fstat[0].st_uid == fstat[1].st_uid && fstat[0].st_gid == fstat[1].st_gid ? 2 : 1 : 0;
}

bool sb::cpertime(cQStr &srcitem, cQStr &newitem, bool skel)
{
    auto err([&] { return error("\n " % tr("An error occurred while cloning the properties of the following item:") % "\n\n  " % srcitem % "\n\n " % tr("Target item:") % "\n\n  " % newitem % fdbg(srcitem, newitem), true); });
    struct stat istat[2];
    if(stat(bstr(srcitem), &istat[0])) return err();
    bstr nitem(newitem);
    if(stat(nitem, &istat[1])) return err();

    if(skel)
    {
        struct stat ustat;
        if(stat(bstr(left(newitem, instr(newitem, "/", 21) - 1)), &ustat)) return err();
        istat[0].st_uid = ustat.st_uid, istat[0].st_gid = ustat.st_gid;
    }

    if(istat[0].st_uid != istat[1].st_uid || istat[0].st_gid != istat[1].st_gid)
    {
        if(chown(nitem, istat[0].st_uid, istat[0].st_gid) || ((istat[0].st_mode != (istat[0].st_mode & ~(S_ISUID | S_ISGID)) || istat[0].st_mode != istat[1].st_mode) && chmod(nitem, istat[0].st_mode))) return err();
    }
    else if(istat[0].st_mode != istat[1].st_mode && chmod(nitem, istat[0].st_mode))
        return err();

    if(istat[0].st_atim.tv_sec != istat[1].st_atim.tv_sec || istat[0].st_mtim.tv_sec != istat[1].st_mtim.tv_sec)
    {
        utimbuf sitimes;
        sitimes.actime = istat[0].st_atim.tv_sec, sitimes.modtime = istat[0].st_mtim.tv_sec;
        if(utime(nitem, &sitimes)) return err();
    }

    return true;
}

bool sb::cplink(cQStr &srclink, cQStr &newlink)
{
    auto err([&] { return error("\n " % tr("An error occurred while cloning the following symbolic link:") % "\n\n  " % srclink % "\n\n " % tr("Target symlink:") % "\n\n  " % newlink % fdbg(srclink, newlink), true); });
    struct stat sistat;
    if(lstat(bstr(srclink), &sistat) || ! S_ISLNK(sistat.st_mode)) return err();
    QStr path(rlink(srclink, sistat.st_size));
    bstr nlink(newlink);
    if(path.isEmpty() || symlink(bstr(path), nlink)) return err();
    timeval sitimes[2];
    sitimes[0].tv_sec = sistat.st_atim.tv_sec, sitimes[1].tv_sec = sistat.st_mtim.tv_sec, sitimes[0].tv_usec = sitimes[1].tv_usec = 0;
    return lutimes(nlink, sitimes) ? err() : true;
}

bool sb::cpfile(cQStr &srcfile, cQStr &newfile, bool skel)
{
    auto err([&] { return error("\n " % tr("An error occurred while cloning the following file:") % "\n\n  " % srcfile % "\n\n " % tr("Target file:") % "\n\n  " % newfile % fdbg(srcfile, newfile), true); });
    int src, dst;
    struct stat fstat;
    { bstr sfile(srcfile);
    if(stat(sfile, &fstat) || (src = open(sfile, O_RDONLY | O_NOATIME)) == -1) return err(); }
    bstr nfile(newfile);
    bool herr;

    if(! (herr = (dst = creat(nfile, fstat.st_mode)) == -1))
    {
        if(fstat.st_size)
        {
            llong size(0);

            do {
                llong csize(size)
#ifdef __i386__
                    , rsize(fstat.st_size - size)
#endif
                    ;
                if((size += sendfile(dst, src, nullptr,
#ifdef __i386__
                    rsize < 2147483648 ? rsize : 2147483647
#else
                    fstat.st_size - size
#endif
                    )) <= csize) herr = true;
            } while(! herr && size < fstat.st_size);
        }

        close(dst);
    }

    close(src);
    if(herr) return err();

    if(skel)
    {
        struct stat ustat;
        if(stat(bstr(left(newfile, instr(newfile, "/", 21) - 1)), &ustat)) return err();
        fstat.st_uid = ustat.st_uid, fstat.st_gid = ustat.st_gid;
    }

    if(fstat.st_uid + fstat.st_gid && (chown(nfile, fstat.st_uid, fstat.st_gid) || (fstat.st_mode != (fstat.st_mode & ~(S_ISUID | S_ISGID)) && chmod(nfile, fstat.st_mode)))) return err();
    utimbuf sftimes;
    sftimes.actime = fstat.st_atim.tv_sec, sftimes.modtime = fstat.st_mtim.tv_sec;
    return utime(nfile, &sftimes) ? err() : true;
}

bool sb::cpdir(cQStr &srcdir, cQStr &newdir)
{
    auto err([&] { return error("\n " % tr("An error occurred while cloning the following directory:") % "\n\n  " % srcdir % "\n\n " % tr("Target directory:") % "\n\n  " % newdir % fdbg(srcdir, newdir), true); });
    struct stat dstat;
    if(stat(bstr(srcdir), &dstat) || ! S_ISDIR(dstat.st_mode)) return err();
    bstr ndir(newdir);
    if(mkdir(ndir, dstat.st_mode) || (dstat.st_uid + dstat.st_gid && (chown(ndir, dstat.st_uid, dstat.st_gid) || (dstat.st_mode != (dstat.st_mode & ~(S_ISUID | S_ISGID)) && chmod(ndir, dstat.st_mode))))) return err();
    utimbuf sdtimes;
    sdtimes.actime = dstat.st_atim.tv_sec, sdtimes.modtime = dstat.st_mtim.tv_sec;
    return utime(ndir, &sdtimes) ? err() : true;
}

void sb::edetect(QSL &elst, bool spath)
{
    QSL mpts;

    {
        QBA mnts(fload("./proc/self/mounts"));
        QTS in(&mnts, QIODevice::ReadOnly);

        while(! in.atEnd())
        {
            QStr cline(in.readLine());
            if(cline.contains(" /var/lib/")) mpts.append(cline.split(' ').at(1) % '/');
        }
    }

    if(! mpts.isEmpty())
    {
        if(isfile("./etc/fstab"))
        {
            QFile file("./etc/fstab");

            if(fopen(file))
                while(! file.atEnd())
                {
                    QStr cline(file.readLine().trimmed());

                    if(! cline.startsWith('#'))
                    {
                        cline.replace('\t', ' ');

                        for(uchar a(0) ; a < mpts.count() ; ++a)
                            if(cline.contains(' ' % left(mpts.at(a), -1) % ' '))
                            {
                                if(mpts.count() == 1) return;
                                mpts.removeAt(a);
                                break;
                            }
                    }
                }
        }

        if(spath)
            for(cQStr &mpt : mpts) elst.append(right(mpt, -5));
        else
            elst.append(mpts);
    }
}

bool sb::exclcheck(cQSL &elist, cQStr &item)
{
    for(cQStr &excl : elist)
        if(excl.endsWith('/'))
        {
            if(item.startsWith(excl)) return true;
        }
        else if(excl.endsWith('*'))
        {
            if(item.startsWith(left(excl, -1))) return true;
        }
        else if(like(item, {'_' % excl % '_', '_' % excl % "/*"}))
            return true;

    return false;
}

bool sb::inclcheck(cQSL &ilist, cQStr &item)
{
    for(cQStr &ixcl : ilist)
        if(ixcl.length() >= item.length())
        {
            if(like(ixcl, {'_' % item % '_', '_' % item % "/*"})) return true;
        }
        else if(item.startsWith(ixcl % '/'))
            return true;

    return false;
}

bool sb::lcomp(cQStr &link1, cQStr &link2)
{
    struct stat istat[2];
    if(lstat(bstr(link1), &istat[0]) || lstat(bstr(link2), &istat[1]) || ! (S_ISLNK(istat[0].st_mode) && S_ISLNK(istat[1].st_mode)) || istat[0].st_mtim.tv_sec != istat[1].st_mtim.tv_sec) return false;
    QStr lnk(rlink(link1, istat[0].st_size));
    return ! lnk.isEmpty() && lnk == rlink(link2, istat[1].st_size);
}

bool sb::rodir(QBA &ba, QUCL &ucl, cQStr &path, uchar hidden, cQSL &ilist, uchar oplen)
{
    DIR *dir(opendir(bstr(path)));

    if(dir)
    {
        dirent *ent;
        QStr ppath(ba.isEmpty() ? nullptr : QStr(right(path, -(oplen == 1 ? 1 : oplen + 1)) % '/'));
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QStr iname(ent->d_name), npath(ppath % iname);

            if(! like(iname, dd) && [&, hidden] {
                    switch(hidden) {
                    case False:
                        return true;
                    case True:
                        return like(iname, {"_.*", "_snap_"}) || (! ilist.isEmpty() && inclcheck(ilist, iname));
                    default:
                        return like(npath, {"_.*", "_snap/*"}) || inclcheck(ilist, npath);
                    }
                }())
            {
                uchar type([&]() -> uchar {
                        switch(ent->d_type) {
                        case DT_LNK:
                            return Islink;
                        case DT_DIR:
                            return Isdir;
                        case DT_REG:
                            return Isfile;
                        case DT_UNKNOWN:
                            return stype(path % '/' % iname);
                        default:
                            return Unknown;
                        }
                    }());

                switch(type) {
                case Islink:
                case Isfile:
                    ucl.append(type), ba.append(npath % '\n');
                    break;
                case Isdir:
                    rodir(ba.append(npath % '\n'), ucl << Isdir, path % '/' % iname, hidden == True ? ilist.isEmpty() ? uchar(False) : uchar(Include) : hidden, ilist, (oplen ? oplen : path.length()));
                }
            }
        }

        closedir(dir);
        if(! (ThrdKill || oplen)) ba.squeeze();
    }

    return ! ThrdKill;
}

bool sb::rodir(cQStr &path, QBA &ba, QUCL &ucl, ullong id, uchar oplen)
{
    DIR *dir(opendir(bstr(path)));

    if(dir)
    {
        if(! oplen)
        {
            struct stat dstat;
            if(! stat(bstr(path), &dstat)) id = dstat.st_dev;
        }

        dirent *ent;
        QStr ppath(ba.isEmpty() ? nullptr : QStr(right(path, -(oplen == 1 ? 1 : oplen + 1)) % '/'));
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QStr iname(ent->d_name), npath(ppath % iname);

            if(! like(iname, dd))
            {
                uchar type([&]() -> uchar {
                        switch(ent->d_type) {
                        case DT_LNK:
                            return Islink;
                        case DT_DIR:
                            return Isdir;
                        case DT_REG:
                            return Isfile;
                        case DT_UNKNOWN:
                            return stype(path % '/' % iname);
                        default:
                            return Unknown;
                        }
                    }());

                switch(type) {
                case Islink:
                case Isfile:
                    ucl.append(type), ba.append(npath % '\n');
                    break;
                case Isdir:
                    QStr fpath(path % '/' % iname);
                    struct stat dstat;

                    if(! stat(bstr(fpath), &dstat) && dstat.st_dev == id)
                        rodir(fpath, ba.append(npath % '\n'), ucl << Isdir, id, (oplen ? oplen : path.length()));
                    else
                        ucl.append(Isdir), ba.append(npath % '\n');
                }
            }
        }

        closedir(dir);
        if(! (ThrdKill || oplen)) ba.squeeze();
    }

    return ! ThrdKill;
}

bool sb::rodir(QBA &ba, cQStr &path, uchar oplen)
{
    DIR *dir(opendir(bstr(path)));

    if(dir)
    {
        dirent *ent;
        QStr ppath(ba.isEmpty() ? nullptr : QStr(right(path, -(oplen == 1 ? 1 : oplen + 1)) % '/'));
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QStr iname(ent->d_name);

            if(! like(iname, dd))
                switch([&]() -> uchar {
                        switch(ent->d_type) {
                        case DT_LNK:
                        case DT_REG:
                            return Isfile;
                        case DT_DIR:
                            return Isdir;
                        case DT_UNKNOWN:
                            return stype(path % '/' % iname);
                        default:
                            return Unknown;
                        }
                    }()) {
                case Islink:
                case Isfile:
                    ba.append(ppath % iname % '\n');
                    break;
                case Isdir:
                    rodir(ba.append(ppath % iname % '\n'), path % '/' % iname, (oplen ? oplen : path.length()));
                }
        }

        closedir(dir);
        if(! (ThrdKill || oplen)) ba.squeeze();
    }

    return ! ThrdKill;
}

bool sb::rodir(QUCL &ucl, cQStr &path, uchar oplen)
{
    DIR *dir(opendir(bstr(path)));

    if(dir)
    {
        dirent *ent;
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QStr iname(ent->d_name);

            if(! like(iname, dd))
                switch([&]() -> uchar {
                        switch(ent->d_type) {
                        case DT_LNK:
                        case DT_REG:
                            return Isfile;
                        case DT_DIR:
                            return Isdir;
                        case DT_UNKNOWN:
                            return stype(path % '/' % iname);
                        default:
                            return Unknown;
                        }
                    }()) {
                case Islink:
                case Isfile:
                    ucl.append(0);
                    break;
                case Isdir:
                    rodir(ucl << 0, path % '/' % iname, (oplen ? oplen : path.length()));
                }
        }

        closedir(dir);
    }

    return ! ThrdKill;
}

bool sb::odir(QBAL &balst, cQStr &path, uchar hidden, cQSL &ilist, cQStr &ppath)
{
    DIR *dir(opendir(bstr(path)));

    if(dir)
    {
        balst.reserve(1000);
        dirent *ent;
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QStr iname(ent->d_name);

            if(! like(iname, dd) && [&, hidden] {
                    switch(hidden) {
                    case False:
                        return true;
                    case True:
                        return like(iname, {"_.*", "_snap_"}) || (! ilist.isEmpty() && inclcheck(ilist, iname));
                    default:
                        return inclcheck(ilist, ppath % '/' % iname);
                    }
                }()) balst.append(QBA(ent->d_name));
        }

        closedir(dir);
    }

    return ! ThrdKill;
}

bool sb::recrmdir(cbstr &path, bool slimit)
{
    DIR *dir(opendir(path));

    if(dir)
    {
        auto size([](cbstr &spath) -> ullong {
                struct stat fstat;
                if(stat(spath, &fstat)) return 8000001;
                return fstat.st_size;
            });

        dirent *ent;
        QSL dd{"_._", "_.._"};

        while(! ThrdKill && (ent = readdir(dir)))
        {
            QBA iname(ent->d_name);

            if(! like(iname, dd))
            {
                QBA fpath(QBA(path) % '/' % iname);

                switch(ent->d_type) {
                case DT_UNKNOWN:
                    switch(stype(fpath)) {
                    case Isfile:
                        if(slimit && size(fpath) > 8000000) continue;
                    default:
                        rmfile(fpath);
                        continue;
                    case Isdir:;
                    }
                case DT_DIR:
                    if(! recrmdir(fpath, slimit))
                    {
                        closedir(dir);
                        return false;
                    }

                    break;
                case DT_REG:
                    if(slimit && size(fpath) > 8000000) break;
                default:
                    rmfile(fpath);
                }
            }
        }

        closedir(dir);
    }

    return ! ThrdKill && (! rmdir(path) || slimit || (errno == ENOTEMPTY ? false : error("\n " % tr("An error occurred while deleting the following directory:") % "\n\n  " % QStr(path) % fdbg(QStr(path)), true)));
}

void sb::run()
{
    if(ThrdKill) ThrdKill = false;

    auto psalign([](ullong pstart, ushort ssize) -> ullong {
            if(pstart <= 1048576 / ssize) return 1048576 / ssize;
            ushort rem(pstart % (1048576 / ssize));
            return rem ? pstart + 1048576 / ssize - rem : pstart;
        });

    auto pealign([](ullong end, ushort ssize) -> ullong {
            ushort rem(end % (1048576 / ssize));
            return rem ? rem < (1048576 / ssize) - 1 ? end - rem - 1 : end : end - 1;
        });

    switch(ThrdType) {
    case Remove:
        ThrdRslt = [this] {
                switch(stype(ThrdStr[0])) {
                case Isdir:
                    return recrmdir(ThrdStr[0]);
                default:
                    return rmfile(ThrdStr[0]);
                }
            }();

        break;
    case Copy:
        ThrdRslt = cpfile(ThrdStr[0], ThrdStr[1]);
        break;
    case Sync:
        return sync();
    case Mount:
    {
        libmnt_context *mcxt(mnt_new_context());
        mnt_context_set_source(mcxt, bstr(ThrdStr[0])),
        mnt_context_set_target(mcxt, bstr(ThrdStr[1])),
        mnt_context_set_options(mcxt, ! ThrdStr[2].isEmpty() ? bstr(ThrdStr[2]).data : isdir(ThrdStr[0]) ? "bind" : "noatime"),
        ThrdRslt = ! mnt_context_mount(mcxt);
        return mnt_free_context(mcxt);
    }
    case Umount:
        ThrdRslt = umnt(ThrdStr[0]);
        return;
    case Readprttns:
    {
        ThrdSlst->reserve(25);
        QSL dlst{"_/dev/sd*", "_/dev/hd*", "_/dev/vd*", "_/dev/mmcblk*"};

        for(cQStr &spath : QDir("/dev").entryList(QDir::System))
        {
            QStr path("/dev/" % spath);

            if(like(path.length(), {8, 12}) && like(path, dlst) && devsize(path) > 536870911)
            {
                PedDevice *dev(ped_device_get(bstr(path)));
                PedDisk *dsk(ped_disk_new(dev));

                uchar type(dsk ? [&dsk] {
                        QStr name(dsk->type->name);
                        return name == "gpt" ? GPT : name == "msdos" ? MSDOS : Clear;
                    }() : Clear);

                ThrdSlst->append(path % '\n' % QStr::number(dev->length * dev->sector_size) % '\n' % QStr::number(type));

                if(type != Clear)
                {
                    PedPartition *prt(nullptr);
                    QLIL egeom;

                    while((prt = ped_disk_next_partition(dsk, prt)))
                        if(prt->geom.length >= 1048576 / dev->sector_size)
                        {
                            if(prt->num > 0)
                            {
                                QStr ppath(path % (path.length() == 12 ? "p" : nullptr) % QStr::number(prt->num));

                                if(stype(ppath) == Isblock)
                                {
                                    if(prt->type == PED_PARTITION_EXTENDED)
                                        ThrdSlst->append(ppath % '\n' % QStr::number(prt->geom.length * dev->sector_size) % '\n' % QStr::number(Extended) % '\n' % QStr::number(prt->geom.start * dev->sector_size)),
                                        egeom.append({prt->geom.start, prt->geom.end});
                                    else
                                    {
                                        if(egeom.count() > 2)
                                        {
                                            ullong pstart(psalign(egeom.at(2), dev->sector_size));
                                            ThrdSlst->append(path % "?\n" % QStr::number((pealign(egeom.at(3), dev->sector_size) - pstart + 1) * dev->sector_size - (prt->type == PED_PARTITION_LOGICAL ? 2097152 : 1048576 - dev->sector_size)) % '\n' % QStr::number(Emptyspace) % '\n' % QStr::number(pstart * dev->sector_size + 1048576));
                                            for(uchar a(3) ; a > 1 ; --a) egeom.removeAt(a);
                                        }

                                        blkid_probe pr(blkid_new_probe_from_filename(bstr(ppath)));
                                        blkid_do_probe(pr);
                                        cchar *uuid(nullptr), *fstype("?"), *label(nullptr);

                                        if(! blkid_probe_lookup_value(pr, "UUID", &uuid, nullptr)) blkid_probe_lookup_value(pr, "TYPE", &fstype, nullptr),
                                                                                                   blkid_probe_lookup_value(pr, "LABEL", &label, nullptr);

                                        ThrdSlst->append(ppath % '\n' % QStr::number(prt->geom.length * dev->sector_size) % '\n' % QStr::number(prt->type == PED_PARTITION_LOGICAL ? Logical : Primary) % '\n' % QStr::number(prt->geom.start * dev->sector_size) % '\n' % fstype % '\n' % label % '\n' % uuid),
                                        blkid_free_probe(pr);
                                    }
                                }
                            }
                            else if(prt->type == PED_PARTITION_FREESPACE)
                            {
                                if(egeom.count() > 2)
                                {
                                    ullong pstart(psalign(egeom.at(2), dev->sector_size));
                                    ThrdSlst->append(path % "?\n" % QStr::number((pealign(egeom.at(3), dev->sector_size) - pstart + 1) * dev->sector_size - 1048576) % '\n' % QStr::number(Emptyspace) % '\n' % QStr::number(pstart * dev->sector_size + 1048576)),
                                    egeom.clear();
                                }

                                llong fgeom[]{llong(prt->geom.start < 1048576 / dev->sector_size ? 1048576 / dev->sector_size : psalign(prt->geom.start, dev->sector_size)), llong(prt->next && prt->next->type == PED_PARTITION_METADATA ? type == MSDOS ? prt->next->geom.end : prt->next->geom.end - (34816 / dev->sector_size * 10 + 5) / 10 : pealign(prt->geom.end, dev->sector_size))};
                                if(fgeom[1] - fgeom[0] > 1048576 / dev->sector_size - 2) ThrdSlst->append(path % "?\n" % QStr::number((fgeom[1] - fgeom[0] + 1) * dev->sector_size) % '\n' % QStr::number(Freespace) % '\n' % QStr::number(fgeom[0] * dev->sector_size));
                            }
                            else if(! egeom.isEmpty())
                            {
                                if(prt->geom.end <= egeom.at(1))
                                    switch(egeom.count()) {
                                    case 2:
                                        if(prt->geom.length >= 3145728 / dev->sector_size) egeom.append({(prt->geom.start - egeom.at(0) < 1048576 / dev->sector_size ? egeom.at(0) : prt->geom.start), prt->geom.end + (prt->geom.end == egeom.at(1) ? 1 : 0)});
                                        break;
                                    default:
                                        egeom.replace(3, prt->geom.end + (prt->geom.end == egeom.at(1) ? 1 : 0));
                                    }
                                else
                                    egeom.clear();
                            }
                        }

                    if(egeom.count() > 2)
                    {
                        ullong pstart(psalign(egeom.at(2), dev->sector_size));
                        ThrdSlst->append(path % "?\n" % QStr::number((pealign(egeom.at(3), dev->sector_size) - pstart + 1) * dev->sector_size - 1048576) % '\n' % QStr::number(Emptyspace) % '\n' % QStr::number(pstart * dev->sector_size + 1048576));
                    }
                }
                else if(! dsk)
                    goto next_1;

                ped_disk_destroy(dsk);
            next_1:
                ped_device_destroy(dev);
            }
        }

        break;
    }
    case Readlvdevs:
    {
        ThrdSlst->reserve(10);
        QBA fstab(fload("/etc/fstab"));
        QSL dlst[]{{"_usb-*", "_mmc-*"}, {"_/dev/sd*", "_/dev/mmcblk*"}};

        for(cQStr &item : QDir("/dev/disk/by-id").entryList(QDir::Files))
        {
            if(like(item, dlst[0]) && islink("/dev/disk/by-id/" % item))
            {
                QStr path(rlink("/dev/disk/by-id/" % item, 14));

                if(! path.isEmpty() && like((path = "/dev" % right(path, -5)).length(), {8, 12}) && like(path, dlst[1]))
                {
                    ullong size(devsize(path));

                    if(size > 536870911)
                    {
                        if(! fstab.isEmpty())
                        {
                            QSL fchk('_' % path % '*');

                            {
                                PedDevice *dev(ped_device_get(bstr(path)));
                                PedDisk *dsk(ped_disk_new(dev));

                                if(dsk)
                                {
                                    PedPartition *prt(nullptr);

                                    while((prt = ped_disk_next_partition(dsk, prt)))
                                        if(prt->num > 0 && prt->type != PED_PARTITION_EXTENDED)
                                        {
                                            QStr ppath(path % (path.length() == 12 ? "p" : nullptr) % QStr::number(prt->num));

                                            if(stype(ppath) == Isblock)
                                            {
                                                blkid_probe pr(blkid_new_probe_from_filename(bstr(ppath)));
                                                blkid_do_probe(pr);
                                                cchar *uuid(nullptr);
                                                if(! blkid_probe_lookup_value(pr, "UUID", &uuid, nullptr) && uuid) fchk.append("_UUID=" % QStr(uuid) % '*');
                                                blkid_free_probe(pr);
                                            }
                                        }

                                    ped_disk_destroy(dsk);
                                }

                                ped_device_destroy(dev);
                            }

                            QTS in(&fstab, QIODevice::ReadOnly);

                            while(! in.atEnd())
                                if(like(in.readLine().trimmed(), fchk)) goto next_2;
                        }

                        ThrdSlst->append(path % '\n' % mid(item, 5, rinstr(item, "_") - 5).replace('_', ' ') % '\n' % QStr::number(size));
                    }
                }
            }

        next_2:;
        }

        return ThrdSlst->sort();
    }
    case Ruuid:
    {
        blkid_probe pr(blkid_new_probe_from_filename(bstr(ThrdStr[0])));
        blkid_do_probe(pr);
        cchar *uuid(nullptr);
        blkid_probe_lookup_value(pr, "UUID", &uuid, nullptr),
        ThrdStr[1] = uuid;
        return blkid_free_probe(pr);
    }
    case Setpflag:
    {
        PedDevice *dev(ped_device_get(bstr(left(ThrdStr[0], (ThrdStr[0].contains("mmc") ? 12 : 8)))));
        PedDisk *dsk(ped_disk_new(dev));
        PedPartition *prt(nullptr);
        if(ThrdRslt) ThrdRslt = false;

        while(! ThrdKill && (prt = ped_disk_next_partition(dsk, prt)))
            if(QStr(ped_partition_get_path(prt)) == ThrdStr[0])
            {
                for(cQStr &flag : ThrdStr[1].split(' '))
                    if(! ped_partition_set_flag(prt, ped_partition_flag_get_by_name(bstr(flag)), 1)) goto err;

                if(ped_disk_commit_to_dev(dsk)) ThrdRslt = true;
            err:
                ped_disk_commit_to_os(dsk);
                break;
            }

        return ped_disk_destroy(dsk), ped_device_destroy(dev);
    }
    case Mkptable:
    {
        PedDevice *dev(ped_device_get(bstr(ThrdStr[0])));
        PedDisk *dsk(ped_disk_new_fresh(dev, ped_disk_type_get(bstr(ThrdStr[1]))));
        ThrdRslt = ped_disk_commit_to_dev(dsk);
        return ped_disk_commit_to_os(dsk), ped_disk_destroy(dsk), ped_device_destroy(dev);
    }
    case Mkpart:
    {
        PedDevice *dev(ped_device_get(bstr(ThrdStr[0])));
        PedDisk *dsk(ped_disk_new(dev));
        PedPartition *prt(nullptr);
        if(ThrdRslt) ThrdRslt = false;

        if(ThrdLng[0] && ThrdLng[1])
        {
            PedPartition *crtprt(ped_partition_new(dsk, ThrdChr == Primary ? PED_PARTITION_NORMAL : ThrdChr == Extended ? PED_PARTITION_EXTENDED : PED_PARTITION_LOGICAL, ped_file_system_type_get("ext2"), psalign(ThrdLng[0] / dev->sector_size, dev->sector_size), ullong(dev->length - 1048576 / dev->sector_size) >= (ThrdLng[0] + ThrdLng[1]) / dev->sector_size - 1 ? pealign((ThrdLng[0] + ThrdLng[1]) / dev->sector_size - 1, dev->sector_size) : (ThrdLng[0] + ThrdLng[1]) / dev->sector_size - 1));
            if(ped_disk_add_partition(dsk, crtprt, ped_constraint_exact(&crtprt->geom)) && ped_disk_commit_to_dev(dsk)) ThrdRslt = true;
            ped_disk_commit_to_os(dsk);
        }
        else
            while(! ThrdKill && (prt = ped_disk_next_partition(dsk, prt)))
                if(prt->type == PED_PARTITION_FREESPACE && prt->geom.length >= 1048576 / dev->sector_size)
                {
                    PedPartition *crtprt(ped_partition_new(dsk, PED_PARTITION_NORMAL, ped_file_system_type_get("ext2"), ThrdLng[0] ? psalign(ThrdLng[0] / dev->sector_size, dev->sector_size) : psalign(prt->geom.start, dev->sector_size), ThrdLng[1] ? pealign((ThrdLng[0] + ThrdLng[1]) / dev->sector_size - 1, dev->sector_size) : prt->next && prt->next->type == PED_PARTITION_METADATA ? prt->next->geom.end : pealign(prt->geom.end, dev->sector_size)));
                    if(ped_disk_add_partition(dsk, crtprt, ped_constraint_exact(&crtprt->geom)) && ped_disk_commit_to_dev(dsk)) ThrdRslt = true;
                    ped_disk_commit_to_os(dsk);
                    break;
                }

        return ped_disk_destroy(dsk), ped_device_destroy(dev);
    }
    case Delpart:
    {
        bool ismmc(ThrdStr[0].contains("mmc"));
        PedDevice *dev(ped_device_get(bstr(left(ThrdStr[0], ismmc ? 12 : 8))));
        PedDisk *dsk(ped_disk_new(dev));
        if(ped_disk_delete_partition(dsk, ped_disk_get_partition(dsk, right(ThrdStr[0], -(ismmc ? 13 : 8)).toUShort()))) ped_disk_commit_to_dev(dsk), ped_disk_commit_to_os(dsk);
        return ped_disk_destroy(dsk), ped_device_destroy(dev);
    }
    case Crtrpoint:
        ThrdRslt = thrdcrtrpoint(ThrdStr[0]);
        break;
    case Srestore:
        ThrdRslt = thrdsrestore(ThrdChr, ThrdStr[0], ThrdStr[1], ThrdStr[2], ThrdBool);
        break;
    case Scopy:
        ThrdRslt = thrdscopy(ThrdChr, ThrdStr[0], ThrdStr[1]);
        break;
    case Lvprpr:
        ThrdRslt = thrdlvprpr(ThrdBool);
    }
}

bool sb::umnt(cbstr &dev)
{
    libmnt_context *ucxt(mnt_new_context());
    mnt_context_set_target(ucxt, dev),
    mnt_context_enable_force(ucxt, true),
    mnt_context_enable_lazy(ucxt, true);
#ifndef C_MNT_LIB
    mnt_context_enable_loopdel(ucxt, true);
#endif
    bool rv(! mnt_context_umount(ucxt));
    mnt_free_context(ucxt);
    return rv;
}

bool sb::thrdcrtrpoint(cQStr &trgt)
{
    QStr rsdir('.' % QDir(sdir[1]).canonicalPath());
    if(chroot(bstr(sdir[1]))) return false;

    auto out([](bool val = false) {
            chroot(bstr("./"));
            return val;
        });

    QStr rtrgt(rsdir % trgt);
    uint lcnt;

    {
        QBA sysitms[13];
        QUCL sysitmst[13];

        {
            QStr dirs[]{"./bin", "./boot", "./etc", "./lib", "./lib32", "./lib64", "./opt", "./sbin", "./selinux", "./srv", "./usr", "./var"};
            uint bbs[]{10000, 20000, 100000, 500000, 10000, 10000, 100000, 10000, 10000, 10000, 10000000, 1000000}, ibs[]{500, 1000, 10000, 20000, 500, 500, 5000, 1000, 500, 500, 500000, 50000};

            for(uchar a(0) ; a < 12 ; ++a)
                if(isdir(dirs[a]))
                {
                    sysitms[a].reserve(bbs[a]), sysitmst[a].reserve(ibs[a]);
                    if(! rodir(sysitms[a], sysitmst[a], dirs[a])) return out();
                }
        }

        if(isdir("./snap"))
        {
            sysitms[12].reserve(10000), sysitmst[12].reserve(500);
            if(! rodir("./snap", sysitms[12], sysitmst[12])) return out();
        }

        uint anum(0), cnum(0);
        for(uchar a(0) ; a < 12 ; ++a) anum += sysitmst[a].count();
        QSL rplst;
        QBA *cditms;
        QUCL *cditmst;
        uchar cperc;

        {
            QSL usrs;
            QBAL homeitms;
            QUCLL homeitmst;

            {
                QFile file("./etc/passwd");
                if(! fopen(file)) return out();

                while(! file.atEnd())
                {
                    QStr usr(file.readLine().trimmed());

                    if(usr.contains(":/home/") && isdir("./home/" % (usr = left(usr, instr(usr, ":") -1)))) usrs.append(usr),
                                                                                                            homeitms.append(nullptr),
                                                                                                            homeitmst.append(QUCL());
                }
            }

            {
                QSL ilst;

                {
                    QFile file("." incfile);
                    if(! fopen(file)) return out();

                    while(! file.atEnd())
                    {
                        QStr cline(left(file.readLine(), -1));
                        if(! cline.isEmpty()) ilst.append(cline);
                        if(ThrdKill) return out();
                    }
                }

                if(! (usrs << (isdir("./root") ? "" : nullptr)).last().isNull())
                {
                    homeitms.append(nullptr), homeitms.last().reserve(50000),
                    homeitmst.append(QUCL()), homeitmst.last().reserve(1000);
                    if(! rodir(homeitms.last(), homeitmst.last(), "./root", True, ilst)) return out();
                }

                for(schar a(usrs.count() - 2) ; a > -1 ; --a)
                {
                    homeitms[a].reserve(5000000), homeitmst[a].reserve(100000);
                    if(! rodir(homeitms[a], homeitmst[a], "./home/" % usrs.at(a), True, ilst)) return out();
                }
            }

            for(cQUCL &cucl : homeitmst) anum += cucl.count();
            Progress = 0;
            if(! crtdir(rtrgt) || (isdir("./home") && ! crtdir(rtrgt % "/home"))) return out();

            if(incrmtl)
            {
                rplst.reserve(15);
                QSL incl{"_S01_*", "_S02_*", "_S03_*", "_S04_*", "_S05_*", "_S06_*", "_S07_*", "_S08_*", "_S09_*", "_S10_*", "_H01_*", "_H02_*", "_H03_*", "_H04_*", "_H05_*"};

                for(cQStr &item : QDir("/").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
                {
                    if(like(item, incl)) rplst.append(item);
                    if(ThrdKill) return out();
                }
            }

            QSL elst{".sbuserdata", ".cache/gvfs", ".local/share/Trash/files/", ".local/share/Trash/info/", ".Xauthority", ".ICEauthority"};

            {
                QFile file("." excfile);
                if(! fopen(file)) return out();

                while(! file.atEnd())
                {
                    QStr cline(left(file.readLine(), -1));
                    if(like(cline, {"_.*", "_snap_", "_snap/*"})) elst.append(cline);
                    if(ThrdKill) return out();
                }
            }

            QSL excl{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*~_", "*~/*"};

            for(uchar a(0) ; a < usrs.count() ; ++a)
            {
                cQStr &usr(usrs.at(a));

                if(! usr.isNull())
                {
                    QStr srcd(usr.isEmpty() ? QStr("/root") : "/home/" % usr);
                    if(! crtdir(rtrgt % srcd)) return out();

                    if(! (cditmst = &homeitmst[a])->isEmpty())
                    {
                        lcnt = 0;
                        QTS in(cditms = &homeitms[a], QIODevice::ReadOnly);

                        while(! in.atEnd())
                        {
                            if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                            QStr item(in.readLine());

                            if(! (like(item, excl) || (like(item, {"_.*", "_snap_", "_snap/*"}) && exclcheck(elst, item))))
                            {
                                QStr srci(srcd % '/' % item), rsrci('.' % srci);

                                if(exist(rsrci))
                                {
                                    QStr nrpi(rtrgt % srci);

                                    switch(cditmst->at(lcnt)) {
                                    case Islink:
                                        for(cQStr &pname : rplst)
                                        {
                                            QStr orpi(rsdir % '/' % pname % srci);

                                            if(stype(orpi) == Islink && lcomp(rsrci, orpi))
                                            {
                                                if(! crthlnk(orpi, nrpi)) return out();
                                                goto nitem_1;
                                            }

                                            if(ThrdKill) return out();
                                        }

                                        if(! cplink(rsrci, nrpi)) return out();
                                        break;
                                    case Isdir:
                                        if(! crtdir(nrpi)) return out();
                                        break;
                                    case Isfile:
                                        if(fsize(rsrci) <= 8000000)
                                        {
                                            for(cQStr &pname : rplst)
                                            {
                                                QStr orpi(rsdir % '/' % pname % srci);

                                                if(stype(orpi) == Isfile && fcomp(rsrci, orpi) == 2)
                                                {
                                                    if(! crthlnk(orpi, nrpi)) return out();
                                                    goto nitem_1;
                                                }

                                                if(ThrdKill) return out();
                                            }

                                            if(! cpfile(rsrci, nrpi)) return out();
                                        }
                                    }
                                }
                            }

                        nitem_1:
                            if(ThrdKill) return out();
                            ++lcnt;
                        }

                        in.seek(0), lcnt = 0;

                        while(! in.atEnd())
                        {
                            QStr item(in.readLine());

                            if(cditmst->at(lcnt++) == Isdir)
                            {
                                QStr srci(srcd % '/' % item), nrpi(rtrgt % srci);
                                if(exist(nrpi) && ! cpertime('.' % srci, nrpi)) return out();
                            }

                            if(ThrdKill) return out();
                        }

                        cditms->clear(), cditmst->clear();
                    }

                    if(! cpertime('.' % srcd, rtrgt % srcd)) return out();
                }
            }
        }

        if(isdir(rtrgt % "/home") && ! cpertime("./home", rtrgt % "/home")) return out();

        {
            QSL incl{"_initrd.img_", "_initrd.img.old_", "_vmlinuz_", "_vmlinuz.old_"};

            for(cQStr &item : QDir("./").entryList(QDir::Files))
            {
                if(like(item, incl))
                {
                    QStr srci('/' % item), rsrci('.' % srci);

                    if(islink(rsrci))
                    {
                        QStr nrpi(rtrgt % srci);

                        for(cQStr &pname : rplst)
                        {
                            QStr orpi(rsdir % '/' % pname % srci);

                            if(stype(orpi) == Islink && lcomp(rsrci, orpi))
                            {
                                if(! crthlnk(orpi, nrpi)) return out();
                                goto nitem_2;
                            }

                            if(ThrdKill) return out();
                        }

                        if(! cplink(rsrci, nrpi)) return out();
                    }
                }

            nitem_2:
                if(ThrdKill) return out();
            }
        }

        for(cQStr &cdir : {"/cdrom", "/dev", "/mnt", "/proc", "/run", "/sys", "/tmp"})
        {
            QStr rcdir('.' % cdir);
            if(isdir(rcdir) && ! cpdir(rcdir, rtrgt % cdir)) return out();
            if(ThrdKill) return out();
        }

        QSL elst{"/etc/mtab", "/snap/.sblvtmp", "/var/.sblvtmp", "/var/cache/fontconfig/", "/var/lib/dpkg/lock", "/var/lib/udisks2/", "/var/lib/ureadahead/", "/var/log/", "/var/run/", "/var/tmp/"}, dlst{"/bin", "/boot", "/etc", "/lib", "/lib32", "/lib64", "/opt", "/sbin", "/selinux", "/srv", "/usr", "/var", "/snap"}, excl[]{{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*.dpkg-old_", "*~_", "*~/*"}, {"+_/var/cache/apt/*", "-*.bin_", "-*.bin.*"}, {"_/var/cache/apt/archives/*", "*.deb_"}};
        edetect(elst);

        for(uchar a(0) ; a < dlst.count() ; ++a)
        {
            cQStr &cdir(dlst.at(a)), rcdir('.' % cdir);

            if(isdir(rcdir))
            {
                if(! crtdir(rtrgt % cdir)) return out();

                if(! (cditmst = &sysitmst[a])->isEmpty())
                {
                    lcnt = 0;
                    QTS in(cditms = &sysitms[a], QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                        QStr item(in.readLine());

                        if(! like(item, excl[0]))
                        {
                            QStr srci(cdir % '/' % item), rsrci;

                            if(! (like(srci, excl[1], Mixed) || like(srci, excl[2], All) || exclcheck(elst, srci)) && exist(rsrci = '.' % srci))
                            {
                                QStr nrpi(rtrgt % srci);

                                switch(cditmst->at(lcnt)) {
                                case Islink:
                                    for(cQStr &pname : rplst)
                                    {
                                        QStr orpi(rsdir % '/' % pname % srci);

                                        if(stype(orpi) == Islink && lcomp(rsrci, orpi))
                                        {
                                            if(! crthlnk(orpi, nrpi)) return out();
                                            goto nitem_3;
                                        }

                                        if(ThrdKill) return out();
                                    }

                                    if(! cplink(rsrci, nrpi)) return out();
                                    break;
                                case Isdir:
                                    if(! crtdir(nrpi)) return out();
                                    break;
                                case Isfile:
                                    for(cQStr &pname : rplst)
                                    {
                                        QStr orpi(rsdir % '/' % pname % srci);

                                        if(stype(orpi) == Isfile && fcomp(rsrci, orpi) == 2)
                                        {
                                            if(! crthlnk(orpi, nrpi)) return out();
                                            goto nitem_3;
                                        }

                                        if(ThrdKill) return out();
                                    }

                                    if(! cpfile(rsrci, nrpi)) return out();
                                }
                            }
                        }

                    nitem_3:
                        if(ThrdKill) return out();
                        ++lcnt;
                    }

                    in.seek(0), lcnt = 0;

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine());

                        if(cditmst->at(lcnt++) == Isdir)
                        {
                            QStr srci(cdir % '/' % item), nrpi(rtrgt % srci);
                            if(exist(nrpi) && ! cpertime('.' % srci, nrpi)) return out();
                        }

                        if(ThrdKill) return out();
                    }

                    cditms->clear(), cditmst->clear();
                }

                if(! cpertime(rcdir, rtrgt % cdir)) return out();
            }
        }
    }

    if(isdir("./media"))
    {
        if(! crtdir(rtrgt % "/media")) return out();

        if(isfile("./etc/fstab"))
        {
            QSL dlst(QDir("./media").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot));
            QFile file("./etc/fstab");
            if(! fopen(file)) return out();

            for(uchar a(0) ; a < dlst.count() ; ++a)
            {
                cQStr &item(dlst.at(a));
                if(a && ! fopen(file)) return out();
                QSL incl{"* /media/" % item % " *", "* /media/" % item % "/*"};

                while(! file.atEnd())
                {
                    QStr cline(file.readLine().trimmed()), fdir;

                    if(! cline.startsWith('#') && like(cline.replace('\t', ' ').contains("\\040") ? QStr(cline).replace("\\040", " ") : cline, incl))
                        for(cQStr cdname : mid(cline, instr(cline, "/media/") + 7, instr(cline, " ", instr(cline, "/media/")) - instr(cline, "/media/") - 7).split('/'))
                            if(! cdname.isEmpty())
                            {
                                QStr nrpi(rtrgt % "/media" % fdir.append('/' % (cdname.contains("\\040") ? QStr(cdname).replace("\\040", " ") : cdname)));
                                if(! (isdir(nrpi) || cpdir("./media" % fdir, nrpi))) return out();
                            }

                    if(ThrdKill) return out();
                }

                file.close();
            }
        }

        if(! cpertime("./media", rtrgt % "/media")) return out();
    }

    if(isdir("./var/log"))
    {
        QBA logitms;
        QUCL logitmst;
        logitms.reserve(20000), logitmst.reserve(1000);
        if(! rodir(logitms, logitmst, "./var/log")) return out();

        if(! logitmst.isEmpty())
        {
            QSL excl{"*.gz_", "*.old_"};
            lcnt = 0;
            QTS in(&logitms, QIODevice::ReadOnly);

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                switch(logitmst.at(lcnt++)) {
                case Isdir:
                    if(! crtdir(rtrgt % "/var/log/" % item)) return out();
                    break;
                case Isfile:
                    if(! (like(item, excl) || (item.contains('.') && isnum(right(item, -rinstr(item, "."))))))
                    {
                        QStr srci("/var/log/" % item), nrpi(rtrgt % srci);
                        crtfile(nrpi);
                        if(! cpertime('.' % srci, nrpi)) return out();
                    }
                }

                if(ThrdKill) return out();
            }

            in.seek(0), lcnt = 0;

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                if(logitmst.at(lcnt++) == Isdir)
                {
                    QStr srci("/var/log/" % item), nrpi(rtrgt % srci);
                    if(exist(nrpi) && ! cpertime('.' % srci, nrpi)) return out();
                }

                if(ThrdKill) return out();
            }
        }

        if(! (cpertime("./var/log", rtrgt % "/var/log") && cpertime("./var", rtrgt % "/var"))) return out();
    }

    return out(true);
}

bool sb::thrdsrestore(uchar mthd, cQStr &usr, cQStr &srcdir, cQStr &trgt, bool sfstab)
{
    QSL usrs, ilst;
    QBAL homeitms;
    QUCLL homeitmst;
    uint anum(0);

    if(mthd != 2)
    {
        if(! like(mthd, {4, 6}))
        {
            if(isdir(srcdir % "/home"))
                for(cQStr &cusr : QDir(srcdir % "/home").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot)) usrs.append(cusr),
                                                                                                                          homeitms.append(nullptr),
                                                                                                                          homeitmst.append(QUCL());

            usrs.append(isdir(srcdir % "/root") ? "" : nullptr);
        }
        else if(usr == "root")
            usrs.append(isdir(srcdir % "/root") ? "" : nullptr);
        else if(isdir(srcdir % "/home/" % usr))
            usrs = QSL{usr, nullptr}, homeitms.append(nullptr), homeitmst.append(QUCL());
        else
            usrs.append(nullptr);

        if(isfile(srcdir % incfile))
        {
            QFile file(srcdir % incfile);
            if(! fopen(file)) return false;

            while(! file.atEnd())
            {
                QStr cline(left(file.readLine(), -1));
                if(! cline.isEmpty()) ilst.append(cline);
                if(ThrdKill) return false;
            }
        }

        if(! usrs.last().isNull())
        {
            homeitms.append(nullptr), homeitms.last().reserve(50000),
            homeitmst.append(QUCL()), homeitmst.last().reserve(1000);
            if(! rodir(homeitms.last(), homeitmst.last(), srcdir % "/root", True, ilst)) return false;
        }

        for(schar a(usrs.count() - 2) ; a > -1 ; --a)
        {
            homeitms[a].reserve(5000000), homeitmst[a].reserve(100000);
            if(! rodir(homeitms[a], homeitmst[a], srcdir % "/home/" % usrs.at(a), True, ilst)) return false;
        }

        for(cQUCL &cucl : homeitmst) anum += cucl.count();
    }

    QBA *cditms;
    QUCL *cditmst;
    uint cnum(0), lcnt;
    uchar cperc;
    auto fspchk([&trgt] { return dfree(trgt.isEmpty() ? "/" : trgt) > 10485760; });

    if(mthd < 3)
    {
        {
            QBA sysitms[13];
            QUCL sysitmst[13];

            {
                QStr dirs[]{srcdir % "/bin", srcdir % "/boot", srcdir % "/etc", srcdir % "/lib", srcdir % "/lib32", srcdir % "/lib64", srcdir % "/opt", srcdir % "/sbin", srcdir % "/selinux", srcdir % "/snap", srcdir % "/srv", srcdir % "/usr", srcdir % "/var"};
                uint bbs[]{10000, 20000, 100000, 500000, 10000, 10000, 100000, 10000, 10000, 10000, 10000, 10000000, 1000000}, ibs[]{500, 1000, 10000, 20000, 500, 500, 5000, 1000, 500, 500, 500, 500000, 50000};

                for(uchar a(0) ; a < 13 ; ++a)
                    if(isdir(dirs[a]))
                    {
                        sysitms[a].reserve(bbs[a]), sysitmst[a].reserve(ibs[a]);
                        if(! rodir(sysitms[a], sysitmst[a], dirs[a])) return false;
                    }
            }

            for(uchar a(0) ; a < 12 ; ++a) anum += sysitmst[a].count();
            Progress = 0;

            {
                QSL incl{"_initrd.img_", "_initrd.img.old_", "_vmlinuz_", "_vmlinuz.old_"};

                for(cQStr &item : QDir(trgt.isEmpty() ? "/" : trgt).entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files | QDir::System))
                {
                    if(like(item, incl))
                    {
                        QStr trgi(trgt % '/' % item);

                        switch(stype(trgi)) {
                        case Islink:
                        {
                            QStr srci(srcdir % '/' % item);
                            if(exist(srci) && lcomp(srci, trgi)) break;
                        }
                        case Isfile:
                            rmfile(trgi);
                            break;
                        case Isdir:
                            recrmdir(trgi);
                        }
                    }

                    if(ThrdKill) return false;
                }

                for(cQStr &item : QDir(srcdir).entryList(QDir::Files | QDir::System))
                {
                    if(like(item, incl))
                    {
                        QStr trgi(trgt % '/' % item);
                        if(! exist(trgi)) cplink(srcdir % '/' % item, trgi);
                    }

                    if(ThrdKill) return false;
                }
            }

            for(cQStr &cdir : {"/cdrom", "/dev", "/home", "/mnt", "/root", "/proc", "/run", "/sys", "/tmp"})
            {
                QStr srci(srcdir % cdir), trgi(trgt % cdir);

                if(! isdir(srci))
                {
                    if(exist(trgi)) stype(trgi) == Isdir ? recrmdir(trgi) : rmfile(trgi);
                }
                else if(isdir(trgi))
                    cpertime(srci, trgi);
                else
                {
                    if(exist(trgi)) rmfile(trgi);
                    if(! (cpdir(srci, trgi) || fspchk())) return false;
                }

                if(ThrdKill) return false;
            }

            QSL elst;
            if(trgt.isEmpty()) elst = QSL{"/etc/mtab", "/var/cache/fontconfig/", "/var/lib/dpkg/lock", "/var/lib/udisks2/", "/var/run/", "/var/tmp/"};
            if(trgt.isEmpty() || (isfile("/mnt/etc/xdg/autostart/sbschedule.desktop") && isfile("/mnt/etc/xdg/autostart/sbschedule-kde.desktop") && isfile("/mnt/usr/bin/systemback") && isfile("/mnt/usr/lib/systemback/libsystemback.so.1.0.0") && isfile("/mnt/usr/lib/systemback/sbscheduler") && isfile("/mnt/usr/lib/systemback/sbsustart") && isfile("/mnt/usr/lib/systemback/sbsysupgrade")&& isdir("/mnt/usr/share/systemback/lang") && isfile("/mnt/usr/share/systemback/efi.tar.gz") && isfile("/mnt/usr/share/systemback/splash.png") && isfile("/mnt/var/lib/dpkg/info/systemback.list") && isfile("/mnt/var/lib/dpkg/info/systemback.md5sums"))) elst.append({"/etc/systemback", "/etc/xdg/autostart/sbschedule*", "/usr/bin/systemback*", "/usr/lib/systemback/", "/usr/share/systemback/", "/var/lib/dpkg/info/systemback*"});
            if(sfstab) elst.append("/etc/fstab");
            edetect(elst);
            QSL dlst{"/bin", "/boot", "/etc", "/lib", "/lib32", "/lib64", "/opt", "/sbin", "/selinux", "/snap", "/srv", "/usr", "/var"}, excl[]{{"_lost+found_", "_Systemback_"}, {"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*"}};
            QStr mnts;

            auto mchk([&](cQStr &dir) {
                    if(mcheck(dir % '/', mnts.isEmpty() ? mnts = fload("/proc/self/mounts", true) : mnts))
                    {
                        cQStr &mpt(dir.contains(' ') ? bstr(dir).rplc(" ", "\\040") : dir);
                        QTS in(&mnts, QIODevice::ReadOnly);

                        while(! in.atEnd())
                        {
                            QStr cline(in.readLine());
                            if(like(cline, {"* " % mpt % " *", "* " % mpt % "/*"})) umnt(cline.split(' ').value(1).replace("\\040", " "));
                        }
                    }
                });

            for(uchar a(0) ; a < dlst.count() ; ++a)
            {
                cQStr &cdir(dlst.at(a));
                QStr srcd(srcdir % cdir), trgd(trgt % cdir);

                if(isdir(srcd))
                {
                    if(isdir(trgd))
                    {
                        QBAL sdlst;
                        if(! odir(sdlst, trgd)) return false;

                        for(cQStr &item : sdlst)
                        {
                            if(! (like(item, excl[0]) || exclcheck(elst, cdir % '/' % item) || exist(srcd % '/' % item)))
                            {
                                QStr trgi(trgd % '/' % item);
                                if(a == 9) mchk(trgi);
                                stype(trgi) == Isdir ? recrmdir(trgi) : rmfile(trgi);
                            }

                            if(ThrdKill) return false;
                        }
                    }
                    else
                    {
                        if(exist(trgd)) rmfile(trgd);
                        if(! (crtdir(trgd) || fspchk())) return false;
                    }

                    if(! (cditmst = &sysitmst[a])->isEmpty())
                    {
                        lcnt = 0;
                        QTS in(cditms = &sysitms[a], QIODevice::ReadOnly);

                        while(! in.atEnd())
                        {
                            if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                            QStr item(in.readLine());

                            if(! (like(item, excl[1]) || exclcheck(elst, cdir % '/' % item)))
                            {
                                QStr srci(srcd % '/' % item), trgi(trgd % '/' % item);

                                switch(cditmst->at(lcnt)) {
                                case Islink:
                                    switch(stype(trgi)) {
                                    case Islink:
                                        if(lcomp(srci, trgi)) goto nitem_1;
                                    case Isfile:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        if(a == 9) mchk(trgi);
                                        recrmdir(trgi);
                                    }

                                    if(! (cplink(srci, trgi) || fspchk())) return false;
                                    break;
                                case Isdir:
                                    switch(stype(trgi)) {
                                    case Isdir:
                                    {
                                        if(a == 9) mchk(trgi);
                                        QBAL sdlst;
                                        if(! odir(sdlst, trgi)) return false;

                                        for(cQStr &sitem : sdlst)
                                        {
                                            if(! (like(sitem, excl[0]) || exclcheck(elst, cdir % '/' % item % '/' % sitem) || exist(srci % '/' % sitem)))
                                            {
                                                QStr strgi(trgi % '/' % sitem);
                                                if(a == 9) mchk(strgi);
                                                stype(strgi) == Isdir ? recrmdir(strgi) : rmfile(strgi);
                                            }

                                            if(ThrdKill) return false;
                                        }

                                        goto nitem_1;
                                    }
                                    case Islink:
                                    case Isfile:
                                        rmfile(trgi);
                                    }

                                    if(! (crtdir(trgi) || fspchk())) return false;
                                    break;
                                case Isfile:
                                    switch(stype(trgi)) {
                                    case Isfile:
                                        switch(fcomp(srci, trgi)) {
                                        case 1:
                                            cpertime(srci, trgi);
                                        case 2:
                                            goto nitem_1;
                                        }
                                    case Islink:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        if(a == 9) mchk(trgi);
                                        recrmdir(trgi);
                                    }

                                    if(! (cpfile(srci, trgi) || fspchk())) return false;
                                }
                            }

                        nitem_1:
                            if(ThrdKill) return false;
                            ++lcnt;
                        }

                        in.seek(0), lcnt = 0;

                        while(! in.atEnd())
                        {
                            QStr item(in.readLine());

                            if(cditmst->at(lcnt++) == Isdir)
                            {
                                QStr trgi(trgd % '/' % item);
                                if(exist(trgi)) cpertime(srcd % '/' % item, trgi);
                            }

                            if(ThrdKill) return false;
                        }

                        cditms->clear(), cditmst->clear();
                    }

                    cpertime(srcd, trgd);
                }
                else if(exist(trgd))
                {
                    if(a == 9) mchk(trgd);
                    stype(trgd) == Isdir ? recrmdir(trgd) : rmfile(trgd);
                }
            }
        }

        {
            QStr srcd(srcdir % "/media"), trgd(trgt % "/media");

            if(isdir(srcd))
            {
                if(isdir(trgd))
                    for(cQStr &item : QDir(trgd).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
                    {
                        if(! exist(srcd % '/' % item))
                        {
                            QStr trgi(trgd % '/' % item);
                            if(! mcheck(trgi % '/')) recrmdir(trgi);
                        }

                        if(ThrdKill) return false;
                    }
                else
                {
                    if(exist(trgd)) rmfile(trgd);
                    if(! (crtdir(trgd) || fspchk())) return false;
                }

                QBA mediaitms;
                mediaitms.reserve(10000);
                if(! rodir(mediaitms, srcd)) return false;

                if(! mediaitms.isEmpty())
                {
                    QTS in(&mediaitms, QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine()), trgi(trgd % '/' % item);

                        if(exist(trgi))
                        {
                            if(stype(trgi) != Isdir)
                            {
                                rmfile(trgi);
                                if(! (crtdir(trgi) || fspchk())) return false;
                            }

                            cpertime(srcd % '/' % item, trgi);
                        }
                        else if(! (cpdir(srcd % '/' % item, trgi) || fspchk()))
                            return false;

                        if(ThrdKill) return false;
                    }
                }

                cpertime(srcd, trgd);
            }
            else if(exist(trgd))
                stype(trgd) == Isdir ? recrmdir(trgd) : rmfile(trgd);
        }

        if(srcdir == "/.systembacklivepoint" && isdir("/.systembacklivepoint/.systemback"))
        {
            QBA sbitms;
            QUCL sbitmst;
            sbitms.reserve(10000), sbitmst.reserve(500);
            if(! rodir(sbitms, sbitmst, "/.systembacklivepoint/.systemback")) return false;
            lcnt = 0;
            QTS in(&sbitms, QIODevice::ReadOnly);

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                switch(sbitmst.at(lcnt++)) {
                case Islink:
                    if(! ((sfstab && item == "etc/fstab") || cplink("/.systembacklivepoint/.systemback/" % item, trgt % '/' % item) || fspchk())) return false;
                    break;
                case Isfile:
                    if(! ((sfstab && item == "etc/fstab") || cpfile("/.systembacklivepoint/.systemback/" % item, trgt % '/' % item) || fspchk())) return false;
                    break;
                }

                if(ThrdKill) return false;
            }

            in.seek(0), lcnt = 0;

            while(! in.atEnd())
            {
                QStr item(in.readLine());
                if(sbitmst.at(lcnt++) == Isdir) cpertime("/.systembacklivepoint/.systemback/" % item, trgt % '/' % item);
                if(ThrdKill) return false;
            }
        }
    }
    else
        Progress = 0;

    if(mthd != 2)
    {
        QSL elst{".cache/gvfs", ".gvfs", ".local/share/Trash/files/", ".local/share/Trash/info/", ".Xauthority", ".ICEauthority"};

        {
            QFile file(srcdir % excfile);
            if(! fopen(file)) return false;

            while(! file.atEnd())
            {
                QStr cline(left(file.readLine(), -1));
                if(like(cline, {"_.*", "_snap_", "_snap/*"})) elst.append(cline);
                if(ThrdKill) return false;
            }
        }

        bool skppd;
        QSL excl[]{{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*~_", "*~/*"}, {"_lost+found_", "_Systemback_", "*~_"}};

        for(schar a(usrs.count() - 1) ; a > -1 ; --a)
        {
            cQStr &cusr(usrs.at(a));

            if(! cusr.isNull())
            {
                QStr srcd(srcdir), trgd(trgt);

                if(cusr.isEmpty())
                    for(QStr *dir : {&srcd, &trgd}) dir->append("/root");
                else
                {
                    QStr hdir("/home/" % cusr);
                    for(QStr *dir : {&srcd, &trgd}) dir->append(hdir);
                }

                {
                    if(! isdir(trgd))
                    {
                        if(exist(trgd)) rmfile(trgd);
                        if(! (crtdir(trgd) || fspchk())) return false;
                    }
                    else if(! like(mthd, {3, 4}))
                    {
                        QBAL sdlst;
                        if(! odir(sdlst, trgd, True, ilst)) return false;

                        for(cQStr &item : sdlst)
                        {
                            bool inc(! (ilst.isEmpty() || like(item, {"_.*", "_snap_"})));

                            if((inc || ! (item.endsWith('~') || exclcheck(elst, item))) && ! exist(srcd % '/' % item))
                            {
                                QStr trgi(trgd % '/' % item);

                                switch(stype(trgi)) {
                                case Isdir:
                                    recrmdir(trgi, true);
                                    break;
                                case Isfile:
                                    if(! inc && fsize(trgi) > 8000000) break;
                                case Islink:
                                    rmfile(trgi);
                                }
                            }

                            if(ThrdKill) return false;
                        }
                    }
                }

                if(! (cditmst = &homeitmst[a])->isEmpty() && anum)
                {
                    lcnt = 0;
                    QTS in(cditms = &homeitms[a], QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                        QStr item(in.readLine());
                        bool inc;

                        if(! like(item, excl[0]) && ((inc = ! (ilst.isEmpty() || like(item, {"_.*", "_snap_", "_snap/*"}))) || ! exclcheck(elst, item)))
                        {
                            QStr srci(srcd % '/' % item), trgi(trgd % '/' % item);

                            switch(cditmst->at(lcnt)) {
                            case Islink:
                                switch(stype(trgi)) {
                                case Islink:
                                    if(lcomp(srci, trgi)) goto nitem_2;
                                case Isfile:
                                    rmfile(trgi);
                                    break;
                                case Isdir:
                                    recrmdir(trgi);
                                }

                                if(! (cplink(srci, trgi) || fspchk())) return false;
                                break;
                            case Isdir:
                                switch(stype(trgi)) {
                                case Isdir:
                                    if(! like(mthd, {3, 4}))
                                    {
                                        QBAL sdlst;
                                        if(! (inc ? odir(sdlst, trgi, Include, ilst, item) : odir(sdlst, trgi))) return false;

                                        for(cQStr &sitem : sdlst)
                                        {
                                            if(! (like(sitem, excl[1]) || exclcheck(elst, item % '/' % sitem) || exist(srci % '/' % sitem)))
                                            {
                                                QStr strgi(trgi % '/' % sitem);

                                                switch(stype(strgi)) {
                                                case Isdir:
                                                    recrmdir(strgi, true);
                                                    break;
                                                case Isfile:
                                                    if(! inc && fsize(strgi) > 8000000) break;
                                                case Islink:
                                                    rmfile(strgi);
                                                }
                                            }

                                            if(ThrdKill) return false;
                                        }
                                    }

                                    goto nitem_2;
                                case Islink:
                                case Isfile:
                                    rmfile(trgi);
                                }

                                if(! (crtdir(trgi) || fspchk())) return false;
                                break;
                            case Isfile:
                                skppd = ! inc && fsize(srci) > 8000000;

                                switch(stype(trgi)) {
                                case Isfile:
                                    switch(fcomp(trgi, srci)) {
                                    case 1:
                                        cpertime(srci, trgi);
                                    case 2:
                                        goto nitem_2;
                                    }

                                    if(skppd) goto nitem_2;
                                case Islink:
                                    rmfile(trgi);
                                    break;
                                case Isdir:
                                    recrmdir(trgi);
                                }

                                if(! (skppd || cpfile(srci, trgi) || fspchk())) return false;
                            }
                        }

                    nitem_2:
                        if(ThrdKill) return false;
                        ++lcnt;
                    }

                    in.seek(0), lcnt = 0;

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine());

                        if(cditmst->at(lcnt++) == Isdir)
                        {
                            QStr trgi(trgd % '/' % item);
                            if(exist(trgi)) cpertime(srcd % '/' % item, trgi);
                        }

                        if(ThrdKill) return false;
                    }

                    cditms->clear(), cditmst->clear();
                }

                cpertime(srcd, trgd);
            }
        }
    }

    return true;
}

bool sb::thrdscopy(uchar mthd, cQStr &usr, cQStr &srcdir)
{
    uint lcnt;

    {
        QBA sysitms[13];
        QUCL sysitmst[13];

        {
            QStr dirs[]{srcdir % "/bin", srcdir % "/boot", srcdir % "/etc", srcdir % "/lib", srcdir % "/lib32", srcdir % "/lib64", srcdir % "/opt", srcdir % "/sbin", srcdir % "/selinux", srcdir % "/srv", srcdir % "/usr", srcdir % "/var"};
            uint bbs[]{10000, 20000, 100000, 500000, 10000, 10000, 100000, 10000, 10000, 10000, 10000000, 1000000}, ibs[]{500, 1000, 10000, 20000, 500, 500, 5000, 1000, 500, 500, 500000, 50000};

            for(uchar a(0) ; a < 12 ; ++a)
                if(isdir(dirs[a]))
                {
                    sysitms[a].reserve(bbs[a]), sysitmst[a].reserve(ibs[a]);
                    if(! rodir(sysitms[a], sysitmst[a], dirs[a])) return false;
                }
        }

        if(isdir(srcdir % "/snap"))
        {
            sysitms[12].reserve(10000), sysitmst[12].reserve(500);
            if(! rodir("./snap", sysitms[12], sysitmst[12])) return false;
        }

        uint anum(0), cnum(0);
        for(uchar a(0) ; a < 12 ; ++a) anum += sysitmst[a].count();
        QStr macid;
        QBA *cditms;
        QUCL *cditmst;
        uchar cperc;

        {
            QSL usrs;
            QBAL homeitms;
            QUCLL homeitmst;

            if(mthd > 2)
            {
                if(isdir(srcdir % "/home/" % usr)) usrs.append(usr);
                homeitms.append(nullptr), homeitmst.append(QUCL());
            }
            else if(mthd && isdir(srcdir % "/home"))
            {
                if(srcdir.isEmpty())
                {
                    QFile file("/etc/passwd");
                    if(! fopen(file)) return false;

                    while(! file.atEnd())
                    {
                        QStr cusr(file.readLine().trimmed());

                        if(cusr.contains(":/home/") && isdir("/home/" % (cusr = left(cusr, instr(cusr, ":") -1)))) usrs.append(cusr),
                                                                                                                   homeitms.append(nullptr),
                                                                                                                   homeitmst.append(QUCL());
                    }
                }
                else
                    for(cQStr &cusr : QDir(srcdir % "/home").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot)) usrs.append(cusr),
                                                                                                                              homeitms.append(nullptr),
                                                                                                                              homeitmst.append(QUCL());

                if(ThrdKill) return false;
            }

            usrs.append(isdir(srcdir % "/root") ? "" : nullptr);

            if(mthd == 5)
            {
                homeitms[0].reserve(10000), homeitmst[0].reserve(500);
                if(! rodir(homeitms[0], homeitmst[0], srcdir % "/etc/skel")) return false;
            }
            else
            {
                if(! usrs.last().isNull())
                {
                    homeitms.append(nullptr), homeitms.last().reserve(50000),
                    homeitmst.append(QUCL()), homeitmst.last().reserve(1000);
                    if(! rodir(homeitms.last(), homeitmst.last(), srcdir % "/root", like(mthd, {2, 3}) ? True : False)) return false;
                }

                for(schar a(usrs.count() - 2) ; a > -1 ; --a)
                {
                    homeitms[a].reserve(5000000), homeitmst[a].reserve(100000);
                    if(! rodir(homeitms[a], homeitmst[a], srcdir % "/home/" % usrs.at(a), like(mthd, {2, 3}) ? True : False)) return false;
                }
            }

            for(cQUCL &cucl : homeitmst) anum += cucl.count();
            Progress = 0;

            if(isdir(srcdir % "/home") && ! (isdir("/.sbsystemcopy/home") || crtdir("/.sbsystemcopy/home")))
            {
                QFile::rename("/.sbsystemcopy/home", "/.sbsystemcopy/home_" % rndstr());
                if(! crtdir("/.sbsystemcopy/home")) return false;
            }

            if(mthd > 2)
            {
                QStr mfile(isfile(srcdir % "/var/lib/dbus/machine-id") ? "/var/lib/dbus/machine-id" : isfile(srcdir % "/etc/machine-id") ? "/etc/machine-id" : nullptr);

                if(! mfile.isEmpty())
                {
                    QFile file(srcdir % mfile);
                    if(! fopen(file)) return false;
                    QStr cline(file.readLine().trimmed());
                    if(cline.length() == 32) macid = cline;
                 }
            }

            QSL elst{".cache/gvfs", ".gvfs", ".local/share/Trash/files/", ".local/share/Trash/info/", ".Xauthority", ".ICEauthority"};

            {
                QFile file(srcdir % excfile);
                if(! fopen(file)) return false;

                while(! file.atEnd())
                {
                    QStr cline(left(file.readLine(), -1));
                    if(like(cline, {"_.*", "_snap_", "_snap/*"})) elst.append(cline);
                    if(ThrdKill) return false;
                }
            }

            bool skppd;
            QSL excl{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*~_", "*~/*"};

            for(uchar a(0) ; a < usrs.count() ; ++a)
            {
                cQStr &cusr(usrs.at(a));

                if(! cusr.isNull())
                {
                    QStr srcd[2], trgd;
                    cusr.isEmpty() ? (srcd[0] = srcdir % "/root", trgd = "/.sbsystemcopy/root") : (srcd[0] = srcdir % "/home/" % cusr, trgd = "/.sbsystemcopy/home/" % cusr);
                    srcd[1] = mthd == 5 ? srcdir % "/etc/skel" : srcd[0];

                    if(! isdir(trgd))
                    {
                        if(exist(trgd)) rmfile(trgd);
                        if(! cpdir(srcd[0], trgd)) return false;
                    }

                    if(! (cditmst = &homeitmst[mthd == 5 ? 0 : a])->isEmpty())
                    {
                        lcnt = 0;
                        QTS in(cditms = &homeitms[mthd == 5 ? 0 : a], QIODevice::ReadOnly);

                        while(! in.atEnd())
                        {
                            if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                            QStr item(in.readLine());

                            if((mthd == 5 || (! (like(item, excl) || exclcheck(elst, item)) && (macid.isEmpty() || ! item.contains(macid)))) && (! srcdir.isEmpty() || exist(srcd[1] % '/' % item)))
                            {
                                QStr trgi(trgd % '/' % item);

                                switch(cditmst->at(lcnt)) {
                                case Islink:
                                {
                                    QStr srci(srcd[1] % '/' % item);

                                    switch(stype(trgi)) {
                                    case Islink:
                                        if(! lcomp(srci, trgi)) goto nitem_1;
                                    case Isfile:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        recrmdir(trgi);
                                    }

                                    if(! cplink(srci, trgi)) return false;
                                    break;
                                }
                                case Isdir:
                                    switch(stype(trgi)) {
                                    case Isdir:
                                        goto nitem_1;
                                    case Islink:
                                    case Isfile:
                                        rmfile(trgi);
                                    }

                                    if(! crtdir(trgi)) return false;
                                    break;
                                case Isfile:
                                    QStr srci(srcd[1] % '/' % item);
                                    skppd = like(mthd, {2, 3}) && fsize(srci) > 8000000;

                                    switch(stype(trgi)) {
                                    case Isfile:
                                        if(mthd == 5)
                                            switch(fcomp(trgi, srci)) {
                                            case 1:
                                                if(! cpertime(srci, trgi, ! cusr.isEmpty())) return false;
                                            case 2:
                                                goto nitem_1;
                                            }
                                        else
                                        {
                                            switch(fcomp(trgi, srci)) {
                                            case 1:
                                                if(! cpertime(srci, trgi)) return false;
                                            case 2:
                                                goto nitem_1;
                                            }

                                            if(skppd) goto nitem_1;
                                        }
                                    case Islink:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        recrmdir(trgi);
                                    }

                                    if(mthd == 5)
                                    {
                                        if(! cpfile(srci, trgi, ! cusr.isEmpty())) return false;
                                    }
                                    else if(! (skppd || cpfile(srci, trgi)))
                                        return false;
                                }
                            }

                        nitem_1:
                            if(ThrdKill) return false;
                            ++lcnt;
                        }

                        in.seek(0), lcnt = 0;

                        while(! in.atEnd())
                        {
                            QStr item(in.readLine());

                            if(cditmst->at(lcnt++) == Isdir)
                            {
                                QStr trgi(trgd % '/' % item);
                                if(exist(trgi) && ! cpertime(srcd[1] % '/' % item, trgi, mthd == 5 && ! cusr.isEmpty())) return false;
                            }

                            if(ThrdKill) return false;
                        }

                        if(mthd < 5) cditms->clear(), cditmst->clear();
                    }

                    if(! cusr.isEmpty())
                    {
                        if(isfile(srcd[0] % "/.config/user-dirs.dirs"))
                        {
                            QFile file(srcd[0] % "/.config/user-dirs.dirs");
                            if(! fopen(file)) return false;

                            while(! file.atEnd())
                            {
                                QStr cline(file.readLine().trimmed()), dir;

                                if(! cline.startsWith('#') && cline.contains("$HOME") && (dir = left(right(cline, -instr(cline, "/")), -1)).length())
                                {
                                    QStr trgi(trgd % '/' % dir);

                                    if(! isdir(trgi))
                                    {
                                        QStr srci(srcd[0] % '/' % dir);

                                        if(isdir(srci))
                                        {
                                            if(! cpdir(srci, trgi)) return false;
                                        }
                                        else if(srcdir.startsWith(sdir[1]) && ! (crtdir(trgi) && cpertime(trgd, trgi)))
                                            return false;
                                    }
                                }

                                if(ThrdKill) return false;
                            }
                        }

                        if(! cpertime(srcd[0], trgd)) return false;
                    }
                }
            }
        }

        {
            QSL incl{"_initrd.img_", "_initrd.img.old_", "_vmlinuz_", "_vmlinuz.old_"};

            for(cQStr &item : QDir("/.sbsystemcopy").entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files | QDir::System))
            {
                if(like(item, incl))
                {
                    QStr trgi("/.sbsystemcopy/" % item);

                    switch(stype(trgi)) {
                    case Islink:
                    {
                        QStr srci(srcdir % '/' % item);
                        if(islink(srci) && lcomp(srci, trgi)) break;
                    }
                    case Isfile:
                        if(! rmfile(trgi)) return false;
                        break;
                    case Isdir:
                        if(! recrmdir(trgi)) return false;
                    }
                }

                if(ThrdKill) return false;
            }

            for(cQStr &item : QDir(srcdir.isEmpty() ? "/" : srcdir).entryList(QDir::Files | QDir::System))
            {
                if(like(item, incl))
                {
                    QStr srci(srcdir % '/' % item);

                    if(islink(srci))
                    {
                        QStr trgi("/.sbsystemcopy/" % item);
                        if(! (exist(trgi) || cplink(srci, trgi))) return false;
                    }
                }

                if(ThrdKill) return false;
            }
        }

        for(cQStr &cdir : {"/cdrom", "/dev", "/home", "/mnt", "/root", "/proc", "/run", "/sys", "/tmp"})
        {
            QStr trgd("/.sbsystemcopy" % cdir);

            if(! isdir(srcdir % cdir))
            {
                if(exist(trgd)) stype(trgd) == Isdir ? recrmdir(trgd) : rmfile(trgd);
            }
            else if(! isdir(trgd))
            {
                if(exist(trgd)) rmfile(trgd);
                if(! cpdir(srcdir % cdir, trgd)) return false;
            }
            else if(! cpertime(srcdir % cdir, trgd))
                return false;

            if(ThrdKill) return false;
        }

        QSL elst{"/boot/efi", "/etc/crypttab", "/etc/mtab", "/snap/.sblvtmp", "/var/.sblvtmp", "/var/cache/fontconfig/", "/var/lib/dpkg/lock", "/var/lib/udisks2/", "/var/log", "/var/run/", "/var/tmp/"};
        if(mthd > 2) elst.append({"/etc/machine-id", "/var/lib/dbus/machine-id"});

        if(srcdir.isEmpty())
            elst.append(cfgfile);
        else
        {
            elst.append("/etc/systemback/");
            if(srcdir == "/.systembacklivepoint" && fload("/proc/cmdline").contains("noxconf")) elst.append({"/etc/X11/xorg.conf", "/etc/X11/xorg.conf.d/"});
        }

        edetect(elst);

        for(cQStr &cdir : {"/etc/rc0.d", "/etc/rc1.d", "/etc/rc2.d", "/etc/rc3.d", "/etc/rc4.d", "/etc/rc5.d", "/etc/rc6.d", "/etc/rcS.d"})
        {
            QStr srcd(srcdir % cdir);

            if(isdir(srcd))
                for(cQStr &item : QDir(srcd).entryList(QDir::Files))
                    if(item.contains("cryptdisks")) elst.append(cdir % '/' % item);
        }

        QSL dlst{"/bin", "/boot", "/etc", "/lib", "/lib32", "/lib64", "/opt", "/sbin", "/selinux", "/srv", "/usr", "/var", "/snap"}, excl[]{{"_lost+found_", "_Systemback_"}, {"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*"}, {"_/etc/udev/rules.d*", "*-persistent-*"}};

        for(uchar a(0) ; a < dlst.count() ; ++a)
        {
            cQStr &cdir(dlst.at(a));
            QStr srcd(srcdir % cdir), trgd("/.sbsystemcopy" % cdir);

            if(isdir(srcd))
            {
                if(isdir(trgd))
                {
                    QBAL sdlst;
                    if(! odir(sdlst, trgd)) return false;

                    for(cQStr &item : sdlst)
                    {
                        bool exc;

                        if(! like(item, excl[0]) && ((exc = exclcheck(elst, cdir % '/' % item)) || ! exist(srcd % '/' % item)))
                        {
                            QStr trgi(trgd % '/' % item);

                            switch(stype(trgi)) {
                            case Isdir:
                                if(! exc) recrmdir(trgi);
                                break;
                            default:
                                rmfile(trgi);
                            }
                        }

                        if(ThrdKill) return false;
                    }
                }
                else
                {
                    if(exist(trgd)) rmfile(trgd);
                    if(! crtdir(trgd)) return false;
                }

                if(! (cditmst = &sysitmst[a])->isEmpty())
                {
                    lcnt = 0;
                    QTS in(cditms = &sysitms[a], QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        if(Progress < (cperc = (++cnum * 100 + 50) / anum)) Progress = cperc;
                        QStr item(in.readLine());

                        if(! like(item, excl[1]))
                        {
                            QStr pdi(cdir % '/' % item);

                            if(! exclcheck(elst, pdi) && (macid.isEmpty() || ! item.contains(macid)) && (mthd < 3 || ! like(pdi, excl[2], All)) && (! srcdir.isEmpty() || exist(pdi)))
                            {
                                QStr srci(srcd % '/' % item), trgi(trgd % '/' % item);

                                switch(cditmst->at(lcnt)) {
                                case Islink:
                                    switch(stype(trgi)) {
                                    case Islink:
                                        if(lcomp(srci, trgi)) goto nitem_2;
                                    case Isfile:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        recrmdir(trgi);
                                    }

                                    if(! cplink(srci, trgi)) return false;
                                    break;
                                case Isdir:
                                    switch(stype(trgi)) {
                                    case Isdir:
                                    {
                                        QBAL sdlst;
                                        if(! odir(sdlst, trgi)) return false;

                                        for(cQStr &sitem : sdlst)
                                        {
                                            bool exc;

                                            if(! like(sitem, excl[0]) && ((exc = exclcheck(elst, pdi % '/' % sitem)) || ! exist(srci % '/' % sitem)))
                                            {
                                                QStr strgi(trgi % '/' % sitem);

                                                switch(stype(strgi)) {
                                                case Isdir:
                                                    if(! exc) recrmdir(strgi);
                                                    break;
                                                default:
                                                    rmfile(strgi);
                                                }
                                            }

                                            if(ThrdKill) return false;
                                        }

                                        goto nitem_2;
                                    }
                                    case Islink:
                                    case Isfile:
                                        rmfile(trgi);
                                    }

                                    if(! crtdir(trgi)) return false;
                                    break;
                                case Isfile:
                                    switch(stype(trgi)) {
                                    case Isfile:
                                        switch(fcomp(trgi, srci)) {
                                        case 1:
                                            if(! cpertime(srci, trgi)) return false;
                                        case 2:
                                            goto nitem_2;
                                        }
                                    case Islink:
                                        rmfile(trgi);
                                        break;
                                    case Isdir:
                                        recrmdir(trgi);
                                    }

                                    if(! cpfile(srci, trgi)) return false;
                                }
                            }
                        }

                    nitem_2:
                        if(ThrdKill) return false;
                        ++lcnt;
                    }

                    in.seek(0), lcnt = 0;

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine());

                        if(cditmst->at(lcnt++) == Isdir)
                        {
                            QStr trgi(trgd % '/' % item);
                            if(exist(trgi) && ! cpertime(srcd % '/' % item, trgi)) return false;
                        }

                        if(ThrdKill) return false;
                    }

                    cditms->clear(), cditmst->clear();
                }

                if(! cpertime(srcd, trgd)) return false;
            }
            else if(exist(trgd))
                stype(trgd) == Isdir ? recrmdir(trgd) : rmfile(trgd);
        }
    }

    {
        QStr srcd(srcdir % "/media"), trgd("/.sbsystemcopy/media");

        if(isdir(srcd))
        {
            if(isdir(trgd))
                for(cQStr &item : QDir(trgd).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot))
                {
                    QStr trgi(trgd % '/' % item);
                    if(! (exist(srcdir % '/' % item) || mcheck(trgi % '/'))) recrmdir(trgi);
                    if(ThrdKill) return false;
                }
            else
            {
                if(exist(trgd)) rmfile(trgd);
                if(! crtdir(trgd)) return false;
            }

            if(! srcdir.isEmpty())
            {
                QBA mediaitms;
                mediaitms.reserve(10000);
                if(! rodir(mediaitms, srcd)) return false;

                if(! mediaitms.isEmpty())
                {
                    QTS in(&mediaitms, QIODevice::ReadOnly);

                    while(! in.atEnd())
                    {
                        QStr item(in.readLine()), trgi(trgd % '/' % item);

                        if(exist(trgi))
                        {
                            if(stype(trgi) != Isdir)
                            {
                                rmfile(trgi);
                                if(! crtdir(trgi)) return false;
                            }

                            if(! cpertime(srcdir % "/media/" % item, trgi)) return false;
                        }
                        else if(! cpdir(srcdir % "/media/" % item, trgi))
                            return false;

                        if(ThrdKill) return false;
                    }
                }
            }
            else if(isfile("/etc/fstab"))
            {
                QSL dlst(QDir(srcd).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot));
                QFile file("/etc/fstab");
                if(! fopen(file)) return false;

                for(uchar a(0) ; a < dlst.count() ; ++a)
                {
                    cQStr &item(dlst.at(a));
                    if(a && ! fopen(file)) return false;
                    QSL incl{"* /media/" % item % " *", "* /media/" % item % "/*"};

                    while(! file.atEnd())
                    {
                        QStr cline(file.readLine().trimmed()), fdir;

                        if(! cline.startsWith('#') && like(cline.replace('\t', ' ').contains("\\040") ? QStr(cline).replace("\\040", " ") : cline, incl))
                            for(cQStr cdname : mid(cline, instr(cline, "/media/") + 7, instr(cline, " ", instr(cline, "/media/")) - instr(cline, "/media/") - 7).split('/'))
                                if(! cdname.isEmpty())
                                {
                                    QStr trgi(trgd % fdir.append('/' % (cdname.contains("\\040") ? QStr(cdname).replace("\\040", " ") : cdname)));
                                    if(! (isdir(trgi) || cpdir("/media" % fdir, trgi))) return false;
                                }

                        if(ThrdKill) return false;
                    }

                    file.close();
                }
            }

            if(! cpertime(srcd, trgd)) return false;
        }
        else if(exist(trgd))
            stype(trgd) == Isdir ? recrmdir(trgd) : rmfile(trgd);
    }

    if(exist("/.sbsystemcopy/var/log")) stype("/.sbsystemcopy/var/log") == Isdir ? recrmdir("/.sbsystemcopy/var/log") : rmfile("/.sbsystemcopy/var/log");

    if(isdir(srcdir % "/var/log"))
    {
        if(! crtdir("/.sbsystemcopy/var/log")) return false;
        QBA logitms;
        QUCL logitmst;
        logitms.reserve(20000), logitmst.reserve(1000);
        if(! rodir(logitms, logitmst, srcdir % "/var/log")) return false;

        if(! logitmst.isEmpty())
        {
            QSL excl{"*.gz_", "*.old_"};
            lcnt = 0;
            QTS in(&logitms, QIODevice::ReadOnly);

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                switch(logitmst.at(lcnt++)) {
                case Isdir:
                    if(! crtdir("/.sbsystemcopy/var/log/" % item)) return false;
                    break;
                case Isfile:
                    if(! like(item, excl) && ! (item.contains('.') && isnum(right(item, -rinstr(item, ".")))))
                    {
                        QStr trgi("/.sbsystemcopy/var/log/" % item);
                        crtfile(trgi);
                        if(! cpertime(srcdir % "/var/log/" % item, trgi)) return false;
                    }
                }

                if(ThrdKill) return false;
            }

            in.seek(0), lcnt = 0;

            while(! in.atEnd())
            {
                QStr item(in.readLine());
                if((logitmst.at(lcnt++) == Isdir && ! cpertime(srcdir % "/var/log/" % item, "/.sbsystemcopy/var/log/" % item)) || ThrdKill) return false;
            }
        }

        if(! (cpertime(srcdir % "/var/log", "/.sbsystemcopy/var/log") && cpertime(srcdir % "/var", "/.sbsystemcopy/var"))) return false;
    }

    if(srcdir == "/.systembacklivepoint" && isdir("/.systembacklivepoint/.systemback"))
    {
        QBA sbitms;
        QUCL sbitmst;
        sbitms.reserve(10000), sbitmst.reserve(500);
        if(! rodir(sbitms, sbitmst, "/.systembacklivepoint/.systemback")) return false;
        lcnt = 0;
        QTS in(&sbitms, QIODevice::ReadOnly);

        while(! in.atEnd())
        {
            QStr item(in.readLine());

            switch(sbitmst.at(lcnt++)) {
            case Islink:
                if(! (item == "etc/fstab" || cplink("/.systembacklivepoint/.systemback/" % item, "/.sbsystemcopy/" % item))) return false;
                break;
            case Isfile:
                if(! (item == "etc/fstab" || cpfile("/.systembacklivepoint/.systemback/" % item, "/.sbsystemcopy/" % item))) return false;
            }

            if(ThrdKill) return false;
        }

        in.seek(0), lcnt = 0;

        while(! in.atEnd())
        {
            QStr item(in.readLine());
            if((sbitmst.at(lcnt++) == Isdir && ! cpertime("/.systembacklivepoint/.systemback/" % item, "/.sbsystemcopy/" % item)) || ThrdKill) return false;
        }
    }

    return true;
}

bool sb::thrdlvprpr(bool iudata)
{
    if(ThrdLng[0]) ThrdLng[0] = 0;

    {
        QUCL sitmst;
        sitmst.reserve(1000000);

        for(cQStr &item : {"/bin", "/boot", "/etc", "/lib", "/lib32", "/lib64", "/opt", "/sbin", "/selinux", "/usr", "/initrd.img", "/initrd.img.old", "/vmlinuz", "/vmlinuz.old"})
            if(isdir(item))
            {
                if(! rodir(sitmst, item)) return false;
                ++ThrdLng[0];
            }
            else if(exist(item))
                ++ThrdLng[0];

        ThrdLng[0] += sitmst.count();
    }

    if(! (crtdir(sdir[2] % "/.sblivesystemcreate/.systemback") && crtdir(sdir[2] % "/.sblivesystemcreate/.systemback/etc"))) return false;

    if(isdir("/etc/udev"))
    {
        if(! crtdir(sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev")) return false;

        if(isdir("/etc/udev/rules.d") && (! crtdir(sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev/rules.d") ||
            (isfile("/etc/udev/rules.d/70-persistent-cd.rules") && ! cpfile("/etc/udev/rules.d/70-persistent-cd.rules", sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev/rules.d/70-persistent-cd.rules")) ||
            (isfile("/etc/udev/rules.d/70-persistent-net.rules") && ! cpfile("/etc/udev/rules.d/70-persistent-net.rules", sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev/rules.d/70-persistent-net.rules")) ||
            ! cpertime("/etc/udev/rules.d", sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev/rules.d"))) return false;

        if(! cpertime("/etc/udev", sdir[2] % "/.sblivesystemcreate/.systemback/etc/udev")) return false;
    }

    if((isfile("/etc/fstab") && ! cpfile("/etc/fstab", sdir[2] % "/.sblivesystemcreate/.systemback/etc/fstab")) || ! cpertime("/etc", sdir[2] % "/.sblivesystemcreate/.systemback/etc")) return false;
    if(exist("/.sblvtmp")) stype("/.sblvtmp") == Isdir ? recrmdir("/.sblvtmp") : rmfile("/.sblvtmp");
    if(! crtdir("/.sblvtmp")) return false;

    for(cQStr &cdir : {"/cdrom", "/dev", "/mnt", "/proc", "/run", "/srv", "/sys", "/tmp"})
        if(isdir(cdir))
        {
            if(! cpdir(cdir, "/.sblvtmp" % cdir)) return false;
            ++ThrdLng[0];
        }

    if(ThrdKill) return false;

    if(! isdir("/media"))
    {
        if(! crtdir("/media")) return false;
    }
    else if(exist("/media/.sblvtmp"))
       stype("/media/.sblvtmp") == Isdir ? recrmdir("/media/.sblvtmp") : rmfile("/media/.sblvtmp");

    if(! (crtdir("/media/.sblvtmp") && crtdir("/media/.sblvtmp/media"))) return false;
    ++ThrdLng[0];
    if(ThrdKill) return false;

    if(isfile("/etc/fstab"))
    {
        QFile file("/etc/fstab");
        if(! fopen(file)) return false;
        QSL dlst(QDir("/media").entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot));

        for(uchar a(0) ; a < dlst.count() ; ++a)
        {
            cQStr &item(dlst.at(a));
            if(a && ! fopen(file)) return false;
            QSL incl{"* /media/" % item % " *", "* /media/" % item % "/*"};

            while(! file.atEnd())
            {
                QStr cline(file.readLine().trimmed()), fdir;

                if(! cline.startsWith('#') && like(cline.replace('\t', ' ').contains("\\040") ? QStr(cline).replace("\\040", " ") : cline, incl))
                    for(cQStr cdname : mid(cline, instr(cline, "/media/") + 7, instr(cline, " ", instr(cline, "/media/")) - instr(cline, "/media/") - 7).split('/'))
                        if(! cdname.isEmpty())
                        {
                            QStr sbli("/media/.sblvtmp/media" % fdir.append('/' % (cdname.contains("\\040") ? QStr(cdname).replace("\\040", " ") : cdname)));

                            if(! isdir(sbli))
                            {
                                if(! cpdir("/media" % fdir, sbli)) return false;
                                ++ThrdLng[0];
                            }
                        }

                if(ThrdKill) return false;
            }

            file.close();
        }
    }

    if(exist("/var/.sblvtmp")) stype("/var/.sblvtmp") == Isdir ? recrmdir("/var/.sblvtmp") : rmfile("/var/.sblvtmp");
    if(ThrdKill) return false;
    uint lcnt;

    {
        QBA varitms;
        QUCL varitmst;
        varitms.reserve(1000000), varitmst.reserve(50000);
        if(! rodir(varitms, varitmst, "/var")) return false;
        if(! (crtdir("/var/.sblvtmp") && crtdir("/var/.sblvtmp/var"))) return false;
        ++ThrdLng[0];
        QSL elst{"lib/dpkg/lock", "lib/udisks/mtab", "lib/ureadahead/", "log/", "run/", "tmp/"};
        edetect(elst, true);

        if(! varitmst.isEmpty())
        {
            QSL excl[]{{"+_cache/apt/*", "-*.bin_", "-*.bin.*"}, {"_cache/apt/archives/*", "*.deb_"}, {"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*.dpkg-old_", "*~_", "*~/*"}, {"*.gz_", "*.old_"}};
            lcnt = 0;
            QTS in(&varitms, QIODevice::ReadOnly);

            while(! in.atEnd())
            {
                QStr item(in.readLine()), srci("/var/" % item);

                if(exist(srci))
                {
                    if(! (like(item, excl[0], Mixed) || like(item, excl[1], All) || like(item, excl[2]) || exclcheck(elst, item)))
                        switch(varitmst.at(lcnt)) {
                        case Islink:
                            if(! cplink(srci, "/var/.sblvtmp/var/" % item)) return false;
                            ++ThrdLng[0];
                            break;
                        case Isdir:
                            if(! crtdir("/var/.sblvtmp/var/" % item)) return false;
                            ++ThrdLng[0];
                            break;
                        case Isfile:
                            if(issmfs("/var/.sblvtmp", srci) && ! crthlnk(srci, "/var/.sblvtmp/var/" % item)) return false;
                            ++ThrdLng[0];
                        }
                    else if(item.startsWith("log"))
                        switch(varitmst.at(lcnt)) {
                        case Isdir:
                            if(! crtdir("/var/.sblvtmp/var/" % item)) return false;
                            ++ThrdLng[0];
                            break;
                        case Isfile:
                            if(! (like(item, excl[3]) || (item.contains('.') && isnum(right(item, -rinstr(item, "."))))))
                            {
                                QStr sbli("/var/.sblvtmp/var/" % item);
                                crtfile(sbli);
                                if(! cpertime(srci, sbli)) return false;
                                ++ThrdLng[0];
                            }
                        }
                }

                if(ThrdKill) return false;
                ++lcnt;
            }

            in.seek(0), lcnt = 0;

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                if(varitmst.at(lcnt++) == Isdir)
                {
                    QStr sbli("/var/.sblvtmp/var/" % item);
                    if(exist(sbli) && ! cpertime("/var/" % item, sbli)) return false;
                }

                if(ThrdKill) return false;
            }
        }
    }

    if(! cpertime("/var", "/var/.sblvtmp/var")) return false;

    if(isdir("/snap"))
    {
        if(exist("/snap/.sblvtmp")) stype("/snap/.sblvtmp") == Isdir ? recrmdir("/snap/.sblvtmp") : rmfile("/snap/.sblvtmp");
        if(ThrdKill) return false;
        QBA snapitms;
        QUCL snapitmst;
        snapitms.reserve(10000), snapitmst.reserve(500);
        if(! rodir("/snap", snapitms, snapitmst)) return false;
        if(! (crtdir("/snap/.sblvtmp") && crtdir("/snap/.sblvtmp/snap"))) return false;
        ++ThrdLng[0];

        if(! snapitmst.isEmpty())
        {
            QSL excl{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*.dpkg-old_", "*~_", "*~/*"};
            lcnt = 0;
            QTS in(&snapitms, QIODevice::ReadOnly);

            while(! in.atEnd())
            {
                QStr item(in.readLine()), srci("/snap/" % item);

                if(exist(srci) && ! like(item, excl))
                    switch(snapitmst.at(lcnt)) {
                    case Islink:
                    case Isfile:
                        if(! crthlnk(srci, "/snap/.sblvtmp/snap/" % item)) return false;
                        ++ThrdLng[0];
                        break;
                    case Isdir:
                        if(! crtdir("/snap/.sblvtmp/snap/" % item)) return false;
                        ++ThrdLng[0];
                    }

                if(ThrdKill) return false;
                ++lcnt;
            }

            in.seek(0), lcnt = 0;

            while(! in.atEnd())
            {
                QStr item(in.readLine());

                if(snapitmst.at(lcnt++) == Isdir)
                {
                    QStr sbli("/snap/.sblvtmp/snap/" % item);
                    if(exist(sbli) && ! cpertime("/snap/" % item, sbli)) return false;
                }

                if(ThrdKill) return false;
            }
        }

        if(! cpertime("/snap", "/snap/.sblvtmp/snap")) return false;
    }

    QSL usrs;

    {
        QFile file("/etc/passwd");
        if(! fopen(file)) return false;

        while(! file.atEnd())
        {
            QStr usr(file.readLine().trimmed());
            if(usr.contains(":/home/") && isdir("/home/" % (usr = left(usr, instr(usr, ":") -1)))) usrs.prepend(usr);
        }
    }

    usrs.prepend(isdir("/root") ? "" : nullptr);
    if(ThrdKill) return false;

    bool uhl(dfree("/home") > 104857600 && dfree("/root") > 104857600 && [&usrs] {
            QStr mnts(fload("/proc/self/mounts"));

            for(uchar a(1) ; a < usrs.count() ; ++a)
                if(mnts.contains(" /home/" % usrs.at(a) % '/') || ! issmfs("/home", "/home/" % usrs.at(a))) return false;

            return true;
        }());

    if(ThrdKill) return false;

    if(uhl)
    {
        if(exist("/home/.sbuserdata")) stype("/home/.sbuserdata") == Isdir ? recrmdir("/home/.sbuserdata") : rmfile("/home/.sbuserdata");
        if(! (crtdir("/home/.sbuserdata") && cpdir("/home", "/home/.sbuserdata/home"))) return false;
    }
    else if(! (crtdir(sdir[2] % "/.sblivesystemcreate/userdata") && cpdir("/home", sdir[2] % "/.sblivesystemcreate/userdata/home")))
        return false;

    ++ThrdLng[0];
    if(ThrdKill) return false;
    QSL elst{".sbuserdata", ".cache/gvfs", ".local/share/Trash/files/", ".local/share/Trash/info/", ".Xauthority", ".ICEauthority"};

    {
        QFile file(excfile);
        if(! fopen(file)) return false;

        while(! file.atEnd())
        {
            QStr cline(left(file.readLine(), -1));
            if(! cline.isEmpty()) elst.append(cline);
            if(ThrdKill) return false;
        }
    }

    QSL excl{"_lost+found_", "_lost+found/*", "*/lost+found_", "*/lost+found/*", "_Systemback_", "_Systemback/*", "*/Systemback_", "*/Systemback/*", "*~_", "*~/*"};

    for(cQStr &usr : usrs)
        if(! usr.isNull())
        {
            QStr usdir;

            if(uhl)
            {
                if(usr.isEmpty())
                {
                    usdir = "/root/.sbuserdata";
                    if(exist(usdir)) stype(usdir) == Isdir ? recrmdir(usdir) : rmfile(usdir);
                    if(! (crtdir(usdir) && crtdir(usdir.append("/root")))) return false;
                }
                else
                {
                    usdir = "/home/.sbuserdata/home/" % usr;
                    if(! crtdir(usdir)) return false;
                }
            }
            else
            {
                usdir = sdir[2] % (usr.isEmpty() ? "/.sblivesystemcreate/userdata/root" : QStr("/.sblivesystemcreate/userdata/home/" % usr));
                if(! crtdir(usdir)) return false;
            }

            ++ThrdLng[0];
            QStr srcd(usr.isEmpty() ? "/root" : QStr("/home/" % usr));
            QBA useritms;
            QUCL useritmst;
            useritms.reserve(usr.isEmpty() ? 50000 : 5000000), useritmst.reserve(usr.isEmpty() ? 1000 : 100000);
            if(! rodir(useritms, useritmst, srcd, ! iudata ? True : False)) return false;

            if(! useritmst.isEmpty())
            {
                lcnt = 0;
                QTS in(&useritms, QIODevice::ReadOnly);

                while(! in.atEnd())
                {
                    QStr item(in.readLine());

                    if(! (like(item, excl) || exclcheck(elst, item)))
                    {
                        QStr srci(srcd % '/' % item);

                        if(exist(srci))
                        {
                            switch(useritmst.at(lcnt)) {
                            case Islink:
                                if(uhl)
                                {
                                    if(! crthlnk(srci, usdir % '/' % item)) return false;
                                }
                                else if(! cplink(srci, usdir % '/' % item))
                                    return false;

                                ++ThrdLng[0];
                                break;
                            case Isdir:
                                if(! crtdir(usdir % '/' % item)) return false;
                                ++ThrdLng[0];
                                break;
                            case Isfile:
                                if(iudata || fsize(srci) <= 8000000)
                                {
                                    if(uhl)
                                    {
                                        if(! crthlnk(srci, usdir % '/' % item)) return false;
                                    }
                                    else if(! cpfile(srci, usdir % '/' % item))
                                        return false;

                                    ++ThrdLng[0];
                                }
                            }
                        }
                    }

                    if(ThrdKill) return false;
                    ++lcnt;
                }

                in.seek(0), lcnt = 0;

                while(! in.atEnd())
                {
                    QStr item(in.readLine());

                    if(useritmst.at(lcnt++) == Isdir)
                    {
                        QStr sbli(usdir % '/' % item);
                        if(exist(sbli) && ! cpertime(srcd % '/' % item, sbli)) return false;
                    }

                    if(ThrdKill) return false;
                }
            }

            if(! (iudata || usr.isEmpty()) && isfile(srcd % "/.config/user-dirs.dirs"))
            {
                QFile file(srcd % "/.config/user-dirs.dirs");
                if(! fopen(file)) return false;

                while(! file.atEnd())
                {
                    QStr cline(file.readLine().trimmed()), dir;

                    if(! cline.startsWith('#') && cline.contains("$HOME") && (dir = left(right(cline, -instr(cline, "/")), -1)).length())
                    {
                        QStr srci(srcd % '/' % dir);

                        if(isdir(srci))
                        {
                            QStr sbld(usdir % '/' % dir);

                            if(! isdir(sbld))
                            {
                                if(! cpdir(srci, sbld)) return false;
                                ++ThrdLng[0];
                            }
                        }
                    }

                    if(ThrdKill) return false;
                    continue;
                }

                file.close();
            }

            if(! cpertime(srcd, usdir)) return false;
        }

    return true;
}
