1 /*
2     SPDX-FileCopyrightText: 2012-2018 Andrius Štikonas <andrius@stikonas.eu>
3     SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net>
4     SPDX-FileCopyrightText: 2020 Arnaud Ferraris <arnaud.ferraris@collabora.com>
5     SPDX-FileCopyrightText: 2020 Gaël PORTAY <gael.portay@collabora.com>
6 
7     SPDX-License-Identifier: GPL-3.0-or-later
8 */
9 
10 #include "fs/nilfs2.h"
11 
12 #include "util/externalcommand.h"
13 #include "util/capacity.h"
14 #include "util/report.h"
15 
16 #include <cmath>
17 
18 #include <QRegularExpression>
19 #include <QString>
20 #include <QTemporaryDir>
21 #include <QUuid>
22 
23 #include <KLocalizedString>
24 
25 namespace FS
26 {
27 FileSystem::CommandSupportType nilfs2::m_GetUsed = FileSystem::cmdSupportNone;
28 FileSystem::CommandSupportType nilfs2::m_GetLabel = FileSystem::cmdSupportNone;
29 FileSystem::CommandSupportType nilfs2::m_Create = FileSystem::cmdSupportNone;
30 FileSystem::CommandSupportType nilfs2::m_Grow = FileSystem::cmdSupportNone;
31 FileSystem::CommandSupportType nilfs2::m_Shrink = FileSystem::cmdSupportNone;
32 FileSystem::CommandSupportType nilfs2::m_Move = FileSystem::cmdSupportNone;
33 FileSystem::CommandSupportType nilfs2::m_Check = FileSystem::cmdSupportNone;
34 FileSystem::CommandSupportType nilfs2::m_Copy = FileSystem::cmdSupportNone;
35 FileSystem::CommandSupportType nilfs2::m_Backup = FileSystem::cmdSupportNone;
36 FileSystem::CommandSupportType nilfs2::m_SetLabel = FileSystem::cmdSupportNone;
37 FileSystem::CommandSupportType nilfs2::m_UpdateUUID = FileSystem::cmdSupportNone;
38 FileSystem::CommandSupportType nilfs2::m_GetUUID = FileSystem::cmdSupportNone;
39 
nilfs2(qint64 firstsector,qint64 lastsector,qint64 sectorsused,const QString & label,const QVariantMap & features)40 nilfs2::nilfs2(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label, const QVariantMap& features) :
41     FileSystem(firstsector, lastsector, sectorsused, label, features, FileSystem::Type::Nilfs2)
42 {
43 }
44 
init()45 void nilfs2::init()
46 {
47     m_Create = findExternal(QStringLiteral("mkfs.nilfs2")) ? cmdSupportFileSystem : cmdSupportNone;
48     m_Check = /*findExternal(QStringLiteral("fsck.nilfs2")) ? cmdSupportFileSystem : */cmdSupportNone;
49 
50     m_GetLabel = cmdSupportCore;
51     m_SetLabel = findExternal(QStringLiteral("nilfs-tune")) ? cmdSupportFileSystem : cmdSupportNone;
52     m_UpdateUUID = findExternal(QStringLiteral("nilfs-tune")) ? cmdSupportFileSystem : cmdSupportNone;
53 
54     m_Grow = findExternal(QStringLiteral("nilfs-resize")) ? cmdSupportFileSystem : cmdSupportNone;
55     m_GetUsed = findExternal(QStringLiteral("nilfs-tune")) ? cmdSupportFileSystem : cmdSupportNone;
56     m_Shrink = (m_Grow != cmdSupportNone && m_GetUsed != cmdSupportNone) ? cmdSupportFileSystem : cmdSupportNone;
57 
58     m_Copy =/* (m_Check != cmdSupportNone) ?*/ cmdSupportCore /*: cmdSupportNone*/;
59     m_Move =/* (m_Check != cmdSupportNone) ?*/ cmdSupportCore /*: cmdSupportNone*/;
60 
61     m_GetLabel = cmdSupportCore;
62     m_Backup = cmdSupportCore;
63     m_GetUUID = cmdSupportCore;
64 }
65 
supportToolFound() const66 bool nilfs2::supportToolFound() const
67 {
68     return
69         m_GetUsed != cmdSupportNone &&
70         m_GetLabel != cmdSupportNone &&
71         m_SetLabel != cmdSupportNone &&
72         m_Create != cmdSupportNone &&
73 //         m_Check != cmdSupportNone &&
74         m_UpdateUUID != cmdSupportNone &&
75         m_Grow != cmdSupportNone &&
76         m_Shrink != cmdSupportNone &&
77         m_Copy != cmdSupportNone &&
78         m_Move != cmdSupportNone &&
79         m_Backup != cmdSupportNone &&
80         m_GetUUID != cmdSupportNone;
81 }
82 
supportToolName() const83 FileSystem::SupportTool nilfs2::supportToolName() const
84 {
85     return SupportTool(QStringLiteral("nilfs2-utils"), QUrl(QStringLiteral("https://github.com/nilfs-dev/nilfs-utils")));
86 }
87 
minCapacity() const88 qint64 nilfs2::minCapacity() const
89 {
90     return 128 * Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::MiB);
91 }
92 
maxCapacity() const93 qint64 nilfs2::maxCapacity() const
94 {
95     return Capacity::unitFactor(Capacity::Unit::Byte, Capacity::Unit::EiB);
96 }
97 
maxLabelLength() const98 int nilfs2::maxLabelLength() const
99 {
100     return 80;
101 }
102 
check(Report & report,const QString & deviceNode) const103 bool nilfs2::check(Report& report, const QString& deviceNode) const
104 {
105     ExternalCommand cmd(report, QStringLiteral("fsck.nilfs2"), { deviceNode });
106     return cmd.run(-1) && cmd.exitCode() == 0;
107 }
108 
create(Report & report,const QString & deviceNode)109 bool nilfs2::create(Report& report, const QString& deviceNode)
110 {
111     ExternalCommand cmd(report, QStringLiteral("mkfs.nilfs2"), { QStringLiteral("-f"), deviceNode });
112     return cmd.run(-1) && cmd.exitCode() == 0;
113 }
114 
readUsedCapacity(const QString & deviceNode) const115 qint64 nilfs2::readUsedCapacity(const QString& deviceNode) const
116 {
117     ExternalCommand cmd(QStringLiteral("nilfs-tune"), { QStringLiteral("-l"), deviceNode });
118 
119     if (cmd.run(-1) && cmd.exitCode() == 0) {
120         QRegularExpression re(QStringLiteral("Block size:\\s+(\\d+)"));
121         QRegularExpressionMatch reBlockSize = re.match(cmd.output());
122         re.setPattern(QStringLiteral("Device size:\\s+(\\d+)"));
123         QRegularExpressionMatch reDeviceSize = re.match(cmd.output());
124         re.setPattern(QStringLiteral("Free blocks count:\\s+(\\d+)"));
125         QRegularExpressionMatch reFreeBlocks = re.match(cmd.output());
126         if (reBlockSize.hasMatch() && reDeviceSize.hasMatch() && reFreeBlocks.hasMatch())
127             return reDeviceSize.captured(1).toLongLong() - reBlockSize.captured(1).toLongLong() * reFreeBlocks.captured(1).toLongLong();
128     }
129 
130     return -1;
131 }
132 
resize(Report & report,const QString & deviceNode,qint64 length) const133 bool nilfs2::resize(Report& report, const QString& deviceNode, qint64 length) const
134 {
135     QTemporaryDir tempDir;
136     if (!tempDir.isValid()) {
137         report.line() << xi18nc("@info:progress", "Resizing NILFS2 file system on partition <filename>%1</filename> failed: Could not create temp dir.", deviceNode);
138         return false;
139     }
140 
141     bool rval = false;
142 
143     ExternalCommand mountCmd(report, QStringLiteral("mount"), { QStringLiteral("--verbose"), QStringLiteral("--types"), QStringLiteral("nilfs2"), deviceNode, tempDir.path() });
144 
145     if (mountCmd.run(-1) && mountCmd.exitCode() == 0) {
146         ExternalCommand resizeCmd(report, QStringLiteral("nilfs-resize"), { QStringLiteral("--verbose"), QStringLiteral("--assume-yes"), deviceNode, QString::number(length) });
147 
148         if (resizeCmd.run(-1) && resizeCmd.exitCode() == 0)
149             rval = true;
150         else
151             report.line() << xi18nc("@info:progress", "Resizing NILFS2 file system on partition <filename>%1</filename> failed: NILFS2 file system resize failed.", deviceNode);
152 
153         ExternalCommand unmountCmd(report, QStringLiteral("umount"), { tempDir.path() });
154 
155         if (!unmountCmd.run(-1) && unmountCmd.exitCode() == 0)
156             report.line() << xi18nc("@info:progress", "<warning>Resizing NILFS2 file system on partition <filename>%1</filename>: Unmount failed.</warning>", deviceNode);
157     } else
158         report.line() << xi18nc("@info:progress", "Resizing NILFS2 file system on partition <filename>%1</filename> failed: Initial mount failed.", deviceNode);
159 
160     return rval;
161 }
162 
resizeOnline(Report & report,const QString & deviceNode,const QString &,qint64 length) const163 bool nilfs2::resizeOnline(Report& report, const QString& deviceNode, const QString&, qint64 length) const
164 {
165     ExternalCommand resizeCmd(report, QStringLiteral("nilfs-resize"), { QStringLiteral("--verbose"), QStringLiteral("--assume-yes"), deviceNode, QString::number(length) });
166 
167     if (resizeCmd.run(-1) && resizeCmd.exitCode() == 0)
168         return true;
169 
170     report.line() << xi18nc("@info:progress", "Resizing NILFS2 file system on partition <filename>%1</filename> failed: NILFS2 file system resize failed.", deviceNode);
171     return false;
172 }
173 
writeLabel(Report & report,const QString & deviceNode,const QString & newLabel)174 bool nilfs2::writeLabel(Report& report, const QString& deviceNode, const QString& newLabel)
175 {
176     ExternalCommand cmd(report, QStringLiteral("nilfs-tune"), { QStringLiteral("-l"), newLabel, deviceNode });
177     return cmd.run(-1) && cmd.exitCode() == 0;
178 }
179 
updateUUID(Report & report,const QString & deviceNode) const180 bool nilfs2::updateUUID(Report& report, const QString& deviceNode) const
181 {
182     QUuid uuid = QUuid::createUuid();
183     ExternalCommand cmd(report, QStringLiteral("nilfs-tune"), { QStringLiteral("-U"), uuid.toString(), deviceNode });
184     return cmd.run(-1) && cmd.exitCode() == 0;
185 }
186 }
187