1 /*
2 SPDX-FileCopyrightText: 2010 Volker Lanz <vl@fidra.de>
3 SPDX-FileCopyrightText: 2014-2018 Andrius Štikonas <andrius@stikonas.eu>
4 SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac <teo@kde.org>
5 SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com>
6 SPDX-FileCopyrightText: 2019 Yuri Chornoivan <yurchor@ukr.net>
7
8 SPDX-License-Identifier: GPL-3.0-or-later
9 */
10
11 #include "core/partitionalignment.h"
12
13 #include "core/partition.h"
14 #include "core/partitiontable.h"
15 #include "core/device.h"
16 #include "core/diskdevice.h"
17
18 #include "fs/filesystem.h"
19
20 #include "util/globallog.h"
21
22 #include <KLocalizedString>
23
24 int PartitionAlignment::s_sectorAlignment = 2048;
25
firstDelta(const Device & d,const Partition & p,qint64 s)26 qint64 PartitionAlignment::firstDelta(const Device& d, const Partition& p, qint64 s)
27 {
28 if (d.partitionTable()->type() == PartitionTable::msdos) {
29 const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
30 if (p.roles().has(PartitionRole::Logical) && s == 2 * diskDevice.sectorsPerTrack())
31 return (s - (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d);
32
33 if (p.roles().has(PartitionRole::Logical) || s == diskDevice.sectorsPerTrack())
34 return (s - diskDevice.sectorsPerTrack()) % sectorAlignment(d);
35 }
36
37 return s % sectorAlignment(d);
38 }
39
lastDelta(const Device & d,const Partition &,qint64 s)40 qint64 PartitionAlignment::lastDelta(const Device& d, const Partition&, qint64 s)
41 {
42 return (s + 1) % sectorAlignment(d);
43 }
44
isLengthAligned(const Device & d,const Partition & p)45 bool PartitionAlignment::isLengthAligned(const Device& d, const Partition& p)
46 {
47 if (d.partitionTable()->type() == PartitionTable::msdos) {
48 const DiskDevice& diskDevice = dynamic_cast<const DiskDevice&>(d);
49 if (p.roles().has(PartitionRole::Logical) && p.firstSector() == 2 * diskDevice.sectorsPerTrack())
50 return (p.length() + (2 * diskDevice.sectorsPerTrack())) % sectorAlignment(d) == 0;
51
52 if (p.roles().has(PartitionRole::Logical) || p.firstSector() == diskDevice.sectorsPerTrack())
53 return (p.length() + diskDevice.sectorsPerTrack()) % sectorAlignment(d) == 0;
54 }
55
56 return p.length() % sectorAlignment(d) == 0;
57 }
58
59 /** Checks if the Partition is properly aligned to the PartitionTable's alignment requirements.
60
61 Will print warning messages to GlobalLog if the Partition's first sector is not aligned and
62 another one if the last sector is not aligned. This can be suppressed with setting the @p quiet to
63 true.
64
65 @see alignPartition(), canAlignToSector()
66
67 @param d device the partition is on
68 @param p the partition to check
69 @param quiet if true, will not print warning
70 @return true if properly aligned
71 */
isAligned(const Device & d,const Partition & p,bool quiet)72 bool PartitionAlignment::isAligned(const Device& d, const Partition& p, bool quiet)
73 {
74 return isAligned(d, p, p.firstSector(), p.lastSector(), quiet);
75 }
76
isAligned(const Device & d,const Partition & p,qint64 newFirst,qint64 newLast,bool quiet)77 bool PartitionAlignment::isAligned(const Device& d, const Partition& p, qint64 newFirst, qint64 newLast, bool quiet)
78 {
79 if (firstDelta(d, p, newFirst) && !quiet)
80 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));
81
82 if (lastDelta(d, p, newLast) && !quiet)
83 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));
84
85 return firstDelta(d, p, newFirst) == 0 && lastDelta(d, p, newLast) == 0;
86 }
87
88 /** @return the sector size to align the partition start and end to
89 */
sectorAlignment(const Device & d)90 qint64 PartitionAlignment::sectorAlignment(const Device& d)
91 {
92 Q_UNUSED(d)
93 return s_sectorAlignment;
94 }
95
setSectorAlignment(int sectorAlignment)96 void PartitionAlignment::setSectorAlignment(int sectorAlignment)
97 {
98 s_sectorAlignment = sectorAlignment;
99 }
100
alignedFirstSector(const Device & d,const Partition & p,qint64 s,qint64 min_first,qint64 max_first,qint64 min_length,qint64 max_length)101 qint64 PartitionAlignment::alignedFirstSector(const Device& d, const Partition& p, qint64 s, qint64 min_first, qint64 max_first, qint64 min_length, qint64 max_length)
102 {
103 if (firstDelta(d, p, s) == 0)
104 return s;
105
106 /** @todo Don't assume we always want to align to the front.
107 Always trying to align to the front solves the problem that a partition does
108 get too small to take another one that's copied to it, but it introduces
109 a new bug: The user might create a partition aligned at the end of a device,
110 extended partition or at the start of the next one, but we align to the back
111 and leave some space in between.
112 */
113 // We always want to make the partition larger, not smaller. Making it smaller
114 // might, in case it's a partition that another is being copied to, mean the partition
115 // ends up too small. So try to move the start to the front first.
116 s = s - firstDelta(d, p, s);
117
118 while (s < d.partitionTable()->firstUsable() || s < min_first || (max_length > -1 && p.lastSector() - s + 1 > max_length))
119 s += sectorAlignment(d);
120
121 while (s > d.partitionTable()->lastUsable() || (max_first > -1 && s > max_first) || p.lastSector() - s + 1 < min_length)
122 s -= sectorAlignment(d);
123
124 return s;
125 }
126
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)127 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)
128 {
129 if (lastDelta(d, p, s) == 0)
130 return s;
131
132 s = s + sectorAlignment(d) - lastDelta(d, p, s);
133
134 // if we can retain the partition length exactly by aligning to the front, do that
135 if (original_aligned && p.length() - original_length == lastDelta(d, p, s))
136 s -= sectorAlignment(d);
137
138 while (s < d.partitionTable()->firstUsable() || s < min_last || s - p.firstSector() + 1 < min_length)
139 s += sectorAlignment(d);
140
141 while (s > d.partitionTable()->lastUsable() || (max_last > -1 && s > max_last) || (max_length > -1 && s - p.firstSector() + 1 > max_length))
142 s -= sectorAlignment(d);
143
144 return s;
145 }
146