Files
systemback/libsystemback/sblib.cpp
2021-04-14 16:09:07 +09:00

4244 lines
159 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright(C) 2018-2020, Franco Conidi <edmondweblog@gmail.com>
*
* 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("genisoimage") ? 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/nvme0*", "_/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/nvme0*", "_/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/nvme0*", "_/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;
}