diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -54,3 +54,25 @@ ) ecm_add_test(${solidLogindInhibitionArgument_SRCS} TEST_NAME "logindinhibitionargument" LINK_LIBRARIES Qt5::Test KF5Solid_static) endif() + +########### fstab ########### +if(BUILD_DEVICE_BACKEND_fstab) + ecm_add_test( + filesystem_entry_test.cpp + filesystem_entry_test.h + TEST_NAME "filesystem_entry_test" + LINK_LIBRARIES KF5Solid_static Qt5::Test + ) + ecm_add_test( + filesystem_table_parser_test.cpp + filesystem_table_parser_test.h + TEST_NAME "filesystem_table_parser_test" + LINK_LIBRARIES KF5Solid_static Qt5::Test + ) + ecm_add_test( + system_filesystem_table_test.cpp + system_filesystem_table_test.h + TEST_NAME "system_filesystem_table_test" + LINK_LIBRARIES KF5Solid_static Qt5::Test + ) +endif() diff --git a/autotests/filesystem_entry_test.h b/autotests/filesystem_entry_test.h new file mode 100644 --- /dev/null +++ b/autotests/filesystem_entry_test.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef FILESYSTEM_ENTRY_TEST_H +#define FILESYSTEM_ENTRY_TEST_H + +#include + +class FilesystemEntryTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testDevice(); + void testMountPath(); + void testType(); + void testMountOptions(); + void testId_data(); + void testId(); +}; + +#endif diff --git a/autotests/filesystem_entry_test.cpp b/autotests/filesystem_entry_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/filesystem_entry_test.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filesystem_entry_test.h" +#include + +#include + +namespace Fstab = Solid::Backends::Fstab; + +namespace { + +const QLatin1Literal Device("/dev/sda1"); +const QLatin1Literal MountPath("/mnt"); +const QLatin1Literal Type("ext4"); +const QStringList MountOptions{QLatin1Literal("ro"), QLatin1Literal("noatime")}; + +} // unnamed namespace + +void FilesystemEntryTest::testDevice() +{ + Fstab::FilesystemEntry testObject(Device, MountPath, Type, MountOptions); + QCOMPARE(Device, testObject.device()); +} + +void FilesystemEntryTest::testMountPath() +{ + Fstab::FilesystemEntry testObject(Device, MountPath, Type, MountOptions); + QCOMPARE(MountPath, testObject.mountPath()); +} + +void FilesystemEntryTest::testType() +{ + Fstab::FilesystemEntry testObject(Device, MountPath, Type, MountOptions); + QCOMPARE(Type, testObject.type()); +} + +void FilesystemEntryTest::testMountOptions() +{ + Fstab::FilesystemEntry testObject(Device, MountPath, Type, MountOptions); + QCOMPARE(MountOptions, testObject.mountOptions()); +} + +void FilesystemEntryTest::testId_data() +{ + QTest::addColumn("device"); + QTest::addColumn("mountPath"); + QTest::addColumn("type"); + QTest::addColumn("id"); + QTest::newRow("Device as id") << QString(Device) << QString(MountPath) << QString(Type) << QString(Device); + QTest::newRow("encfs") << QString(QLatin1Literal("encfs")) << QString(MountPath) + << QString(QLatin1Literal("fuse.encfs")) + << QLatin1Literal("encfs") + QLatin1Char('@') + MountPath; +} + +void FilesystemEntryTest::testId() +{ + QFETCH(QString, device); + QFETCH(QString, mountPath); + QFETCH(QString, type); + QFETCH(QString, id); + Fstab::FilesystemEntry testObject(device, mountPath, type, MountOptions); + QCOMPARE(id, testObject.id()); +} + +QTEST_MAIN(FilesystemEntryTest) diff --git a/autotests/filesystem_table_parser_test.h b/autotests/filesystem_table_parser_test.h new file mode 100644 --- /dev/null +++ b/autotests/filesystem_table_parser_test.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef FILESYSTEM_TABLE_PARSER_TEST_H +#define FILESYSTEM_TABLE_PARSER_TEST_H + +#include + +class FilesystemTableParserTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testHappyDay(); + void testWithFilterFunction(); + void testFileNotFound(); + void testParseSpecialCharacters(); +}; + +#endif diff --git a/autotests/filesystem_table_parser_test.cpp b/autotests/filesystem_table_parser_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/filesystem_table_parser_test.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filesystem_table_parser_test.h" +#include +#include + +#include +#include + +namespace Fstab = Solid::Backends::Fstab; + +const char exampleFstab[] = +"/dev/sda1 /boot ext2 noauto,noatime 1 2\n" +"/dev/sda2 / ext4 noatime 0 1\n" ; + +void FilesystemTableParserTest::testHappyDay() +{ + QTemporaryFile testFstabFile; + QVERIFY(testFstabFile.open()); + testFstabFile.write(exampleFstab); + testFstabFile.close(); + QList entries = Fstab::FilesystemTableParser::parseFilesystemTable(testFstabFile.fileName()); + QCOMPARE(entries.size(), 2); + { + QCOMPARE(entries.at(0).device(), QLatin1String("/dev/sda1")); + QCOMPARE(entries.at(0).mountPath(), QLatin1String("/boot")); + QCOMPARE(entries.at(0).type(), QLatin1String("ext2")); + const QStringList expectedOptions{QLatin1String("noauto"), QLatin1String("noatime")}; + QCOMPARE(entries.at(0).mountOptions(), expectedOptions); + QCOMPARE(entries.at(0).id(), QLatin1String("/dev/sda1")); + } + QCOMPARE(entries.at(1).device(), QLatin1String("/dev/sda2")); + QCOMPARE(entries.at(1).mountPath(), QLatin1String("/")); + QCOMPARE(entries.at(1).type(), QLatin1String("ext4")); + QCOMPARE(entries.at(1).mountOptions(), QStringList(QLatin1String("noatime"))); + QCOMPARE(entries.at(1).id(), QLatin1String("/dev/sda2")); +} + +void FilesystemTableParserTest::testWithFilterFunction() +{ + QTemporaryFile testFstabFile; + QVERIFY(testFstabFile.open()); + testFstabFile.write(exampleFstab); + testFstabFile.close(); + const auto filterFunction = + [](const QString& device, const QString& type) -> Fstab::FilesystemTableParser::FilterReturn + { + if (device == QLatin1String("/dev/sda2") && type == QLatin1String("ext4")) { + return Fstab::FilesystemTableParser::FilterReturn::Keep; + } + return Fstab::FilesystemTableParser::FilterReturn::Discard; + }; + QList entries = Fstab::FilesystemTableParser::parseFilesystemTable( + testFstabFile.fileName(), filterFunction); + QCOMPARE(entries.size(), 1); + QCOMPARE(entries.at(0).device(), QLatin1String("/dev/sda2")); + QCOMPARE(entries.at(0).mountPath(), QLatin1String("/")); + QCOMPARE(entries.at(0).type(), QLatin1String("ext4")); + QCOMPARE(entries.at(0).mountOptions(), QStringList(QLatin1String("noatime"))); + QCOMPARE(entries.at(0).id(), QLatin1String("/dev/sda2")); +} + +void FilesystemTableParserTest::testFileNotFound() +{ + QList entries = Fstab::FilesystemTableParser::parseFilesystemTable(QLatin1String("/non_existing_file")); + QCOMPARE(entries.size(), 0); +} + +const char exampleMtabWithSpecialCharacters[] = +"/home/dha/some.iso /home/dha/mn\\\\t\\0401 fuseiso defaults 0 0\n" +"/home/dha/tab_character\\011_in_file_name.iso /home/dha/space_\\040_in_dir_name fuseiso defaults 0 0\n"; + +void FilesystemTableParserTest::testParseSpecialCharacters() +{ + QTemporaryFile testFstabFile; + QVERIFY(testFstabFile.open()); + testFstabFile.write(exampleMtabWithSpecialCharacters); + testFstabFile.close(); + QList entries = Fstab::FilesystemTableParser::parseFilesystemTable(testFstabFile.fileName()); + QCOMPARE(entries.size(), 2); + QCOMPARE(entries.at(0).device(), QLatin1String("/home/dha/some.iso")); + QCOMPARE(entries.at(0).mountPath(), QLatin1String("/home/dha/mn\\t 1")); + QCOMPARE(entries.at(0).type(), QLatin1String("fuseiso")); + QCOMPARE(entries.at(0).mountOptions(), QStringList(QLatin1String("defaults"))); + QCOMPARE(entries.at(0).id(), QLatin1String("/home/dha/some.iso")); + QCOMPARE(entries.at(1).device(), QLatin1String("/home/dha/tab_character\t_in_file_name.iso")); + QCOMPARE(entries.at(1).mountPath(), QLatin1String("/home/dha/space_ _in_dir_name")); + QCOMPARE(entries.at(1).type(), QLatin1String("fuseiso")); + QCOMPARE(entries.at(1).mountOptions(), QStringList(QLatin1String("defaults"))); + QCOMPARE(entries.at(1).id(), QLatin1String("/home/dha/tab_character\t_in_file_name.iso")); +} + +QTEST_MAIN(FilesystemTableParserTest) diff --git a/autotests/system_filesystem_table_test.h b/autotests/system_filesystem_table_test.h new file mode 100644 --- /dev/null +++ b/autotests/system_filesystem_table_test.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef SYSTEM_FILESYSTEM_TABLE_TEST_H +#define SYSTEM_FILESYSTEM_TABLE_TEST_H + +#include +#include + +class SystemFilesystemTableTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + + void init(); + void testFilesystemIds(); + void testDefaultFilterFunction_data(); + void testDefaultFilterFunction(); + void testFilesystemEntry_mtabTakesPrecedence(); + void testFilesystemEntry_onlyInFstab(); + void testFilesystemEntry_onlyInMtab(); + void testFilesystemEntry_nonExisting(); + void testIsMounted_mounted(); + void testIsMounted_notMounted(); + void testTableChanged_fstabUpdated(); + void testTableChanged_mtabUpdated(); + +private: + QTemporaryFile testFstabFile; + QTemporaryFile testMtabFile; +}; + +#endif diff --git a/autotests/system_filesystem_table_test.cpp b/autotests/system_filesystem_table_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/system_filesystem_table_test.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "system_filesystem_table_test.h" +#include +#include + +#include +#include + +namespace Fstab = Solid::Backends::Fstab; + +Q_DECLARE_METATYPE(Fstab::FilesystemTableParser::FilterReturn) + +const char exampleFstab[] = +"/dev/sda1 /boot ext2 noauto,noatime 1 2\n" +"/dev/sda2 / ext4 noatime 0 1\n" +"//192.168.1.2/home /mnt/home cifs guest,uid=1000,iocharset=utf8,file_mode=0777,dir_mode=0777,noperm 0 0\n" +"//192.168.1.2/data /data cifs guest,uid=1000,iocharset=utf8,file_mode=0777,dir_mode=0777,noperm 0 0\n"; + +const char exampleProcSelfMounts[] = +"/dev/root / ext4 rw,noatime,discard 0 0\n" +"devtmpfs /dev devtmpfs rw,nosuid,relatime,size=10240k,nr_inodes=4038699,mode=755 0 0\n" +"proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n" +"tmpfs /run tmpfs rw,nodev,relatime,size=3238020k,mode=755 0 0\n" +"sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n" +"debugfs /sys/kernel/debug debugfs rw,nosuid,nodev,noexec,relatime 0 0\n" +"fusectl /sys/fs/fuse/connections fusectl rw,nosuid,nodev,noexec,relatime 0 0\n" +"pstore /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0\n" +"cgroup_root /sys/fs/cgroup tmpfs rw,nosuid,nodev,noexec,relatime,size=10240k,mode=755 0 0\n" +"openrc /sys/fs/cgroup/openrc cgroup rw,nosuid,nodev,noexec,relatime,release_agent=/lib/rc/sh/cgroup-release-agent.sh,name=openrc 0 0\n" +"none /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate 0 0\n" +"cpuset /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n" +"cpu /sys/fs/cgroup/cpu cgroup rw,nosuid,nodev,noexec,relatime,cpu 0 0\n" +"cpuacct /sys/fs/cgroup/cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpuacct 0 0\n" +"blkio /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n" +"memory /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n" +"devices /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0\n" +"freezer /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n" +"net_cls /sys/fs/cgroup/net_cls cgroup rw,nosuid,nodev,noexec,relatime,net_cls 0 0\n" +"perf_event /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0\n" +"net_prio /sys/fs/cgroup/net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_prio 0 0\n" +"hugetlb /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0\n" +"pids /sys/fs/cgroup/pids cgroup rw,nosuid,nodev,noexec,relatime,pids 0 0\n" +"rdma /sys/fs/cgroup/rdma cgroup rw,nosuid,nodev,noexec,relatime,rdma 0 0\n" +"mqueue /dev/mqueue mqueue rw,nosuid,nodev,noexec,relatime 0 0\n" +"devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n" +"shm /dev/shm tmpfs rw,nosuid,nodev,noexec,relatime 0 0\n" +"/dev/root /var/lib/docker ext4 rw,noatime,discard 0 0\n" +"/dev/mapper/_dev_sda3 /home/dha ext4 rw,noatime,discard,journal_checksum,journal_async_commit,data=writeback 0 0\n" +"none /run/user/1000 tmpfs rw,relatime,mode=700,uid=1000 0 0\n" +"cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc /home/dha/Vaults/TestVaultCryFS fuse.cryfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0\n" +"encfs /home/dha/Vaults/TestVaultEncFS fuse.encfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000,default_permissions 0 0\n" +"encfs /home/dha/Vaults/TestVaultEncFS2 fuse.encfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000,default_permissions 0 0\n" +"//192.168.1.2/data /mnt/data cifs guest,uid=1000,iocharset=utf8,file_mode=0777,dir_mode=0777,noperm 0 0\n"; + +void SystemFilesystemTableTest::init() +{ + QVERIFY(testFstabFile.open()); + QVERIFY(testFstabFile.resize(0)); + testFstabFile.write(exampleFstab); + testFstabFile.close(); + QVERIFY(testMtabFile.open()); + QVERIFY(testMtabFile.resize(0)); + testMtabFile.write(exampleProcSelfMounts); + testMtabFile.close(); +} + +void SystemFilesystemTableTest::testFilesystemIds() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const QStringList ids = testObject.filesystemIds(); + QCOMPARE(ids.size(), 5); + QCOMPARE(ids[0], QLatin1String("//192.168.1.2/home")); + QCOMPARE(ids[1], QLatin1String("//192.168.1.2/data")); + QCOMPARE(ids[2], QLatin1String("cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc")); + QCOMPARE(ids[3], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS")); + QCOMPARE(ids[4], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS2")); +} + +void SystemFilesystemTableTest::testDefaultFilterFunction_data() +{ + QTest::addColumn("device"); + QTest::addColumn("fstype"); + QTest::addColumn("expectedFilterReturn"); + QTest::newRow("keep nfs") << "192.168.1.2:/home" << "nfs" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep nfs4") << "192.168.1.2:/home" << "nfs4" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep smbfs") << "192.168.1.2:/home" << "smbfs" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep cifs") << "192.168.1.2:/home" << "cifs" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep fuse.encfs") << "encfs" << "fuse.encfs" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep fuse.cryfs") << "cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc" << "fuse.cryfs" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep fuse.overlay") << "" << "overlay" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("keep device starting with //") << "//192.168.1.2/data" << "foo" << Fstab::FilesystemTableParser::FilterReturn::Keep; + QTest::newRow("discard device starting with single /") << "/192.168.1.2/data" << "foo" << Fstab::FilesystemTableParser::FilterReturn::Discard; + QTest::newRow("discard unknown fstype") << "/dev/sda1" << "ext4" << Fstab::FilesystemTableParser::FilterReturn::Discard; +} + +void SystemFilesystemTableTest::testDefaultFilterFunction() +{ + QFETCH(QString, device); + QFETCH(QString, fstype); + QFETCH(Fstab::FilesystemTableParser::FilterReturn, expectedFilterReturn); + QCOMPARE(Fstab::DefaultFilterFunction(device, fstype), expectedFilterReturn); +} + +void SystemFilesystemTableTest::testFilesystemEntry_mtabTakesPrecedence() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const Fstab::FilesystemEntry* fsEntry = testObject.filesystemEntry("//192.168.1.2/data"); + QVERIFY(fsEntry != nullptr); + QCOMPARE(fsEntry->device(), "//192.168.1.2/data"); + QCOMPARE(fsEntry->mountPath(), "/mnt/data"); +} + +void SystemFilesystemTableTest::testFilesystemEntry_onlyInFstab() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const Fstab::FilesystemEntry* fsEntry = testObject.filesystemEntry("//192.168.1.2/home"); + QVERIFY(fsEntry != nullptr); + QCOMPARE(fsEntry->device(), "//192.168.1.2/home"); + QCOMPARE(fsEntry->mountPath(), "/mnt/home"); +} + +void SystemFilesystemTableTest::testFilesystemEntry_onlyInMtab() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const Fstab::FilesystemEntry* fsEntry = testObject.filesystemEntry("cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc"); + QVERIFY(fsEntry != nullptr); + QCOMPARE(fsEntry->device(), "cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc"); + QCOMPARE(fsEntry->mountPath(), "/home/dha/Vaults/TestVaultCryFS"); +} + +void SystemFilesystemTableTest::testFilesystemEntry_nonExisting() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + QVERIFY(testObject.filesystemEntry("/dev/non_existing") == nullptr); +} + +void SystemFilesystemTableTest::testIsMounted_mounted() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const Fstab::FilesystemEntry *fsEntry = testObject.filesystemEntry("//192.168.1.2/data"); + QVERIFY(fsEntry != nullptr); + QVERIFY(testObject.isMounted(*fsEntry)); +} + +void SystemFilesystemTableTest::testIsMounted_notMounted() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + const Fstab::FilesystemEntry *fsEntry = testObject.filesystemEntry("//192.168.1.2/home"); + QVERIFY(fsEntry != nullptr); + QVERIFY(testObject.isMounted(*fsEntry) == false); +} + +void SystemFilesystemTableTest::testTableChanged_fstabUpdated() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + QStringList ids = testObject.filesystemIds(); + QCOMPARE(ids.size(), 5); + QCOMPARE(ids[0], QLatin1String("//192.168.1.2/home")); + QCOMPARE(ids[1], QLatin1String("//192.168.1.2/data")); + QCOMPARE(ids[2], QLatin1String("cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc")); + QCOMPARE(ids[3], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS")); + QCOMPARE(ids[4], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS2")); + QSignalSpy tableChangedSpy(&testObject, SIGNAL(tableChanged())); + QVERIFY(testFstabFile.open()); + QVERIFY(testFstabFile.resize(0)); + testFstabFile.write( "//192.168.1.2/media /mnt/media\n"); + testFstabFile.close(); + QVERIFY(tableChangedSpy.wait()); + QCOMPARE(tableChangedSpy.count(), 1); + ids = testObject.filesystemIds(); + QCOMPARE(ids.size(), 5); + QCOMPARE(ids[0], QLatin1String("//192.168.1.2/media")); + QCOMPARE(ids[1], QLatin1String("cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc")); + QCOMPARE(ids[2], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS")); + QCOMPARE(ids[3], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS2")); + QCOMPARE(ids[4], QLatin1String("//192.168.1.2/data")); +} + +void SystemFilesystemTableTest::testTableChanged_mtabUpdated() +{ + Fstab::SystemFilesystemTable testObject(testFstabFile.fileName(), testMtabFile.fileName()); + QStringList ids = testObject.filesystemIds(); + QCOMPARE(ids.size(), 5); + QCOMPARE(ids[0], QLatin1String("//192.168.1.2/home")); + QCOMPARE(ids[1], QLatin1String("//192.168.1.2/data")); + QCOMPARE(ids[2], QLatin1String("cryfs@/home/dha/.local/share/plasma-vault/TestVaultCryFS.enc")); + QCOMPARE(ids[3], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS")); + QCOMPARE(ids[4], QLatin1String("encfs@/home/dha/Vaults/TestVaultEncFS2")); + QSignalSpy tableChangedSpy(&testObject, SIGNAL(tableChanged())); + QVERIFY(testMtabFile.open()); + QVERIFY(testMtabFile.resize(0)); + testMtabFile.write( "//192.168.1.2/media /mnt/media\n"); + testMtabFile.close(); + QVERIFY(tableChangedSpy.wait()); + QCOMPARE(tableChangedSpy.count(), 1); + ids = testObject.filesystemIds(); + QCOMPARE(ids.size(), 3); + QCOMPARE(ids[0], QLatin1String("//192.168.1.2/home")); + QCOMPARE(ids[1], QLatin1String("//192.168.1.2/data")); + QCOMPARE(ids[2], QLatin1String("//192.168.1.2/media")); +} + +QTEST_MAIN(SystemFilesystemTableTest) diff --git a/src/solid/devices/backends/fstab/CMakeLists.txt b/src/solid/devices/backends/fstab/CMakeLists.txt --- a/src/solid/devices/backends/fstab/CMakeLists.txt +++ b/src/solid/devices/backends/fstab/CMakeLists.txt @@ -1,10 +1,18 @@ set(backend_sources - fstabmanager.cpp + call_system_command.cpp + call_system_command.h + filesystem_entry.cpp + filesystem_entry.h + filesystem_table_parser.cpp + filesystem_table_parser.h + filesystem_table_watcher.cpp + filesystem_table_watcher.h fstabdevice.cpp + fstabmanager.cpp fstabnetworkshare.cpp fstabstorageaccess.cpp - fstabhandling.cpp - fstabwatcher.cpp + system_filesystem_table.cpp + system_filesystem_table.h ) ecm_qt_declare_logging_category(backend_sources diff --git a/src/solid/devices/backends/fstab/call_system_command.h b/src/solid/devices/backends/fstab/call_system_command.h new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/call_system_command.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SOLID_BACKENDS_FSTAB_CALL_SYSTEM_COMMAND_H +#define SOLID_BACKENDS_FSTAB_CALL_SYSTEM_COMMAND_H + +#include +#include +#include +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +bool CallSystemCommand(const QString &commandName, const QStringList &args, const QObject *recvr, std::function callback); + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#endif diff --git a/src/solid/devices/backends/fstab/call_system_command.cpp b/src/solid/devices/backends/fstab/call_system_command.cpp new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/call_system_command.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "call_system_command.h" +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +bool CallSystemCommand(const QString &commandName, const QStringList &args, + const QObject *receiver, std::function callback) +{ + QStringList env = QProcess::systemEnvironment(); + env.replaceInStrings( + QRegularExpression(QStringLiteral("^PATH=(.*)"), QRegularExpression::CaseInsensitiveOption), + QStringLiteral("PATH=/sbin:/bin:/usr/sbin/:/usr/bin")); + QProcess *process = new QProcess(); + QObject::connect(process, static_cast(&QProcess::finished), receiver, + [process, callback](int exitCode, QProcess::ExitStatus exitStatus) { + Q_UNUSED(exitCode) + Q_UNUSED(exitStatus) + callback(process); + process->deleteLater(); + }); + process->setEnvironment(env); + process->start(commandName, args); + if (process->waitForStarted()) { + return true; + } + delete process; + return false; +} + +} // namespace Fstab +} // namespace Backends +} // namespace Solid diff --git a/src/solid/devices/backends/fstab/filesystem_entry.h b/src/solid/devices/backends/fstab/filesystem_entry.h new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_entry.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SOLID_BACKENDS_FSTAB_FILESYSTEM_ENTRY_H +#define SOLID_BACKENDS_FSTAB_FILESYSTEM_ENTRY_H + +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +// Class that describes a filesystem that is either mounted by the system or can be mounted by the system. +// The information represented here is populated by parsing /etc/fstab, /etc/mtab or other mtab style files. +class FilesystemEntry +{ +public: + FilesystemEntry(const QString& device, const QString& mountPath, const QString& type, const QStringList& mountOptions); + QString device() const; + QString mountPath() const; + QString type() const; + QStringList mountOptions() const; + QString id() const; +private: + QString m_device; // 1st field fstab: fs_spec mntent: mnt_fsname + QString m_mountPath; // 2nd field fstab: fs_file mntent: mnt_dir + QString m_type; // 3rd field fstab: fs_vfstype mntent: mnt_type + QStringList m_mountOptions; // 4th field fstab: fs_mntopt mntent: mnt_opts + //int m_dumpFrequency; // 5th field fstab: fs_freq mntopt: mnt_freq + //int m_passNo; // 6th field fstab: fs_passno mntent: mnt_passno +}; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#endif diff --git a/src/solid/devices/backends/fstab/filesystem_entry.cpp b/src/solid/devices/backends/fstab/filesystem_entry.cpp new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_entry.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filesystem_entry.h" + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +FilesystemEntry::FilesystemEntry(const QString& device, const QString& mountPath, const QString& type, const QStringList& mountOptions) : + m_device{device}, + m_mountPath{mountPath}, + m_type{type}, + m_mountOptions{mountOptions} +{ +} + +QString FilesystemEntry::device() const +{ + return m_device; +} + +QString FilesystemEntry::mountPath() const +{ + return m_mountPath; +} + +QString FilesystemEntry::type() const +{ + return m_type; +} + +QStringList FilesystemEntry::mountOptions() const +{ + return m_mountOptions; +} + +QString FilesystemEntry::id() const +{ + if (m_type == QLatin1Literal("fuse.encfs")) { + return m_device + QLatin1Char('@') + m_mountPath; + } + return m_device; +} + +} // namespace Fstab +} // namespace Backends +} // namespace Solid diff --git a/src/solid/devices/backends/fstab/filesystem_table_parser.h b/src/solid/devices/backends/fstab/filesystem_table_parser.h new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_table_parser.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SOLID_BACKENDS_FSTAB_FILESYSTEM_TABLE_PARSER_H +#define SOLID_BACKENDS_FSTAB_FILESYSTEM_TABLE_PARSER_H + +#include +#include + +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +class FilesystemEntry; + +// Class that can parse a filesystem table, this could be either a fstab file or mtab file +class FilesystemTableParser +{ +public: + enum class FilterReturn { + Keep, + Discard + }; + using FilterFunction = std::function; + static QList parseFilesystemTable(const QString& path, FilterFunction filterFunction = nullptr); + static QString systemFstabPath(); + static QString systemMtabPath(); +}; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#endif diff --git a/src/solid/devices/backends/fstab/filesystem_table_parser.cpp b/src/solid/devices/backends/fstab/filesystem_table_parser.cpp new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_table_parser.cpp @@ -0,0 +1,183 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filesystem_table_parser.h" +#include "filesystem_entry.h" + +#include + +#include +#include + +#if HAVE_SYS_MNTTAB_H +#include +#endif +#if HAVE_MNTENT_H +#include +#elif defined(HAVE_SYS_MNTENT_H) +#include +#endif + +// This is the *BSD branch +#if HAVE_SYS_MOUNT_H +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_PARAM_H +#include +#endif +#include +#endif + +#ifdef Q_OS_SOLARIS +#define FSTAB "/etc/vfstab" +#else +#define FSTAB "/etc/fstab" +#endif + +#if ! HAVE_GETMNTINFO +# ifdef _PATH_MOUNTED +// On some Linux, MNTTAB points to /etc/fstab ! +# undef MNTTAB +# define MNTTAB _PATH_MOUNTED +# else +# ifndef MNTTAB +# ifdef MTAB_FILE +# define MNTTAB MTAB_FILE +# else +# define MNTTAB "/etc/mnttab" +# endif +# endif +# endif +#endif + +// There are (at least) four kind of APIs: +// setmntent + getmntent + struct mntent (linux...) +// getmntent + struct mnttab +// getmntinfo + struct statfs&flags (BSD 4.4 and friends) +// getfsent + char* (BSD 4.3 and friends) + +#if HAVE_SETMNTENT +#define SETMNTENT setmntent +#define ENDMNTENT endmntent +#define STRUCT_MNTENT struct mntent * +#define STRUCT_SETMNTENT FILE * +#define GETMNTENT(file, var) ((var = getmntent(file)) != nullptr) +#define MOUNTPOINT(var) var->mnt_dir +#define MOUNTTYPE(var) var->mnt_type +#define MOUNTOPTIONS(var) var->mnt_opts +#define FSNAME(var) var->mnt_fsname +#else +#define SETMNTENT fopen +#define ENDMNTENT fclose +#define STRUCT_MNTENT struct mnttab +#define STRUCT_SETMNTENT FILE * +#define GETMNTENT(file, var) (getmntent(file, &var) == nullptr) +#define MOUNTPOINT(var) var.mnt_mountp +#define MOUNTTYPE(var) var.mnt_fstype +#define MOUNTOPTIONS(var) var.mnt_mntopts +#define FSNAME(var) var.mnt_special +#endif + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +QList FilesystemTableParser::parseFilesystemTable(const QString& path, FilterFunction filterFunction) +{ + QList ret; +#if HAVE_SETMNTENT + + FILE *filesystemTable = setmntent(path.toLatin1().data(), "r"); + if (filesystemTable == nullptr) { + return ret; + } + + struct mntent *fe = nullptr; + while ((fe = getmntent(filesystemTable)) != nullptr) { + const QString device(QFile::decodeName(fe->mnt_fsname)); + const QString type(QString::fromLatin1(fe->mnt_type)); + if (filterFunction && filterFunction(device, type) == FilterReturn::Discard) { + continue; + } + ret += FilesystemEntry( device, + QFile::decodeName(fe->mnt_dir), + type, + QString::fromLatin1(fe->mnt_opts).split(QLatin1Char(','))); + } + + endmntent(filesystemTable); + +#else + + QFile fstab(FSTAB); + if (!fstab.open(QIODevice::ReadOnly)) { + return ret; + } + + QTextStream stream(&fstab); + QString line; + + while (!stream.atEnd()) { + line = stream.readLine().simplified(); + if (line.isEmpty() || line.startsWith('#')) { + continue; + } + + // not empty or commented out by '#' + const QStringList items = line.split(' '); + +#ifdef Q_OS_SOLARIS + if (items.count() < 5) { + continue; + } +#else + if (items.count() < 4) { + continue; + } +#endif + const QString device(items.at(0)); + const QString type(items.at(2)); + if (filterFunction && filterFunction(device, type) == FilterReturn::Discard) { + continue; + } + ret += FilesystemEntry(device, items.at(1), type, items.at(3).split(QLatin1Char(','))); + } + + fstab.close(); +#endif + return ret; +} + +QString FilesystemTableParser::systemFstabPath() +{ + return QLatin1String(FSTAB); +} + +QString FilesystemTableParser::systemMtabPath() +{ + return QLatin1String(MNTTAB); +} + +} // namespace Fstab +} // namespace Backends +} // namespace Solid diff --git a/src/solid/devices/backends/fstab/filesystem_table_watcher.h b/src/solid/devices/backends/fstab/filesystem_table_watcher.h new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_table_watcher.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SOLID_BACKENDS_FSTAB_FILESYSTEM_TABLE_H +#define SOLID_BACKENDS_FSTAB_FILESYSTEM_TABLE_H + +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +class FilesystemTableWatcher : public QObject +{ + Q_OBJECT +public: + FilesystemTableWatcher(const QString& tablePath); + virtual ~FilesystemTableWatcher(); +Q_SIGNALS: + void tableChanged(); +private: + class SocketNotifierWatcher; + QScopedPointer m_socketNotifierWatcher; + class FilesystemWatcher; + QScopedPointer m_filesystemWatcher; +}; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#endif diff --git a/src/solid/devices/backends/fstab/filesystem_table_watcher.cpp b/src/solid/devices/backends/fstab/filesystem_table_watcher.cpp new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/filesystem_table_watcher.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filesystem_table_watcher.h" +#include +#include +#include + +namespace +{ + +bool IsProcPath(const QString &path) +{ + return QFile::symLinkTarget(path).startsWith("/proc/"); +} + +} + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +class FilesystemTableWatcher::SocketNotifierWatcher +{ + QFile m_file; + FilesystemTableWatcher *m_fsTableWatcher; + QScopedPointer m_socketNotifier; +public: + SocketNotifierWatcher(const QString &path, FilesystemTableWatcher *fsTableWatcher) + : m_file(path) + , m_fsTableWatcher(fsTableWatcher) + , m_socketNotifier(nullptr) + { + if (m_file.open(QIODevice::ReadOnly)) { + m_socketNotifier.reset(new QSocketNotifier(m_file.handle(), QSocketNotifier::Exception)); + connect(m_socketNotifier.get(), SIGNAL(activated(int)), m_fsTableWatcher, SIGNAL(tableChanged())); + } + } +}; + +class FilesystemTableWatcher::FilesystemWatcher : public QObject +{ + Q_OBJECT + FilesystemTableWatcher *m_fsTableWatcher; + QFileSystemWatcher m_fsWatcher; +private Q_SLOTS: + void fileChanged(const QString &path) + { + emit m_fsTableWatcher->tableChanged(); + m_fsWatcher.addPath(path); + } +public: + FilesystemWatcher(const QString &path, FilesystemTableWatcher *fsTableWatcher) + : m_fsTableWatcher(fsTableWatcher) + { + m_fsWatcher.addPath(path); + connect(&m_fsWatcher, SIGNAL(fileChanged(const QString&)), this, SLOT(fileChanged(const QString&))); + } +}; + +FilesystemTableWatcher::FilesystemTableWatcher(const QString& tablePath) + : m_socketNotifierWatcher(IsProcPath(tablePath) ? new SocketNotifierWatcher(tablePath, this) : nullptr) + , m_filesystemWatcher(IsProcPath(tablePath) ? nullptr : new FilesystemWatcher(tablePath, this)) +{ +} + +FilesystemTableWatcher::~FilesystemTableWatcher() = default; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#include "filesystem_table_watcher.moc" diff --git a/src/solid/devices/backends/fstab/fstabdevice.h b/src/solid/devices/backends/fstab/fstabdevice.h --- a/src/solid/devices/backends/fstab/fstabdevice.h +++ b/src/solid/devices/backends/fstab/fstabdevice.h @@ -22,6 +22,7 @@ #define SOLID_BACKENDS_FSTAB_FSTAB_DEVICE_H #include +#include #include #include #include "fstabstorageaccess.h" @@ -33,12 +34,14 @@ namespace Fstab { +class SystemFilesystemTable; + class FstabDevice : public Solid::Ifaces::Device { Q_OBJECT public: - FstabDevice(QString uid); + FstabDevice(QString uid, const FilesystemEntry &fsEntry, const SystemFilesystemTable& sysFsTable); virtual ~FstabDevice(); @@ -62,11 +65,15 @@ QString device() const; + const FilesystemEntry& filesystemEntry() const; + + const SystemFilesystemTable& systemFilesystemTable() const; + Q_SIGNALS: - void mtabChanged(const QString &device); + void filesystemEntryChanged(const FilesystemEntry &fsEntry); private Q_SLOTS: - void onMtabChanged(const QString &device); + void onFilesystemEntryChanged(const FilesystemEntry &fsEntry); private: QString m_uid; @@ -82,6 +89,8 @@ Encrypted, }; StorageType m_storageType = StorageType::Other; + FilesystemEntry m_fsEntry; + const SystemFilesystemTable& m_systemFilesystemTable; }; } diff --git a/src/solid/devices/backends/fstab/fstabdevice.cpp b/src/solid/devices/backends/fstab/fstabdevice.cpp --- a/src/solid/devices/backends/fstab/fstabdevice.cpp +++ b/src/solid/devices/backends/fstab/fstabdevice.cpp @@ -19,7 +19,6 @@ */ #include "fstabdevice.h" -#include "fstabhandling.h" #include "fstabnetworkshare.h" #include "fstabstorageaccess.h" #include "fstabservice.h" @@ -31,14 +30,16 @@ using namespace Solid::Backends::Fstab; -FstabDevice::FstabDevice(QString uid) : +FstabDevice::FstabDevice(QString uid, const FilesystemEntry& fsEntry, const SystemFilesystemTable& sysFsTable) : Solid::Ifaces::Device(), - m_uid(uid) + m_uid(uid), + m_fsEntry(fsEntry), + m_systemFilesystemTable(sysFsTable) { m_device = m_uid; m_device.remove(parentUdi() + "/"); - const QString& fstype = FstabHandling::fstype(m_device); + QString fstype = m_fsEntry.type(); qCDebug(FSTAB) << "Adding " << m_device << "type:" << fstype; if (m_device.startsWith("//")) { @@ -52,7 +53,7 @@ } else if (fstype.startsWith("fuse.") || fstype == QLatin1String("overlay")) { m_vendor = fstype; - m_product = m_device.mid(m_device.indexOf(fstype) + fstype.length()); + m_product = m_fsEntry.mountPath(); QString home = QDir::homePath(); if (m_product.startsWith(home)) { m_product = "~" + m_product.mid(home.length()); @@ -63,7 +64,7 @@ } } - const QStringList& gvfsOptions = FstabHandling::options(m_device); + QStringList gvfsOptions = m_fsEntry.mountOptions(); foreach (const QString& option, gvfsOptions) { if (option.startsWith(QLatin1String("x-gvfs-name="))) { @@ -91,7 +92,8 @@ } else if (m_storageType == StorageType::Encrypted) { m_iconName = QLatin1String("folder-decrypted"); } else { - const QStringList& mountPoints = FstabHandling::mountPoints(m_device); + QStringList mountPoints; + mountPoints << m_fsEntry.mountPath(); const QString home = QDir::homePath(); if (mountPoints.contains("/")) { m_iconName = QStringLiteral("drive-harddisk-root"); @@ -185,9 +187,21 @@ return m_device; } -void FstabDevice::onMtabChanged(const QString &device) +const FilesystemEntry& FstabDevice::filesystemEntry() const { - if (m_device == device) { - emit mtabChanged(device); + return m_fsEntry; +} + +const SystemFilesystemTable& FstabDevice::systemFilesystemTable() const +{ + return m_systemFilesystemTable; +} + +void FstabDevice::onFilesystemEntryChanged(const FilesystemEntry &fsEntry) +{ + if (fsEntry.id() != m_fsEntry.id()) { + return; } + m_fsEntry = fsEntry; + emit filesystemEntryChanged(m_fsEntry); } diff --git a/src/solid/devices/backends/fstab/fstabhandling.h b/src/solid/devices/backends/fstab/fstabhandling.h deleted file mode 100644 --- a/src/solid/devices/backends/fstab/fstabhandling.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2006-2010 Kevin Ottens - Copyright 2010 Mario Bensi - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . -*/ - -#ifndef SOLID_BACKENDS_FSTAB_FSTABHANDLING_H -#define SOLID_BACKENDS_FSTAB_FSTABHANDLING_H - -#include -#include - -#include - -class QProcess; -class QObject; - -namespace Solid -{ -namespace Backends -{ -namespace Fstab -{ - -class FstabHandling -{ -public: - FstabHandling(); - - static QStringList deviceList(); - static QStringList currentMountPoints(const QString &device); - static QStringList mountPoints(const QString &device); - static QStringList options(const QString &device); - static QString fstype(const QString &device); - static bool callSystemCommand(const QString &commandName, const QStringList &args, const QObject *recvr, std::function callback); - static void flushMtabCache(); - static void flushFstabCache(); - -private: - static void _k_updateMtabMountPointsCache(); - static void _k_updateFstabMountPointsCache(); - - typedef QMultiHash QStringMultiHash; - - QStringMultiHash m_mtabCache; - QStringMultiHash m_fstabCache; - QStringMultiHash m_fstabOptionsCache; - QHash m_fstabFstypeCache; - bool m_fstabCacheValid; - bool m_mtabCacheValid; - -}; - -} -} -} - -#endif - diff --git a/src/solid/devices/backends/fstab/fstabhandling.cpp b/src/solid/devices/backends/fstab/fstabhandling.cpp deleted file mode 100644 --- a/src/solid/devices/backends/fstab/fstabhandling.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - Copyright 2006-2010 Kevin Ottens - Copyright 2010 Mario Bensi - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . -*/ - -#include "fstabhandling.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#if HAVE_SYS_MNTTAB_H -#include -#endif -#if HAVE_MNTENT_H -#include -#elif defined(HAVE_SYS_MNTENT_H) -#include -#endif - -// This is the *BSD branch -#if HAVE_SYS_MOUNT_H -#if HAVE_SYS_TYPES_H -#include -#endif -#if HAVE_SYS_PARAM_H -#include -#endif -#include -#endif - -#ifdef Q_OS_SOLARIS -#define FSTAB "/etc/vfstab" -#else -#define FSTAB "/etc/fstab" -#endif - -#if ! HAVE_GETMNTINFO -# ifdef _PATH_MOUNTED -// On some Linux, MNTTAB points to /etc/fstab ! -# undef MNTTAB -# define MNTTAB _PATH_MOUNTED -# else -# ifndef MNTTAB -# ifdef MTAB_FILE -# define MNTTAB MTAB_FILE -# else -# define MNTTAB "/etc/mnttab" -# endif -# endif -# endif -#endif - -// There are (at least) four kind of APIs: -// setmntent + getmntent + struct mntent (linux...) -// getmntent + struct mnttab -// getmntinfo + struct statfs&flags (BSD 4.4 and friends) -// getfsent + char* (BSD 4.3 and friends) - -#if HAVE_SETMNTENT -#define SETMNTENT setmntent -#define ENDMNTENT endmntent -#define STRUCT_MNTENT struct mntent * -#define STRUCT_SETMNTENT FILE * -#define GETMNTENT(file, var) ((var = getmntent(file)) != nullptr) -#define MOUNTPOINT(var) var->mnt_dir -#define MOUNTTYPE(var) var->mnt_type -#define MOUNTOPTIONS(var) var->mnt_opts -#define FSNAME(var) var->mnt_fsname -#else -#define SETMNTENT fopen -#define ENDMNTENT fclose -#define STRUCT_MNTENT struct mnttab -#define STRUCT_SETMNTENT FILE * -#define GETMNTENT(file, var) (getmntent(file, &var) == nullptr) -#define MOUNTPOINT(var) var.mnt_mountp -#define MOUNTTYPE(var) var.mnt_fstype -#define MOUNTOPTIONS(var) var.mnt_mntopts -#define FSNAME(var) var.mnt_special -#endif - -Q_GLOBAL_STATIC(QThreadStorage, globalFstabCache) - -Solid::Backends::Fstab::FstabHandling::FstabHandling() - : m_fstabCacheValid(false), - m_mtabCacheValid(false) -{ } - -bool _k_isFstabNetworkFileSystem(const QString &fstype, const QString &devName) -{ - if (fstype == "nfs" - || fstype == "nfs4" - || fstype == "smbfs" - || fstype == "cifs" - || devName.startsWith(QLatin1String("//"))) { - return true; - } - return false; -} - -bool _k_isFstabSupportedLocalFileSystem(const QString &fstype) -{ - if (fstype == "fuse.encfs" || - fstype == "fuse.cryfs" || - fstype == "overlay") { - return true; - } - return false; -} - -QString _k_deviceNameForMountpoint(const QString &source, const QString &fstype, - const QString &mountpoint) -{ - if (fstype.startsWith("fuse.") || - fstype == QLatin1String("overlay")) { - return fstype + mountpoint; - } - return source; -} - -void Solid::Backends::Fstab::FstabHandling::_k_updateFstabMountPointsCache() -{ - if (globalFstabCache->localData().m_fstabCacheValid) { - return; - } - - globalFstabCache->localData().m_fstabCache.clear(); - globalFstabCache->localData().m_fstabOptionsCache.clear(); - -#if HAVE_SETMNTENT - - FILE *fstab; - if ((fstab = setmntent(FSTAB, "r")) == nullptr) { - return; - } - - struct mntent *fe; - while ((fe = getmntent(fstab)) != nullptr) { - const QString fsname = QFile::decodeName(fe->mnt_fsname); - const QString fstype = QFile::decodeName(fe->mnt_type); - if (_k_isFstabNetworkFileSystem(fstype, fsname) || - _k_isFstabSupportedLocalFileSystem(fstype)) { - const QString mountpoint = QFile::decodeName(fe->mnt_dir); - const QString device = _k_deviceNameForMountpoint(fsname, fstype, mountpoint); - QStringList options = QFile::decodeName(fe->mnt_opts).split(QLatin1Char(',')); - - globalFstabCache->localData().m_fstabCache.insert(device, mountpoint); - globalFstabCache->localData().m_fstabFstypeCache.insert(device, fstype); - while (!options.isEmpty()) { - globalFstabCache->localData().m_fstabOptionsCache.insert(device, options.takeFirst()); - } - } - } - - endmntent(fstab); - -#else - - QFile fstab(FSTAB); - if (!fstab.open(QIODevice::ReadOnly)) { - return; - } - - QTextStream stream(&fstab); - QString line; - - while (!stream.atEnd()) { - line = stream.readLine().simplified(); - if (line.isEmpty() || line.startsWith('#')) { - continue; - } - - // not empty or commented out by '#' - const QStringList items = line.split(' '); - -#ifdef Q_OS_SOLARIS - if (items.count() < 5) { - continue; - } -#else - if (items.count() < 4) { - continue; - } -#endif - //prevent accessing a blocking directory - if (_k_isFstabNetworkFileSystem(items.at(2), items.at(0)) || - _k_isFstabSupportedLocalFileSystem(items.at(2))) { - const QString device = items.at(0); - const QString mountpoint = items.at(1); - - globalFstabCache->localData().m_fstabCache.insert(device, mountpoint); - } - } - - fstab.close(); -#endif - globalFstabCache->localData().m_fstabCacheValid = true; -} - -QStringList Solid::Backends::Fstab::FstabHandling::deviceList() -{ - _k_updateFstabMountPointsCache(); - _k_updateMtabMountPointsCache(); - - QStringList devices = globalFstabCache->localData().m_mtabCache.keys(); - - // Ensure that regardless an fstab device ends with a slash - // it will match its eventual mounted device regardless whether or not its path - // ends with a slash - for (auto it = globalFstabCache->localData().m_fstabCache.constBegin(), - end = globalFstabCache->localData().m_fstabCache.constEnd(); - it != end; ++it) { - - auto device = it.key(); - // the device is already known - if (devices.contains(device)) { - continue; - } - - // deviceName will or won't end with / depending if device ended with one - QString deviceName = device; - if (deviceName.endsWith(QLatin1Char('/'))) { - deviceName.chop(1); - } else { - deviceName.append(QLatin1Char('/')); - } - if (!devices.contains(deviceName)) { - devices.append(device); - } - } - return devices; -} - -QStringList Solid::Backends::Fstab::FstabHandling::mountPoints(const QString &device) -{ - _k_updateFstabMountPointsCache(); - _k_updateMtabMountPointsCache(); - - QStringList mountpoints = globalFstabCache->localData().m_fstabCache.values(device); - mountpoints += globalFstabCache->localData().m_mtabCache.values(device); - mountpoints.removeDuplicates(); - return mountpoints; -} - -QStringList Solid::Backends::Fstab::FstabHandling::options(const QString &device) -{ - _k_updateFstabMountPointsCache(); - - QStringList options = globalFstabCache->localData().m_fstabOptionsCache.values(device); - return options; -} - -QString Solid::Backends::Fstab::FstabHandling::fstype(const QString &device) -{ - _k_updateFstabMountPointsCache(); - - return globalFstabCache->localData().m_fstabFstypeCache.value(device); -} - -bool Solid::Backends::Fstab::FstabHandling::callSystemCommand(const QString &commandName, const QStringList &args, - const QObject *receiver, std::function callback) -{ - QStringList env = QProcess::systemEnvironment(); - env.replaceInStrings( - QRegularExpression(QStringLiteral("^PATH=(.*)"), QRegularExpression::CaseInsensitiveOption), - QStringLiteral("PATH=/sbin:/bin:/usr/sbin/:/usr/bin")); - - QProcess *process = new QProcess(); - - QObject::connect(process, static_cast(&QProcess::finished), receiver, - [process, callback](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitCode); - Q_UNUSED(exitStatus); - callback(process); - process->deleteLater(); - }); - - process->setEnvironment(env); - process->start(commandName, args); - - if (process->waitForStarted()) { - return true; - } - - delete process; - return false; -} - -void Solid::Backends::Fstab::FstabHandling::_k_updateMtabMountPointsCache() -{ - if (globalFstabCache->localData().m_mtabCacheValid) { - return; - } - - globalFstabCache->localData().m_mtabCache.clear(); - -#if HAVE_GETMNTINFO - -#if GETMNTINFO_USES_STATVFS - struct statvfs *mounted; -#else - struct statfs *mounted; -#endif - - int num_fs = getmntinfo(&mounted, MNT_NOWAIT); - - for (int i = 0; i < num_fs; i++) { - QString type = QFile::decodeName(mounted[i].f_fstypename); - if (_k_isFstabNetworkFileSystem(type, QString()) || - _k_isFstabSupportedLocalFileSystem(type)) { - const QString fsname = QFile::decodeName(mounted[i].f_mntfromname); - const QString mountpoint = QFile::decodeName(mounted[i].f_mntonname); - const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint); - globalFstabCache->localData().m_mtabCache.insert(device, mountpoint); - globalFstabCache->localData().m_fstabFstypeCache.insert(device, type); - } - } - -#else - STRUCT_SETMNTENT mnttab; - if ((mnttab = SETMNTENT(MNTTAB, "r")) == nullptr) { - return; - } - - STRUCT_MNTENT fe; - while (GETMNTENT(mnttab, fe)) { - QString type = QFile::decodeName(MOUNTTYPE(fe)); - if (_k_isFstabNetworkFileSystem(type, QString()) || - _k_isFstabSupportedLocalFileSystem(type)) { - const QString fsname = QFile::decodeName(FSNAME(fe)); - const QString mountpoint = QFile::decodeName(MOUNTPOINT(fe)); - const QString device = _k_deviceNameForMountpoint(fsname, type, mountpoint); - globalFstabCache->localData().m_mtabCache.insert(device, mountpoint); - globalFstabCache->localData().m_fstabFstypeCache.insert(device, type); - } - } - ENDMNTENT(mnttab); -#endif - - globalFstabCache->localData().m_mtabCacheValid = true; -} - -QStringList Solid::Backends::Fstab::FstabHandling::currentMountPoints(const QString &device) -{ - _k_updateMtabMountPointsCache(); - return globalFstabCache->localData().m_mtabCache.values(device); -} - -void Solid::Backends::Fstab::FstabHandling::flushMtabCache() -{ - globalFstabCache->localData().m_mtabCacheValid = false; -} - -void Solid::Backends::Fstab::FstabHandling::flushFstabCache() -{ - globalFstabCache->localData().m_fstabCacheValid = false; -} diff --git a/src/solid/devices/backends/fstab/fstabmanager.h b/src/solid/devices/backends/fstab/fstabmanager.h --- a/src/solid/devices/backends/fstab/fstabmanager.h +++ b/src/solid/devices/backends/fstab/fstabmanager.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -49,13 +50,13 @@ QObject *createDevice(const QString &udi) override; Q_SIGNALS: - void mtabChanged(const QString &device); + void filesystemEntryChanged(const FilesystemEntry &fsEntry); private Q_SLOTS: - void onFstabChanged(); - void onMtabChanged(); + void onFilesystemTableChanged(); private: + SystemFilesystemTable m_systemFilesystemTable; QSet m_supportedInterfaces; QStringList m_deviceList; void _k_updateDeviceList(); diff --git a/src/solid/devices/backends/fstab/fstabmanager.cpp b/src/solid/devices/backends/fstab/fstabmanager.cpp --- a/src/solid/devices/backends/fstab/fstabmanager.cpp +++ b/src/solid/devices/backends/fstab/fstabmanager.cpp @@ -20,10 +20,8 @@ #include "fstabmanager.h" #include "fstabdevice.h" -#include "fstabhandling.h" #include "../shared/rootdevice.h" #include "fstabservice.h" -#include "fstabwatcher.h" using namespace Solid::Backends::Fstab; using namespace Solid::Backends::Shared; @@ -34,10 +32,9 @@ m_supportedInterfaces << Solid::DeviceInterface::StorageAccess; m_supportedInterfaces << Solid::DeviceInterface::NetworkShare; - m_deviceList = FstabHandling::deviceList(); + m_deviceList = m_systemFilesystemTable.filesystemIds(); - connect(FstabWatcher::instance(), SIGNAL(fstabChanged()), this, SLOT(onFstabChanged())); - connect(FstabWatcher::instance(), SIGNAL(mtabChanged()), this, SLOT(onMtabChanged())); + connect(&m_systemFilesystemTable, SIGNAL(tableChanged()), this, SLOT(onFilesystemTableChanged())); } QString FstabManager::udiPrefix() const @@ -102,26 +99,20 @@ } else { // global device manager makes sure udi starts with udi prefix + '/' QString internalName = udi.mid(udiPrefix().length() + 1, -1); - if (!m_deviceList.contains(internalName)) { + FilesystemEntry const *fsEntry = m_systemFilesystemTable.filesystemEntry(internalName); + if (!fsEntry) { return nullptr; } - - QObject *device = new FstabDevice(udi); - connect(this, SIGNAL(mtabChanged(QString)), device, SLOT(onMtabChanged(QString))); + QObject *device = new FstabDevice(udi, *fsEntry, m_systemFilesystemTable); + connect(this, SIGNAL(filesystemEntryChanged(const FilesystemEntry&)), device, SLOT(onFilesystemEntryChanged(const FilesystemEntry&))); return device; } } -void FstabManager::onFstabChanged() -{ - FstabHandling::flushFstabCache(); - _k_updateDeviceList(); -} - void FstabManager::_k_updateDeviceList() { - QStringList deviceList = FstabHandling::deviceList(); + QStringList deviceList = m_systemFilesystemTable.filesystemIds(); QSet newlist = deviceList.toSet(); QSet oldlist = m_deviceList.toSet(); @@ -138,23 +129,18 @@ } m_deviceList = deviceList; - - Q_FOREACH (const QString &device, newlist) { - if (!oldlist.contains(device)) { - emit deviceAdded(udiPrefix() + "/" + device); - } - } } -void FstabManager::onMtabChanged() +void FstabManager::onFilesystemTableChanged() { - FstabHandling::flushMtabCache(); - - _k_updateDeviceList(); // devicelist is union of mtab and fstab - + _k_updateDeviceList(); Q_FOREACH (const QString &device, m_deviceList) { + auto fsEntry = m_systemFilesystemTable.filesystemEntry(device); + if (!fsEntry) { + continue; + } // notify storageaccess objects via device ... - emit mtabChanged(device); + emit filesystemEntryChanged(*fsEntry); } } diff --git a/src/solid/devices/backends/fstab/fstabstorageaccess.h b/src/solid/devices/backends/fstab/fstabstorageaccess.h --- a/src/solid/devices/backends/fstab/fstabstorageaccess.h +++ b/src/solid/devices/backends/fstab/fstabstorageaccess.h @@ -31,6 +31,7 @@ { namespace Fstab { +class FilesystemEntry; class FstabDevice; class FstabStorageAccess : public QObject, public Solid::Ifaces::StorageAccess { @@ -67,7 +68,7 @@ void teardownRequested(const QString &udi) override; private Q_SLOTS: - void onMtabChanged(const QString &device); + void onFilesystemEntryChanged(const FilesystemEntry &fsEntry); void connectDBusSignals(); void slotSetupRequested(); diff --git a/src/solid/devices/backends/fstab/fstabstorageaccess.cpp b/src/solid/devices/backends/fstab/fstabstorageaccess.cpp --- a/src/solid/devices/backends/fstab/fstabstorageaccess.cpp +++ b/src/solid/devices/backends/fstab/fstabstorageaccess.cpp @@ -19,35 +19,27 @@ */ #include "fstabstorageaccess.h" -#include "fstabwatcher.h" +#include #include -#include #include +#include #include #include #include -#define MTAB "/etc/mtab" - using namespace Solid::Backends::Fstab; FstabStorageAccess::FstabStorageAccess(Solid::Backends::Fstab::FstabDevice *device) : QObject(device), m_fstabDevice(device) { - QStringList currentMountPoints = FstabHandling::currentMountPoints(device->device()); - if (currentMountPoints.isEmpty()) { - QStringList mountPoints = FstabHandling::mountPoints(device->device()); - m_filePath = mountPoints.isEmpty() ? QString() : mountPoints.first(); - m_isAccessible = false; - } else { - m_filePath = currentMountPoints.first(); - m_isAccessible = true; - } - m_isIgnored = FstabHandling::options(device->device()).contains(QLatin1String("x-gvfs-hide")); + const FilesystemEntry &fsEntry = m_fstabDevice->filesystemEntry(); + m_filePath = fsEntry.mountPath(); + m_isAccessible = m_fstabDevice->systemFilesystemTable().isMounted(fsEntry); + m_isIgnored = fsEntry.mountOptions().contains(QLatin1String("x-gvfs-hide")); - connect(device, SIGNAL(mtabChanged(QString)), this, SLOT(onMtabChanged(QString))); + connect(device, SIGNAL(filesystemEntryChanged(const FilesystemEntry&)), this, SLOT(onFilesystemEntryChanged(const FilesystemEntry&))); QTimer::singleShot(0, this, SLOT(connectDBusSignals())); } @@ -92,7 +84,7 @@ return false; } m_fstabDevice->broadcastActionRequested("setup"); - return FstabHandling::callSystemCommand("mount", {filePath()}, this, [this](QProcess *process) { + return CallSystemCommand("mount", {filePath()}, this, [this](QProcess *process) { if (process->exitCode() == 0) { m_fstabDevice->broadcastActionDone("setup", Solid::NoError, QString()); } else { @@ -112,7 +104,7 @@ return false; } m_fstabDevice->broadcastActionRequested("teardown"); - return FstabHandling::callSystemCommand("umount", {filePath()}, this, [this](QProcess *process) { + return CallSystemCommand("umount", {filePath()}, this, [this](QProcess *process) { if (process->exitCode() == 0) { m_fstabDevice->broadcastActionDone("teardown", Solid::NoError, QString()); } else { @@ -136,18 +128,9 @@ emit teardownDone(static_cast(error), errorString, m_fstabDevice->udi()); } -void FstabStorageAccess::onMtabChanged(const QString &device) +void FstabStorageAccess::onFilesystemEntryChanged(const FilesystemEntry &fsEntry) { - QStringList currentMountPoints = FstabHandling::currentMountPoints(device); - if (currentMountPoints.isEmpty()) { - // device umounted - m_filePath = FstabHandling::mountPoints(device).first(); - m_isAccessible = false; - emit accessibilityChanged(false, QString(FSTAB_UDI_PREFIX) + "/" + device); - } else { - // device added - m_filePath = currentMountPoints.first(); - m_isAccessible = true; - emit accessibilityChanged(true, QString(FSTAB_UDI_PREFIX) + "/" + device); - } + m_filePath = fsEntry.mountPath(); + m_isAccessible = m_fstabDevice->systemFilesystemTable().isMounted(fsEntry); + emit accessibilityChanged(m_isAccessible, QString(FSTAB_UDI_PREFIX) + "/" + fsEntry.id()); } diff --git a/src/solid/devices/backends/fstab/fstabwatcher.h b/src/solid/devices/backends/fstab/fstabwatcher.h deleted file mode 100644 --- a/src/solid/devices/backends/fstab/fstabwatcher.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2010 Mario Bensi - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . -*/ - -#ifndef SOLID_BACKENDS_FSTAB_WATCHER_H -#define SOLID_BACKENDS_FSTAB_WATCHER_H - -#include - -class QFileSystemWatcher; -class QFile; -class QSocketNotifier; - -namespace Solid -{ -namespace Backends -{ -namespace Fstab -{ - -class FstabWatcher : public QObject -{ - Q_OBJECT -public: - FstabWatcher(); - virtual ~FstabWatcher(); - - static FstabWatcher *instance(); - -Q_SIGNALS: - void mtabChanged(); - void fstabChanged(); - -private Q_SLOTS: - void onFileChanged(const QString &path); - void orphanFileSystemWatcher(); - -private: - bool m_isRoutineInstalled; - QFileSystemWatcher *m_fileSystemWatcher; - QSocketNotifier *m_mtabSocketNotifier; - QFile *m_mtabFile; -}; -} -} -} -#endif // SOLID_BACKENDS_FSTAB_WATCHER_H - diff --git a/src/solid/devices/backends/fstab/fstabwatcher.cpp b/src/solid/devices/backends/fstab/fstabwatcher.cpp deleted file mode 100644 --- a/src/solid/devices/backends/fstab/fstabwatcher.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright 2010 Mario Bensi - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . -*/ - -#include "fstabwatcher.h" -#include "soliddefs_p.h" - -#include -#include -#include -#include - -using namespace Solid::Backends::Fstab; - -Q_GLOBAL_STATIC(FstabWatcher, globalFstabWatcher) - -#define MTAB "/etc/mtab" -#ifdef Q_OS_SOLARIS -#define FSTAB "/etc/vfstab" -#else -#define FSTAB "/etc/fstab" -#endif - -FstabWatcher::FstabWatcher() - : m_isRoutineInstalled(false) - , m_fileSystemWatcher(new QFileSystemWatcher(this)) -{ - if (qApp) { - connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(orphanFileSystemWatcher())); - } - - m_mtabFile = new QFile(MTAB, this); - if (m_mtabFile && m_mtabFile->symLinkTarget().startsWith("/proc/") - && m_mtabFile->open(QIODevice::ReadOnly)) { - - m_mtabSocketNotifier = new QSocketNotifier(m_mtabFile->handle(), - QSocketNotifier::Exception, this); - connect(m_mtabSocketNotifier, - SIGNAL(activated(int)), this, SIGNAL(mtabChanged())); - } else { - m_fileSystemWatcher->addPath(MTAB); - } - - m_fileSystemWatcher->addPath(FSTAB); - connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onFileChanged(QString))); -} - -FstabWatcher::~FstabWatcher() -{ - // The QFileSystemWatcher doesn't work correctly in a singleton - // The solution so far was to destroy the QFileSystemWatcher when the application quits - // But we have some crash with this solution. - // For the moment to workaround the problem, we detach the QFileSystemWatcher from the parent - // effectively leaking it on purpose. - -#if 0 - //qRemovePostRoutine(globalFstabWatcher.destroy); -#else - m_fileSystemWatcher->setParent(nullptr); -#endif -} - -void FstabWatcher::orphanFileSystemWatcher() -{ - m_fileSystemWatcher->setParent(nullptr); -} - -FstabWatcher *FstabWatcher::instance() -{ -#if 0 - FstabWatcher *fstabWatcher = globalFstabWatcher; - - if (fstabWatcher && !fstabWatcher->m_isRoutineInstalled) { - qAddPostRoutine(globalFstabWatcher.destroy); - fstabWatcher->m_isRoutineInstalled = true; - } - return fstabWatcher; -#else - return globalFstabWatcher; -#endif -} - -void FstabWatcher::onFileChanged(const QString &path) -{ - if (path == MTAB) { - emit mtabChanged(); - if (!m_fileSystemWatcher->files().contains(MTAB)) { - m_fileSystemWatcher->addPath(MTAB); - } - } - if (path == FSTAB) { - emit fstabChanged(); - if (!m_fileSystemWatcher->files().contains(FSTAB)) { - m_fileSystemWatcher->addPath(FSTAB); - } - } -} - diff --git a/src/solid/devices/backends/fstab/system_filesystem_table.h b/src/solid/devices/backends/fstab/system_filesystem_table.h new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/system_filesystem_table.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SOLID_BACKENDS_FSTAB_SYSTEM_FILESYSTEM_TABLE_H +#define SOLID_BACKENDS_FSTAB_SYSTEM_FILESYSTEM_TABLE_H + +#include "filesystem_table_parser.h" +#include "filesystem_table_watcher.h" + +#include +#include +#include +#include + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +// The Default Filter Function that filters the filesystems supported by the Fstab Solid Backend +extern const FilesystemTableParser::FilterFunction DefaultFilterFunction; + +// Class that represents the available mountable filesystems and the currently mounted filesystem of the system +// This class provides an interface that sums up the fstab file and the mtab file. It also monitors the two files +// and notifies if they change +class SystemFilesystemTable : public QObject +{ + Q_OBJECT +public: + SystemFilesystemTable(const QString& fstabPath = QString(), const QString& mtabPath = QString(), + FilesystemTableParser::FilterFunction filterFunction = DefaultFilterFunction); + virtual ~SystemFilesystemTable(); + QStringList filesystemIds() const; + const FilesystemEntry* filesystemEntry(const QString &id) const; + bool isMounted(const FilesystemEntry &fsEntry) const; +Q_SIGNALS: + void tableChanged(); +private Q_SLOTS: + void fstabChanged(); + void mtabChanged(); +private: + const QString m_fstabPath; + const QString m_mtabPath; + const FilesystemTableParser::FilterFunction m_filterFunction; + FilesystemTableWatcher m_fstabWatcher; + FilesystemTableWatcher m_mtabWatcher; + // The fstab entries represent the filesystems that the system can mount + QList m_fstabEntries; + // The mtab entries represent the filesystems that the system has currently mounted + QList m_mtabEntries; +}; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid + +#endif diff --git a/src/solid/devices/backends/fstab/system_filesystem_table.cpp b/src/solid/devices/backends/fstab/system_filesystem_table.cpp new file mode 100644 --- /dev/null +++ b/src/solid/devices/backends/fstab/system_filesystem_table.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hallas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "system_filesystem_table.h" +#include "filesystem_entry.h" + +namespace +{ + +QString GetFstabPath(const QString& fstabPath) +{ + if (!fstabPath.isEmpty()) { + return fstabPath; + } + return Solid::Backends::Fstab::FilesystemTableParser::systemFstabPath(); +} + +QString GetMtabPath(const QString& mtabPath) +{ + if (!mtabPath.isEmpty()) { + return mtabPath; + } + return Solid::Backends::Fstab::FilesystemTableParser::systemMtabPath(); +} + +} + +namespace Solid +{ +namespace Backends +{ +namespace Fstab +{ + +SystemFilesystemTable::SystemFilesystemTable(const QString& fstabPath, const QString& mtabPath, + FilesystemTableParser::FilterFunction filterFunction) + : m_fstabPath(GetFstabPath(fstabPath)) + , m_mtabPath(GetMtabPath(mtabPath)) + , m_filterFunction(filterFunction) + , m_fstabWatcher(m_fstabPath) + , m_mtabWatcher(m_mtabPath) + , m_fstabEntries(FilesystemTableParser::parseFilesystemTable(m_fstabPath, m_filterFunction)) + , m_mtabEntries(FilesystemTableParser::parseFilesystemTable(m_mtabPath, m_filterFunction)) +{ + connect(&m_fstabWatcher, SIGNAL(tableChanged()), this, SLOT(fstabChanged())); + connect(&m_mtabWatcher, SIGNAL(tableChanged()), this, SLOT(mtabChanged())); +} + +SystemFilesystemTable::~SystemFilesystemTable() = default; + +QStringList SystemFilesystemTable::filesystemIds() const +{ + QStringList ret; + for (const auto& filesystemEntry : m_fstabEntries) { + ret += filesystemEntry.id(); + } + for (const auto& filesystemEntry : m_mtabEntries) { + ret += filesystemEntry.id(); + } + ret.removeDuplicates(); + return ret; +} + +const FilesystemEntry* SystemFilesystemTable::filesystemEntry(const QString& id) const +{ + for (const auto& filesystemEntry : m_mtabEntries) { + if (filesystemEntry.id() == id) { + return &filesystemEntry; + } + } + for (const auto& filesystemEntry : m_fstabEntries) { + if (filesystemEntry.id() == id) { + return &filesystemEntry; + } + } + return nullptr; +} + +bool SystemFilesystemTable::isMounted(const FilesystemEntry& fsEntry) const +{ + for (const auto& filesystemEntry : m_mtabEntries) { + if (filesystemEntry.id() == fsEntry.id()) { + return true; + } + } + return false; +} + +void SystemFilesystemTable::fstabChanged() +{ + m_fstabEntries = FilesystemTableParser::parseFilesystemTable(m_fstabPath, m_filterFunction); + emit tableChanged(); +} + +void SystemFilesystemTable::mtabChanged() +{ + m_mtabEntries = FilesystemTableParser::parseFilesystemTable(m_mtabPath, m_filterFunction); + emit tableChanged(); +} + +const FilesystemTableParser::FilterFunction DefaultFilterFunction = + [](const QString& device, const QString& type) -> FilesystemTableParser::FilterReturn +{ + if (type == "nfs" || + type == "nfs4" || + type == "smbfs"|| + type == "cifs"|| + device.startsWith(QLatin1String("//")) || + type == "fuse.encfs" || + type == "fuse.cryfs" || + type == "overlay") { + return FilesystemTableParser::FilterReturn::Keep; + } + return FilesystemTableParser::FilterReturn::Discard; +}; + +} // namespace Fstab +} // namespace Backends +} // namespace Solid