source: calamares/trunk/fuentes/src/modules/partition/jobs/ClearMountsJob.cpp @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 17 months ago

sync with github

File size: 8.2 KB
Line 
1/* === This file is part of Calamares - <https://github.com/calamares> ===
2 *
3 *   Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
4 *
5 *   Calamares is free software: you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation, either version 3 of the License, or
8 *   (at your option) any later version.
9 *
10 *   Calamares is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 *   GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "ClearMountsJob.h"
20
21#include "core/PartitionInfo.h"
22#include "core/PartitionIterator.h"
23#include "utils/Logger.h"
24
25// KPMcore
26#include <kpmcore/core/device.h>
27#include <kpmcore/core/partition.h>
28#include <kpmcore/util/report.h>
29
30#include <QDir>
31#include <QProcess>
32#include <QStringList>
33
34
35ClearMountsJob::ClearMountsJob( Device* device )
36    : Calamares::Job()
37    , m_device( device )
38{
39}
40
41
42QString
43ClearMountsJob::prettyName() const
44{
45    return tr( "Clear mounts for partitioning operations on %1" )
46            .arg( m_device->deviceNode() );
47}
48
49
50QString
51ClearMountsJob::prettyStatusMessage() const
52{
53    return tr( "Clearing mounts for partitioning operations on %1." )
54            .arg( m_device->deviceNode() );
55}
56
57
58Calamares::JobResult
59ClearMountsJob::exec()
60{
61    QStringList goodNews;
62
63    QString deviceName = m_device->deviceNode().split( '/' ).last();
64
65    QProcess process;
66    process.setProgram( "sh" );
67    process.setArguments( {
68                              "-c",
69                              QString( "echo $(awk '{print $4}' /proc/partitions | sed -e '/name/d' -e '/^$/d' -e '/[1-9]/!d' | grep %1)" )
70                                      .arg( deviceName )
71                          } );
72    process.start();
73    process.waitForFinished();
74
75    const QString partitions = process.readAllStandardOutput();
76    const QStringList partitionsList = partitions.simplified().split( ' ' );
77
78    // Build a list of partitions of type 82 (Linux swap / Solaris).
79    // We then need to clear them just in case they contain something resumable from a
80    // previous suspend-to-disk.
81    QStringList swapPartitions;
82    process.start( "sfdisk", { "-d", m_device->deviceNode() } );
83    process.waitForFinished();
84    // Sample output:
85    //    % sudo sfdisk -d /dev/sda
86    //    label: dos
87    //    label-id: 0x000ced89
88    //    device: /dev/sda
89    //    unit: sectors
90
91    //    /dev/sda1 : start=          63, size=    29329345, type=83, bootable
92    //    /dev/sda2 : start=    29331456, size=     2125824, type=82
93
94    swapPartitions = QString::fromLocal8Bit( process.readAllStandardOutput() )
95                        .split( '\n' );
96    swapPartitions = swapPartitions.filter( "type=82" );
97    for ( QStringList::iterator it = swapPartitions.begin();
98          it != swapPartitions.end(); ++it )
99    {
100        *it = (*it).simplified().split( ' ' ).first();
101    }
102
103    const QStringList cryptoDevices = getCryptoDevices();
104    for ( const QString &mapperPath : cryptoDevices )
105    {
106        tryUmount( mapperPath );
107        QString news = tryCryptoClose( mapperPath );
108        if ( !news.isEmpty() )
109            goodNews.append( news );
110    }
111
112    // First we umount all LVM logical volumes we can find
113    process.start( "lvscan", { "-a" } );
114    process.waitForFinished();
115    if ( process.exitCode() == 0 ) //means LVM2 tools are installed
116    {
117        const QStringList lvscanLines = QString::fromLocal8Bit( process.readAllStandardOutput() ).split( '\n' );
118        for ( const QString& lvscanLine : lvscanLines )
119        {
120            QString lvPath = lvscanLine.simplified().split( ' ' ).value( 1 ); //second column
121            lvPath = lvPath.replace( '\'', "" );
122
123            QString news = tryUmount( lvPath );
124            if ( !news.isEmpty() )
125                goodNews.append( news );
126        }
127    }
128    else
129        cWarning() << "this system does not seem to have LVM2 tools.";
130
131    // Then we go looking for volume groups that use this device for physical volumes
132    process.start( "pvdisplay", { "-C", "--noheadings" } );
133    process.waitForFinished();
134    if ( process.exitCode() == 0 ) //means LVM2 tools are installed
135    {
136        QString pvdisplayOutput = process.readAllStandardOutput();
137        if ( !pvdisplayOutput.simplified().isEmpty() ) //means there is at least one LVM PV
138        {
139            QSet< QString > vgSet;
140
141            const QStringList pvdisplayLines = pvdisplayOutput.split( '\n' );
142            for ( const QString& pvdisplayLine : pvdisplayLines )
143            {
144                QString pvPath = pvdisplayLine.simplified().split( ' ' ).value( 0 );
145                QString vgName = pvdisplayLine.simplified().split( ' ' ).value( 1 );
146                if ( !pvPath.contains( deviceName ) )
147                    continue;
148
149                vgSet.insert( vgName );
150            }
151
152            foreach ( const QString& vgName, vgSet )
153            {
154                process.start( "vgchange", { "-an", vgName } );
155                process.waitForFinished();
156                if ( process.exitCode() == 0 )
157                    goodNews.append( QString( "Successfully disabled volume group %1." ).arg( vgName ) );
158            }
159        }
160    }
161    else
162        cWarning() << "this system does not seem to have LVM2 tools.";
163
164    const QStringList cryptoDevices2 = getCryptoDevices();
165    for ( const QString &mapperPath : cryptoDevices2 )
166    {
167        tryUmount( mapperPath );
168        QString news = tryCryptoClose( mapperPath );
169        if ( !news.isEmpty() )
170            goodNews.append( news );
171    }
172
173    for ( const QString &p : partitionsList )
174    {
175        QString partPath = QString( "/dev/%1" ).arg( p );
176
177        QString news = tryUmount( partPath );
178        if ( !news.isEmpty() )
179            goodNews.append( news );
180    }
181
182    foreach ( QString p, swapPartitions )
183    {
184        QString news = tryClearSwap( p );
185        if ( !news.isEmpty() )
186            goodNews.append( news );
187    }
188
189    Calamares::JobResult ok = Calamares::JobResult::ok();
190    ok.setMessage( tr( "Cleared all mounts for %1" )
191                        .arg( m_device->deviceNode() ) );
192    ok.setDetails( goodNews.join( "\n" ) );
193
194    cDebug() << "ClearMountsJob finished. Here's what was done:\n" << goodNews.join( "\n" );
195
196    return ok;
197}
198
199
200QString
201ClearMountsJob::tryUmount( const QString& partPath )
202{
203    QProcess process;
204    process.start( "umount", { partPath } );
205    process.waitForFinished();
206    if ( process.exitCode() == 0 )
207        return QString( "Successfully unmounted %1." ).arg( partPath );
208
209    process.start( "swapoff", { partPath } );
210    process.waitForFinished();
211    if ( process.exitCode() == 0 )
212        return QString( "Successfully disabled swap %1." ).arg( partPath );
213
214    return QString();
215}
216
217
218QString
219ClearMountsJob::tryClearSwap( const QString& partPath )
220{
221    QProcess process;
222    process.start( "blkid", { "-s", "UUID", "-o", "value", partPath } );
223    process.waitForFinished();
224    QString swapPartUuid = QString::fromLocal8Bit( process.readAllStandardOutput() ).simplified();
225    if ( process.exitCode() != 0 ||
226         swapPartUuid.isEmpty() )
227        return QString();
228
229    process.start( "mkswap", { "-U", swapPartUuid, partPath } );
230    process.waitForFinished();
231    if ( process.exitCode() != 0 )
232        return QString();
233
234    return QString( "Successfully cleared swap %1." ).arg( partPath );
235}
236
237
238QString
239ClearMountsJob::tryCryptoClose( const QString& mapperPath )
240{
241    QProcess process;
242    process.start( "cryptsetup", { "close", mapperPath } );
243    process.waitForFinished();
244    if ( process.exitCode() == 0 )
245        return QString( "Successfully closed mapper device %1." ).arg( mapperPath );
246
247    return QString();
248}
249
250
251QStringList
252ClearMountsJob::getCryptoDevices() const
253{
254    QDir mapperDir( "/dev/mapper" );
255    const QFileInfoList fiList = mapperDir.entryInfoList( QDir::Files );
256    QStringList list;
257    QProcess process;
258    for ( const QFileInfo &fi : fiList )
259    {
260        if ( fi.baseName() == "control" )
261            continue;
262        list.append( fi.absoluteFilePath() );
263    }
264    return list;
265}
Note: See TracBrowser for help on using the repository browser.