1
/***************************************************************************
2
* Copyright (C) 2008 by Volker Lanz <vl@fidra.de> *
4
* This program is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License for more details. *
14
* You should have received a copy of the GNU General Public License *
15
* along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18
***************************************************************************/
23
#include "core/libparted.h"
24
#include "core/device.h"
25
#include "core/partition.h"
26
#include "core/partitiontable.h"
27
#include "core/operationstack.h"
29
#include "jobs/setpartflagsjob.h"
31
#include "fs/filesystem.h"
32
#include "fs/filesystemfactory.h"
34
#include "util/globallog.h"
37
#include <QStringList>
45
#include <parted/parted.h>
46
#include <sys/statvfs.h>
49
/** Callback to handle exceptions from libparted
50
@param e the libparted exception to handle
52
static PedExceptionOption pedExceptionHandler(PedException* e)
54
/** @todo These messages should end up in the Report, not in the GlobalLog. */
55
log(log::error) << i18nc("@info/plain", "LibParted Exception: %1", QString::fromLocal8Bit(e->message));
56
return PED_EXCEPTION_UNHANDLED;
59
/** Finds a device by UUID.
61
@return the device node the UUID links to
63
static QString findUuidDevice(const QString& s)
65
const QString filename = "/dev/disk/by-uuid/" + QString(s).remove("UUID=", Qt::CaseInsensitive);
66
return QFile::exists(filename) ? QFile::symLinkTarget(filename) : "";
69
/** Reads mountpoints from a file.
70
@param filename the name of the file to read mount points from
71
@param result reference to QMap where the result will be stored
73
static void readMountpoints(const QString& filename, QMap<QString, QStringList>& result)
76
if (!file.open(QIODevice::ReadOnly))
79
QByteArray line = file.readLine();
81
while(!line.isEmpty())
83
line = line.simplified();
85
if (!line.isEmpty() && (line[0] == '/' || line.startsWith("UUID=")))
87
QList<QByteArray> split = line.split(' ');
89
if (split.size() >= 2)
91
QString device = split[0];
93
if (device.startsWith("UUID=", Qt::CaseInsensitive))
94
device = findUuidDevice(device);
96
if (!device.isEmpty())
98
QString mountPoint = split[1].replace("\\040", " ");
100
if (QFile::exists(mountPoint) && result[device].indexOf(mountPoint) == -1)
101
result[device].append(mountPoint);
106
line = file.readLine();
110
/** Reads sectors used on a FileSystem using libparted functions.
111
@param pedDisk pointer to pedDisk where the Partition and its FileSystem are
112
@param p the Partition the FileSystem is on
113
@return the number of sectors used
115
static qint64 readSectorsUsedLibParted(PedDisk* pedDisk, const Partition& p)
121
PedPartition* pedPartition = ped_disk_get_partition_by_sector(pedDisk, p.firstSector());
125
PedFileSystem* pedFileSystem = ped_file_system_open(&pedPartition->geom);
129
if (PedConstraint* pedConstraint = ped_file_system_get_resize_constraint(pedFileSystem))
131
rval = pedConstraint->min_size;
132
ped_constraint_destroy(pedConstraint);
135
ped_file_system_close(pedFileSystem);
142
/** Reads the sectors used in a FileSystem and stores the result in the Partition's FileSystem object.
143
@param pedDisk pointer to pedDisk where the Partition and its FileSystem are
144
@param p the Partition the FileSystem is on
145
@param mountInfo map of mount points for the partition in question
147
static void readSectorsUsed(PedDisk* pedDisk, Partition& p, QMap<QString, QStringList>& mountInfo)
153
if (p.isMounted() && mountInfo[p.deviceNode()].size() > 0 && statvfs(mountInfo[p.deviceNode()][0].toLatin1(), &sfs) == 0)
154
p.fileSystem().setSectorsUsed((sfs.f_blocks - sfs.f_bfree) * sfs.f_bsize / p.sectorSize());
155
else if (p.fileSystem().supportGetUsed() == FileSystem::SupportExternal)
156
p.fileSystem().setSectorsUsed(p.fileSystem().readUsedCapacity(p.deviceNode()) / p.sectorSize());
157
else if (p.fileSystem().supportGetUsed() == FileSystem::SupportLibParted)
158
p.fileSystem().setSectorsUsed(readSectorsUsedLibParted(pedDisk, p));
161
/** Scans a Device for Partitions.
163
This function will scan a Device for all Partitions on it, detect the FileSystem for each Partition,
164
try to determine the FileSystem usage, read the FileSystem label and store it all in newly created
165
objects that are in the end added to the Device's PartitionTable.
167
@param pedDevice libparted pointer to the device
169
@param pedDisk libparted pointer to the disk label
170
@param mountInfo map of mount points
172
static void scanDevicePartitions(PedDevice* pedDevice, Device& d, PedDisk* pedDisk, QMap<QString, QStringList>& mountInfo)
176
Q_ASSERT(d.partitionTable());
178
PedPartition* pedPartition = NULL;
180
while ((pedPartition = ped_disk_next_partition(pedDisk, pedPartition)))
182
if (pedPartition->num < 1)
185
PartitionRole::Roles r = PartitionRole::None;
186
FileSystem::Type type = Job::detectFileSystem(pedDevice, pedPartition);
188
switch(pedPartition->type)
190
case PED_PARTITION_NORMAL:
191
r = PartitionRole::Primary;
194
case PED_PARTITION_EXTENDED:
195
r = PartitionRole::Extended;
196
type = FileSystem::Extended;
199
case PED_PARTITION_LOGICAL:
200
r = PartitionRole::Logical;
207
// Find an extended partition this partition is in.
208
PartitionNode* parent = d.partitionTable()->findPartitionBySector(pedPartition->geom.start, PartitionRole(PartitionRole::Extended));
210
// None found, so it's a primary in the device's partition table.
212
parent = d.partitionTable();
214
const QString node = pedDisk->dev->path + QString::number(pedPartition->num);
215
FileSystem* fs = FileSystemFactory::create(type, pedPartition->geom.start, pedPartition->geom.end);
217
Partition* part = new Partition(parent, d, PartitionRole(r), fs, pedPartition->geom.start, pedPartition->geom.end,
218
pedPartition->num, SetPartFlagsJob::availableFlags(pedPartition),
219
mountInfo[node], ped_partition_is_busy(pedPartition), SetPartFlagsJob::activeFlags(pedPartition));
221
readSectorsUsed(pedDisk, *part, mountInfo);
223
if (fs->supportGetLabel() == FileSystem::SupportExternal)
224
fs->setLabel(fs->readLabel(part->deviceNode()));
226
parent->append(part);
228
PartitionTable::isSnapped(d, *part);
231
d.partitionTable()->updateUnallocated(d);
233
ped_disk_destroy(pedDisk);
236
/** Destructs a LibParted object. */
237
LibParted::LibParted()
239
ped_exception_set_handler(pedExceptionHandler);
242
/** Scans for all available Devices on this computer.
244
This method tries to find all Devices on the computer and creates new Device instances for each of them. It then calls scanDevicePartitions() to find all Partitions and FileSystems on each Device and set those up.
246
The method will clear the list of operations and devices currently in the OperationStack given.
248
@param ostack the OperationStack where the devices will be created
250
void LibParted::scanDevices(OperationStack& ostack)
252
QMap<QString, QStringList> mountInfo;
254
readMountpoints("/proc/mounts", mountInfo);
255
readMountpoints("/etc/mtab", mountInfo);
256
readMountpoints("/etc/fstab", mountInfo);
258
ostack.clearOperations();
259
ostack.clearDevices();
261
ped_device_probe_all();
263
PedDevice* pedDevice = ped_device_get_next(NULL);
267
log(log::information) << i18nc("@info/plain", "Device found: %1", pedDevice->model);
269
Device* d = new Device(pedDevice->model, pedDevice->path, pedDevice->bios_geom.heads, pedDevice->bios_geom.sectors, pedDevice->bios_geom.cylinders, pedDevice->sector_size);
271
PedDisk* pedDisk = ped_disk_new(pedDevice);
275
d->setPartitionTable(new PartitionTable());
276
d->partitionTable()->setMaxPrimaries(ped_disk_get_max_primary_partition_count(pedDisk));
277
d->partitionTable()->setTypeName(pedDisk->type->name);
279
scanDevicePartitions(pedDevice, *d, pedDisk, mountInfo);
282
// add this device if either there is a valid partition table or it's not
283
// read only (read only devices without partition table are CD/DVD readers, writers etc)
284
if (pedDisk || !pedDevice->read_only)
287
pedDevice = ped_device_get_next(pedDevice);