File indexing completed on 2024-04-28 05:45:46

0001 /*
0002     SPDX-FileCopyrightText: 2010 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2014-2018 Andrius Štikonas <andrius@stikonas.eu>
0004     SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org>
0005     SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com>
0006     SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net>
0007 
0008     SPDX-License-Identifier: GPL-3.0-or-later
0009 */
0010 
0011 #include "core/partitionalignment.h"
0012 
0013 #include "core/partition.h"
0014 #include "core/partitiontable.h"
0015 #include "core/device.h"
0016 #include "core/diskdevice.h"
0017 
0018 #include "fs/filesystem.h"
0019 
0020 #include "util/globallog.h"
0021 
0022 #include <KLocalizedString>
0023 
0024 int PartitionAlignment::s_sectorAlignment = 2048;
0025 
0026 qint64 PartitionAlignment::firstDelta(const Device& d, const Partition& p, qint64 s)
0027 {
0028     if (d.partitionTable()->type() == PartitionTable::msdos) {
0029         const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
0030         if (p.roles().has(PartitionRole::Logical) && s == 2 * diskDevice.sectorsPerTrack())
0031             return (s - (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d);
0032 
0033         if (p.roles().has(PartitionRole::Logical) || s == diskDevice.sectorsPerTrack())
0034             return (s - diskDevice.sectorsPerTrack()) % sectorAlignment(d);
0035     }
0036 
0037     return s % sectorAlignment(d);
0038 }
0039 
0040 qint64 PartitionAlignment::lastDelta(const Device& d, const Partition&, qint64 s)
0041 {
0042     return (s + 1) % sectorAlignment(d);
0043 }
0044 
0045 bool PartitionAlignment::isLengthAligned(const Device& d, const Partition& p)
0046 {
0047     if (d.partitionTable()->type() == PartitionTable::msdos) {
0048         const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
0049         if (p.roles().has(PartitionRole::Logical) && p.firstSector() == 2 * diskDevice.sectorsPerTrack())
0050             return (p.length() + (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d) == 0;
0051 
0052         if (p.roles().has(PartitionRole::Logical) || p.firstSector() == diskDevice.sectorsPerTrack())
0053             return (p.length() + diskDevice.sectorsPerTrack()) % sectorAlignment(d) == 0;
0054     }
0055 
0056     return p.length() % sectorAlignment(d) == 0;
0057 }
0058 
0059 /** Checks if the Partition is properly aligned to the PartitionTable's alignment requirements.
0060 
0061     Will print warning messages to GlobalLog if the Partition's first sector is not aligned and
0062     another one if the last sector is not aligned. This can be suppressed with setting the @p quiet to
0063     true.
0064 
0065     @see alignPartition(), canAlignToSector()
0066 
0067     @param d device the partition is on
0068     @param p the partition to check
0069     @param quiet if true, will not print warning
0070     @return true if properly aligned
0071 */
0072 bool PartitionAlignment::isAligned(const Device& d, const Partition& p, bool quiet)
0073 {
0074     return isAligned(d, p, p.firstSector(), p.lastSector(), quiet);
0075 }
0076 
0077 bool PartitionAlignment::isAligned(const Device& d, const Partition& p, qint64 newFirst, qint64 newLast, bool quiet)
0078 {
0079     if (firstDelta(d, p, newFirst) && !quiet)
0080         Log(Log::Level::warning) << xi18nc("@info:status", "Partition <filename>%1</filename> is not properly aligned (first sector: %2, modulo: %3).", p.deviceNode(), newFirst, firstDelta(d, p, newFirst));
0081 
0082     if (lastDelta(d, p, newLast) && !quiet)
0083         Log(Log::Level::warning) << xi18nc("@info:status", "Partition <filename>%1</filename> is not properly aligned (last sector: %2, modulo: %3).", p.deviceNode(), newLast, lastDelta(d, p, newLast));
0084 
0085     return firstDelta(d, p, newFirst) == 0 && lastDelta(d, p, newLast) == 0;
0086 }
0087 
0088 /** @return the sector size to align the partition start and end to
0089 */
0090 qint64 PartitionAlignment::sectorAlignment(const Device& d)
0091 {
0092     Q_UNUSED(d)
0093     return s_sectorAlignment;
0094 }
0095 
0096 void PartitionAlignment::setSectorAlignment(int sectorAlignment)
0097 {
0098     if (sectorAlignment > 0)
0099         s_sectorAlignment = sectorAlignment;
0100 }
0101 
0102 qint64 PartitionAlignment::alignedFirstSector(const Device& d, const Partition& p, qint64 s, qint64 min_first, qint64 max_first, qint64 min_length, qint64 max_length)
0103 {
0104     if (firstDelta(d, p, s) == 0)
0105         return s;
0106 
0107     /** @todo Don't assume we always want to align to the front.
0108         Always trying to align to the front solves the problem that a partition does
0109         get too small to take another one that's copied to it, but it introduces
0110         a new bug: The user might create a partition aligned at the end of a device,
0111         extended partition or at the start of the next one, but we align to the back
0112         and leave some space in between.
0113     */
0114     // We always want to make the partition larger, not smaller. Making it smaller
0115     // might, in case it's a partition that another is being copied to, mean the partition
0116     // ends up too small. So try to move the start to the front first.
0117     s = s - firstDelta(d, p, s);
0118 
0119     while (s < d.partitionTable()->firstUsable() || s < min_first || (max_length > -1 && p.lastSector() - s + 1 > max_length))
0120         s += sectorAlignment(d);
0121 
0122     while (s > d.partitionTable()->lastUsable() || (max_first > -1 && s > max_first) || p.lastSector() - s + 1 < min_length)
0123         s -= sectorAlignment(d);
0124 
0125     return s;
0126 }
0127 
0128 qint64 PartitionAlignment::alignedLastSector(const Device& d, const Partition& p, qint64 s, qint64 min_last, qint64 max_last, qint64 min_length, qint64 max_length, qint64 original_length, bool original_aligned)
0129 {
0130     if (lastDelta(d, p, s) == 0)
0131         return s;
0132 
0133     s = s + sectorAlignment(d) - lastDelta(d, p, s);
0134 
0135     // if we can retain the partition length exactly by aligning to the front, do that
0136     if (original_aligned && p.length() - original_length == lastDelta(d, p, s))
0137         s -= sectorAlignment(d);
0138 
0139     while (s < d.partitionTable()->firstUsable() || s < min_last || s - p.firstSector() + 1 < min_length)
0140         s += sectorAlignment(d);
0141 
0142     while (s > d.partitionTable()->lastUsable() || (max_last > -1 && s > max_last) || (max_length > -1 && s - p.firstSector() + 1 > max_length))
0143         s -= sectorAlignment(d);
0144 
0145     return s;
0146 }