The GUI displays the file system of an open encrypted file system as
"[Encrypted] FSTYPE" [1]. However saved details just writes the file
system type as "luks". Update saved details writing code to use the
same method the GUI currently uses [2].
[1] commit cb3cc505ce
Display "[Encrypted] FSTYPE" in the File System column (#760080)
[2] commit bd6fc67afb
Provide virtual Partition::get_filesystem_string() method (#774818)
gpart scans a drive trying to guess the location of partitions when an
MBR partition table is lost [1]. However the tool is unmaintained,
takes hours or days of 100% CPU time to scan a drive and provides no
progress indication [2][3][4]. We keep recommending killing the gpart
process and using TestDisk [5] instead.
Therefore remove Device > Attempt Data Rescue and the use of gpart from
GParted.
[1] Gpart
https://github.com/baruch/gpart
[2] Have you had a good or bad experience with Dev->Attempt Data Rescue?
http://gparted-forum.surf4.info/viewtopic.php?id=17992
No good, only bad experiences using gpart were reported.
[3] Gparted does not say anything
http://gparted-forum.surf4.info/viewtopic.php?id=17749
Forum user reported waiting 48 hours with no progress indication.
We recommended using TestDisk.
[4] How cancel Data Rescue process?
http://gparted-forum.surf4.info/viewtopic.php?id=18143
Forum user reported it will take 3 days to scan their external 480GB
drive. We recommended using TestDisk instead.
[5] TestDisk, Data Recovery
https://www.cgsecurity.org/wiki/TestDiskCloses!118 - Remove Attempt Data Rescue and use of gpart
In C++11, nullptr [1] is the strongly typed value to use instead of the
macro NULL [2]. Use everywhere [3][4].
[1] nullptr, the pointer literal (since C++11)
https://en.cppreference.com/w/cpp/language/nullptr
[2] NULL
https://en.cppreference.com/w/cpp/types/NULL
[3] Bjarne Stroustrup's C++ Style and Technique FAQ, Should I use NULL
or 0?
https://www.stroustrup.com/bs_faq2.html#null
"In C++, the definition of NULL is 0, so there is only an
aesthetic difference. I prefer to avoid macros, so I use 0.
Another problem with NULL is that people sometimes mistakenly
believe that it is different from 0 and/or not an integer. In
pre-standard code, NULL was/is sometimes defined to something
unsuitable and therefore had/has to be avoided. That's less
common these days.
If you have to name the null pointer, call it nullptr; that's
what it's called in C++11. Then, "nullptr" will be a keyword.
"
[4] What is nullptr in C++? Advantages, Use Cases & Examples
https://favtutor.com/blogs/nullptr-cpp
"Advantages of nullptr
...
Compatible: Null pointers are compatible with null pointer
constants in the C style (such as NULL and 0). This implies
that old C code that uses these constants and null pointers can
communicate with each other in C++.
"
Closes!117 - Require C++11 compilation
As discussed in the previous commit the oldest supported distributions
now provide gtkmm versions higher that 3.18.0 (which requires C++11
compilation). Therefore increase the minimum required version to gtkmm
3.18.0. This allows removal of HAVE_LABEL_SET_XALIGN autoconf
definition and associated fallback code.
Closes!117 - Require C++11 compilation
GParted crashes when blkid doesn't provide the UUID of the exfat
partition and its serial has preceding zeroes (and it isn't mounted).
blkid doesn't report UUID if the serial number is 0000-0000 (a.k.a.
0x0).
Steps to reproduce:
# truncate -s 4M /tmp/disk.img
# losetup -f --show /tmp/disk.img
/dev/loop0
# mkfs.exfat /dev/loop0
[...]
exFAT format complete!
# partprobe /dev/loop0
# blkid /dev/loop0
/dev/loop0: UUID="F7BF-ABFF" BLOCK_SIZE="512" TYPE="exfat" PTTYPE="dos"
# exfatlabel /dev/loop0 -i 0x0
exfatprogs version : 1.1.3
New volume serial : 0x0
# blkid /dev/loop0
/dev/loop0: BLOCK_SIZE="512" TYPE="exfat" PTTYPE="dos"
# gparted /dev/loop0
GParted 1.3.1
configuration --enable-libparted-dmraid --enable-online-resize
libparted 3.4
** (gpartedbin:94926): ERROR **: 10:45:01.894:
unhandled exception (type std::exception) in signal handler:
what: basic_string::assign: __pos (which is 18446744073709551615) > this->size() (which is 3)
Trace/breakpoint trap (core dumped)
# losetup -d /dev/loop0; rm /tmp/disk.img
As blkid doesn't report exfat UUID 0000-0000 FS_Info cache can't report
it so exfat::read_uuid() is called. Then `tune.exfat -i /dev/loop0` is
executed:
# tune.exfat -i /dev/loop0
exfatprogs version : 1.1.3
volume serial : 0x0
And exfat::serial_to_blkid_uuid() is called with "0x0", which causes a
crash. Fix by safely handling volume serial numbers of any length,
specifically shorter than 8 hexadecimal digits.
Closes!115 - Fix crash when dealing with 0000-0000 exfat UUID
get_text() only performs const access on the ProgressBar object so
return the member string by constant reference.
Previously done for other string returning getters, even though the
value is assigned to a variable and doesn't save anything:
1f6e81295b
Return constant reference from OperationDetail::get_description() (!94)
The only use of the reference returned from
OperationDetail::get_progressbar() is to call const methods
ProgressBar::running(), ::get_fraction() and ::get_text(). Therefore
make OperationDetail::get_progressbar() return a const reference.
As described in the previous commit "Clear progress bar text when
starting the bar (#230)" progress bar data is either reporting bytes
copied or fraction complete. The bytes copied case gets in progress
text like this:
544.00 MiB of 1.00 GiB copied (00:00:11 remaining)
But the fraction complete gets no text.
Now also generate time remaining text for progress bars only reporting
fraction complete. As with the bytes copied text only add the time
remaining estimate after 5 seconds have passed. Looks like:
(00:01:59 remaining)
This is most useful for NTFS partition copy and resize operations which
can take a while depending on the amount of data involved.
These operations use steps which generate progress bar bytes copied
information text:
* Partition move or copy using GParted's internal block copy
* EXT2/3/4 partition move or copy
* XFS partition copy
The bytes copied text looks like this (after the copy completes and the
time remaining is no longer included):
1.00 GiB of 1.00 GiB copied
And these operations use steps which generate progress bar information
without text (because the progress bar data only represents a fraction
complete):
* EXT2/3/4 partition resize
* EXT2/3/4 partition create
* EXT2/3/4 partition format
* EXT2/3/4 partition check
* NTFS partition resize
* NTFS partition copy
In the Applying pending operations dialog, while an operation is being
applied there are 2 progress bars. The top progress bar displays either
a pulse bar or the progress bar data for the current step. Additionally
for the relevant steps the progress bar generates the bytes copied text.
This text, when available, is displayed in small grey characters just
above the progress bar itself.
Copy a FAT partition and apply. Bytes copied text is displayed just
above the top progress bar. Copy an NTFS partition and apply. The left
behind bytes copied text from the previous operation is displayed,
instead of nothing.
Restart GParted and copy an NTFS partition and apply. As intended, this
time there is no bytes copied text displayed just above the top progress
bar.
As there is just a single ProgressBar object, single_progressbar, fix
this by clearing the progress bar text each time it is started.
Closes#230 - Missing progress bar text reset when applying operation
STAT_FORMATTED is only used inside snap_to_mebibyte() to suppress
enforcement that partition boundaries must not overlay the MBR or EBRs
when merely formatting existing partitions. However since commit [1],
snap_to_mebibyte() is only called inside the dialogs composing Create
New, Copy / Paste into New and Resize / Move operations and never when
composing a Format operation or any other operation which doesn't change
partition boundaries. Therefore remove STAT_FORMATTED.
[1] 7c94b7d920
Snap partition boundaries before dialogs update FS usage (#48)
The available number of sectors 'total_length' is calculated as one
sector too few. However this doesn't matter because when composing a
new partition in Dialog_Partition_New::Get_New_Partition() the dialog
fits the end of the partition to the end of the unallocated partition
'new_partition->sector_end' in which it is being created, not the
available space. Missed in earlier commit:
4f84cff781
cleanups
Anyway correct the calculation.
As a partition's position is completely defined by it's starting and
ending sector, it is aligned if both boundaries are aligned. The size
of a partition just depends on the starting and ending sector.
Therefore it is never necessary to also check if the size of the
partition is also an exact multiple of MiB. Remove this unnecessary
portion of the alignment check when resizing/moving a partition (or
copy/pasting into a new partition).
Now Win_GParted::m_display_device.partitions is an identical copy of
Win_GParted::display_partitions with the same lifetime. That's wasteful
and pointless. Therefore remove the later and use the former in it's
place.
Closes#227 - Unable to allocate 1 MiB between partitions when moving to
the right
The Create New and Paste dialogs also create partitions and have to
honour currently composed partitions while doing so. Therefore they
must have a Device object containing the currently composed partition
layout for passing into snap_to_alignment() and below. So copy the
current Device object when refreshing the visual at the same time
visual_partitions is generated and use in all 3 dialogs which compose
new partitions.
Note that Create New and Paste aren't subject to the same bug as Resize/
Move was because the code in snap_to_mebibyte() [1] checked the
partition object being composed has status STAT_REAL. This is true for
partition objects created by the Resize/Move dialog, but not true for
the Create New and Paste dialogs which set status to STAT_NEW and
STAT_COPY respectively instead.
[1] Dialog_Base_Partition::snap_to_mebibyte() lines 418 to 438
https://gitlab.gnome.org/GNOME/gparted/-/blob/GPARTED_1_5_0/src/Dialog_Base_Partition.cc#L418Closes#227 - Unable to allocate 1 MiB between partitions when moving to
the right
Start with 2 partitions next to each other, containing file systems that
GParted can move and resize.
EG:
|[#1 ext2 ][#2 swap ] |
Move the start of partition #2 to the right. Then attempt to move the
end of partition #1 to the right to meet it.
EG:
|[#1 ext2 ][#2 swap ] |
The Resize/Move dialog will allow the free space following to be set to
0 so partition #1 is again adjacent to partition #2, but after closing
the dialog a forced 1 MiB gap is added, shrinking the composed size of
partition #1 by that 1 MiB.
If instead the first operation to shrink and move partition #2 is
applied, then partition #1 can be successfully resize right up to
partition #2 without a 1 MiB gap.
Relevant call sequence:
Win_GParted::activate_resize()
Dialog_Partition_Resize_Move::Dialog_Partition_Resize_Move()
Dialog_Base_Partition::Get_New_Partition()
prepare_new_partition()
snap_to_alignment()
snap_to_mebibyte()
prepare_new_partition() created a new partition object to correctly
represent the resized/moved partition #1. However this code in
snap_to_mebibyte() [1] determined that the new location for partition #1
overlapped with where partition #2 currently is on disk, not where
partition #2 will be after the previous operation is applied, therefore
it forced a 1 MiB decrease in partition #1's size creating the gap.
This is because snap_to_mebibyte() is working with the on disk view of
the partitions obtained from devices[current_device] passed into the
Dialog_Partition_Resize_Move() constructor. Hence why applying the
operations one at a time doesn't suffer from the forced 1 MiB gap.
Fix this by creating a copy of the current device object, replacing the
on disk partition layout with the composed partition layout as displayed
in the UI. Then pass this into the Dialog_Partition_Resize_Move
constructor.
[1] Dialog_Base_Partition::snap_to_mebibyte() lines 418 to 438
https://gitlab.gnome.org/GNOME/gparted/-/blob/GPARTED_1_5_0/src/Dialog_Base_Partition.cc#L418Closes#227 - Unable to allocate 1 MiB between partitions when moving to
the right
The code in erase_filesystem_signatures() used libparted
ped_device_write() which allowed any sector in the whole disk device to
be written. The code only depended on calculations of somewhat
complicated zero offset ranges and the start partition offset to ensure
that it didn't zero sectors outside the target partition. The code
doesn't overwrite partition boundaries, but there have been updates and
bug fixes to the calculation code. To improve the safety create a
libparted geometry representing the partition, or whole disk device,
to be cleared and use ped_geometry_write() so that libparted enforces
writes are only within the partition boundary being erased.
Deliberately breaking erase_filesystem_signatures() code so that it
tries to write past the end of the partition produces this dialog:
Libparted Error
(-) Attempt to write sectors 1024000-1024007 outside of
partition on /dev/sdb.
[ Cancel ] [ Ignore ]
And trying to write before the start of the partition produces this
dialog:
Libparted Bug
(-) Assert (offset >= 0) at cs/geom.c:375 in function
ped_geometry_write() failed.
[ No ]
Followed by GParted aborting and producing a core dump. Not ideal from
libparted, but it does prevent GParted writing outside the partition
boundaries and only occurs in the case of a bug in
erase_filesystem_signatures() which is exercised on every Create and
Format Partition operation and now also unit tested. So not something
we will let through to the users.
User reported that GParted didn't clear a pdc (Promise FastTrack) RAID
signature [1]. Reproduce this issue by creating a 16 MiB - 512 byte
test image with Promise FastTrack RAID signatures at all recognised
offsets [2].
$ python << 'EOF'
signature = b'Promise Technology, Inc.'
import os
fd = os.open('/tmp/test.img', os.O_CREAT|os.O_WRONLY)
os.ftruncate(fd, 16*1024*1024 - 512)
for offset in [63, 255, 256, 16, 399, 591, 675, 735, 911, 974, 991, 951, 3087]:
os.lseek(fd, -(offset*512), os.SEEK_END)
os.write(fd, signature)
os.close(fd)
EOF
Then use GParted Format to > Cleared.
$ sudo ./gpartedbin /tmp/test.img
Afterwards blkid, and therefore GParted, still recognises this as a
Promise FastTrack RAID member.
$ blkid /tmp/test.img
/tmp/test.img: TYPE="promise_fasttrack_raid_member"
This is because the test image still contains multiple signatures.
$ hexdump -C /tmp/test.img | grep Promise
00e7e000 50 72 6f 6d 69 73 65 20 54 65 63 ... |Promise Technolo|
00fce000 50 72 6f 6d 69 73 65 20 54 65 63 ... |Promise Technolo|
00fdfe00 50 72 6f 6d 69 73 65 20 54 65 63 ... |Promise Technolo|
00ff8000 50 72 6f 6d 69 73 65 20 54 65 63 ... |Promise Technolo|
Used a test image not an exact multiple of MiBs because drives generally
aren't an exact MiB multiple in size either and as the clearing of ZFS
labels L2 and L3 by writes of zeros at the end of the drive is rounded
to 256 KiBs there will be sectors after that not zeroed where other
Promise signatures remain. The above signatures map back to these
sectors before the end:
16*1024*1024 - 512 = 16776704
512b sectors KiB
(0x00e7e000 - 16776704) / 512 = -3087 -1543.5
(0x00fce000 - 16776704) / 512 = -399 -199.5
(0x00fdfe00 - 16776704) / 512 = -256 -128
(0x00ff8000 - 16776704) / 512 = -63 -31.5
Promise FastTrack RAID signatures are always at multiples 512-byte
sectors (code uses left shift 9 to convert from sectors to byte offset)
[2].
Fix this by:
1. Replace existing zeroing of 3 ranges relative to the end of the
device to be a single range covering the ZFS labels L2 and L3 to the
end of the drive. This will also clear the SWRaid 0.90 & 1.0
super blocks, the Nilfs2 secondary super block, the Intel Software
RAID signature found not zeroed in the unaligned unit test case and
the above Promise FastTrack RAID signatures at -199.5 KiB and later.
2. Add zeroing of the final Promise FastTrack RAID signature at sector
-3087.
Performed a review of all the other ATARAID super blocks detected by
blkid (files *_raid.c) [3] and they are all located within the last 11
sectors so will be zeroed by case 1. above.
[1] GParted forum thread: How to remove a ataraid partition ?
http://gparted-forum.surf4.info/viewtopic.php?id=18104
[2] blkid from util-linux promise_raid.c:probe_pdcraid()
https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git/tree/libblkid/src/superblocks/promise_raid.c?h=v2.38.1#n27
[3] blkid RAID member detection (files *_raid.c)
https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git/tree/libblkid/src/superblocks/?h=v2.38.1Closes#220 - Format to Cleared not clearing "pdc" ataraid signature
As documented in the previous commit xfsprogs >= 5.19.0 refuses to
create an XFS file system smaller than 300 MiB.
$ truncate -s $((300*1024*1024-1)) test.img
$ ls -l test.img
-rw-r--r-- 1 auser auser 314572799 Dec 21 11:01 test.img
$ mkfs.xfs -V
mkfs.xfs version 6.0.0
$ mkfs.xfs test.img
Filesystem must be larger than 300MB.
...
$ echo $?
1
Successfully create an XFS file system at minimum size of 300 MiB.
$ truncate -s $((300*1024*1024)) test.img
$ ls -l test.img
-rw-r--r-- 1 auser auser 314572800 Dec 21 11:05 test.img
$ mkfs.xfs test.img
...
$ echo $?
0
$ blkid test.img
test.img: UUID="..." BLOCK_SIZE="512" TYPE="xfs"
Increase the GParted minimum XFS size to 300 MiB. For simplicity and
because the XFS developers said of smaller XFS file systems [1]:
"are known to have performance and redundancy problems that are not
present on the volume sizes that XFS is best at handling"
regardless of the version of mkfs.xfs used to create that XFS then apply
to all versions of xfsprogs.
[1] https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/commit/?id=6e0ed3d19c54603f0f7d628ea04b550151d8a262
mkfs: stop allowing tiny filesystems
Closes#217 - GitLab CI test job failing with new mkfs.xfs error
"Filesystem must be larger than 300MB."
GParted's check operation is a check and if possible repair. For most
file system types GParted already requests that the file system is
repaired. fsck.exfat -y flag has been available since the first release
of exfatprogs 1.0.1 [1] so unconditionally add this.
[1] exfatprogs 1.0.1 fsck/fsck.c:main() case 'y':
https://github.com/exfatprogs/exfatprogs/blob/1.0.1/fsck/fsck.c#L1231!109 - Enable repair when checking exfat file systems
The code was overly complicated in how it converted to the 32-bit little
endian on-disk representation of the Hidden Sectors field. It did:
1. Formatted the partition start sector as a hexadecimal string.
2. Padded it to 8 digits.
3. Reversed pairs of digits.
4. Converted pairs of hexadecimal digits to bytes of binary data.
5. Wrote the 4 bytes of binary data to the Hidden Sectors field.
There is no need for all this string manipulation to convert to a 32-bit
little endian value. Just do this:
1. Truncate (signed 64-bit) partition start sector to 32-bit.
2. Convert from host native to little endian.
3. Write as 4 bytes of binary data to the Hidden Sectors field.
The code also ignores write errors. ofstream.write() only copies the
data into an in process buffer [1] and the data is not passed to the OS
to write to the open file handle until ofstream.close() [2] is called.
However the status of close() was not checked so a failure of the OS to
perform the write would go unreported.
In the case of a failure providing the user with a command line to set
the Hidden Sectors field is excessive. Updating the Hidden Sectors is
no more or less likely to fail than for any other storage manipulation
action. For example GParted doesn't provide command line instructions
to update a partition size if a libparted call fails. Therefore remove.
Rewrite the code to resolve the above issues and lay it out using
if-operation-fails-return-early pattern.
[1] std::ostream::write()
"... it inserts characters into associated stream_buffer object as
if calling its member function sputc until n characters have been
written or until an insertion fails ..."
https://cplusplus.com/reference/ostream/ostream/write/
[2] std::ofstream::close()
"Any pending output sequence is written to the file."
https://cplusplus.com/reference/fstream/ofstream/close/Closes#164 - GParted crashes copying NTFS partition to starting beyond
2TiB
Create test setup using a 4 TiB loop device:
# truncate -s 4T /tmp/disk.img
# losetup -f --show /tmp/disk.img
/dev/loop0
Create 2 x 1 TiB partitions. First at offset 1 MiB, second at offset
2 TiB:
# sgdisk --new 1:2048:2147485696 --typecode 1:0700 /dev/loop0
# sgdisk --new 2:4294967296:6442450944 --typecode 2:0700 /dev/loop0
# partprobe /dev/loop0
Create NTFS file system in the first partition:
# mkntfs -Q /dev/loop0p1
Then use GParted to copy the first NTFS partition into the second
partition. GParted crashes:
# gpartedbin /dev/loop0
...
(gpartedbin:14660): glibmm-ERROR **: 20:39:01.191:
unhandled exception (type std::exception) in signal handler:
what: basic_string::_M_replace_aux
Trace/breakpoint trap
# echo $?
133
Overview of what is happening is that GParted_Core::update_bootsector()
is attempting to set the Hidden Sectors [1] field in the NTFS Partition
Boot Sector (PBS) to the start sector of the newly copied /dev/loop0p2
partition. But the sector number is greater than will fit in a 32-bit
unsigned integer, which the code doesn't handle.
Specifically the code prints the sector number as a hexadecimal number
into string 'hex'. As the target partition starts at exactly 2 TiB then
hex="100000000" (9 hexadecimal digits long). Next:
hex.insert(0, 8 - hex.length(), '0');
is meant to pad the beginning of the 'hex' string with '0's to make the
string 8 character long. But the string is already 9 character long so
8 - 9 is -1 which as unsigned integral type size_t [2] is 2^64-1. So
insert() is trying to insert 18446744073709551615 '0's at the start of
the 'hex' string! Hence the crash.
mkntfs refuses to accept an explicit partition start sector of 2^32 or
larger:
# mkntfs -Q --partition-start 4294967296 /dev/loop0p2
Invalid partition start sector. Maximum is 4294967295 (2^32-1).
# echo $?
1
When mkntfs can't determine the drive geometry and partition offset, as
is the case on loop devices or the partition start sector is 2^32 or
larger, then mkntfs writes zero into Hidden Sectors:
# mkntfs -Q /dev/loop0p1
The partition start sector was not specified for /dev/loop0p1 and it could not be obtained automatically. It has been set to 0.
...
To boot from a device, Windows needs the 'partition start sector', ...
Windows will not be able to boot from this device.
Creating NTFS volume structures.
mkntfs completed successfully. Have a nice day.
# echo $?
0
# hexdump -C /dev/loop0p1 | head -2
00000000 eb 52 90 4e 54 46 53 20 20 20 20 00 02 08 00 00 |.R.NTFS .....|
00000010 00 00 00 00 00 f8 00 00 00 00 00 00 00 00 00 00 |................|
^^ ^^ ^^ ^^
Hidden Sectors value at offset 0x1C in the NTFS Partition Boot Sector.
So mkntfs is warning, writing the Hidden Sectors as zero and reporting
success. Fix GParted in an equivalent way when it is updating the
Hidden Sectors for a moved or copied NTFS which starts at sector 2^32
and beyond.
After this fix the operational details for the same copy operation are:
Copy /dev/loop0p1 to /dev/loop0p2
* calibrate /dev/loop0p1 (SUCCESS)
* calibrate /dev/loop0p2 (SUCCESS)
* set partition type on /dev/loop0p2 (SUCCESS)
* copy file system from /dev/loop0p1 to /dev/loop0p2 (SUCCESS)
* update boot sector of ntfs file system on /dev/loop0p2 (WARNING)
Partition start (4294967296) is beyond sector 4294967295 (2^32-1).
Windows will not be able to boot from this file system.
* check file system on /dev/loop0p2 for errors and if p... (SUCCESS)
[1] NTFS, Partition Boot Sector (PBS)
"
Byte Field Field name Purpose
offset length
0x1C 5 bytes Hidden Sectors The number of sectors preceding
the partition.
"
https://en.wikipedia.org/wiki/NTFS#Partition_Boot_Sector_(PBS)
[2] std::string::insert
"fill (5) string& insert(size_t pos, size_t n, char c);
Insert into string
Inserts additional characters into the string right before the
character indicated by pos (or p):
(5) fill
Insert n consecutive copies of character c.
"
https://cplusplus.com/reference/string/string/insert/Closes#164 - GParted crashes copying NTFS partition to starting beyond
2TiB
GParted has been using 'btrfs filesystem show' to report file system
usage but that doesn't work on a file system image so doesn't work in a
GitLab CI test job, as discussed earlier in this patchset.
There is 'btrfs inspect-internal min-dev-size' but:
1. That only works on a mounted file system and GParted isn't going to
mount an unmounted file system just to query it's used space, so by
extension won't work on image files.
2. It reports a figure which is almost the same as the chunk usage of
the device within the btrfs file system. However if some files have
been deleted leaving chunks partially used, then 'btrfs filesystem
resize' will successfully shrink a btrfs smaller than the reported
minimum device size.
And there is also 'btrfs filesystem usage' but that also only works on a
mounted file system.
So instead use 'btrfs inspect-internal dump-super' to report some of the
figures previously obtained from 'btrfs filesystem show'. For example
for a single device btrfs in an image file:
$ truncate -s 256M /tmp/test.img
$ mkfs.btrfs /tmp/test.img
$ btrfs inspect-internal dump-super /tmp/test.img | egrep 'total_bytes|bytes_used|sectorsize|devid'
total_bytes 268435456
bytes_used 114688
sectorsize 4096
dev_item.total_bytes 268435456
dev_item.bytes_used 92274688
dev_item.devid 1
Comparing with results from 'btrfs filesystem show' for the same file
system, after adding a loop device to allow 'btrfs filesystem show' to
succeed:
$ su -
# losetup --find --show /tmp/test.img
# btrfs filesystem show --raw /dev/loop0
Label: none uuid: 32a1eb31-4691-41ae-9ede-c45d723655a3
Total devices 1 FS bytes used 114688
devid 1 size 268435456 used 92274688 path /dev/loop0
This does bring a forced change in the calculation which affects multi-
device btrfs file systems. 'btrfs filesystem show' provided chunk
allocation information per device ("used" figure for each "devid"). The
file system wide used bytes ("FS bytes used") was apportioned according
to the fraction of the chunk allocation each device contained. However
'btrfs inspect-internal dump-super' doesn't provide chunk allocation
information for all devices, only for the current device
("dev_item.bytes_used"). Instead the calculation now has to apportion
the file system wide used bytes ("bytes_used") according to the fraction
of the size of the current device ("dev_item.total_bytes") within the
total size ("total_bytes").
This can't make any difference to a single device btrfs file system as
both fractions will be 1. It only affects how the file system wide used
bytes is distributed among multiple devices.
As an example to see the difference between calculation methods, create
a 2 GiB btrfs taking the defaults so getting duplicated metadata and
single data. Add another 2 GiB partition and populate with some files.
# mkfs.btrfs /dev/sdb1
btrfs-progs v4.15.1
See http://btrfs.wiki.kernel.org for more information.
Label: (null)
UUID: 68195e7e-c13f-4095-945f-675af4b1a451
Node size: 16384
Sector size: 4096
Filesystem size: 2.00GiB
Block group profiles:
Data: single 8.00MiB
Metadata: DUP 102.38MiB
System: DUP 8.00MiB
SSD detected: no
Incompat features: extref, skinny-metadata
Number of devices: 1
Devices:
ID SIZE PATH
1 2.00GiB /dev/sdb1
# mount /dev/sdb1 /mnt/1
# btrfs device add /dev/sdc1 /mnt/1
# cp -a /home/$USER/programming/c/gparted/ /mnt/1/
Usage figures using the old calculation apportioning file system wide
usage according to chunk allocation per device:
# btrfs filesystem show --raw /dev/sdb1
Label: none uuid: 68195e7e-c13f-4095-945f-675af4b1a451
Total devices 2 FS bytes used 178749440
devid 1 size 2147483648 used 239861760 path /dev/sdb1
devid 2 size 2147483648 used 436207616 path /dev/sdc1
sum_devid_used = 239861760 + 436207616
= 676069376
sdb1 usage = 178749440 * 239861760 / 676069376
= 63418277
sdc1 usage = 178749440 * 436207616 / 676069376
= 115331163
Usage figures using the new calculation apportioning file system wide
usage according to device sizes:
# btrfs inspect-internal dump-super /dev/sdb1 | egrep 'total_bytes|^bytes_used'
total_bytes 4294967296
bytes_used 178749440
dev_item.total_bytes 2147483648
# btrfs inspect-internal dump-super /dev/sdc1 | egrep 'total_bytes|^bytes_used'
total_bytes 4294967296
bytes_used 178749440
dev_item.total_bytes 2147483648
sdb1 usage = 178749440 * 2147483648 / 4294967296
= 89374720
sdc1 usage = 178749440 * 2147483648 / 4294967296
= 89374720
Both calculation methods ignore that btrfs allocates chunks at the
volume manager level. So when fully compacted the last chunk for
metadata and data for each storage profile (RAID level) will be
partially filled and this is not accounted for.
Also for multi-device btrfs file systems the new calculation provides
different results. However given that shrinking a device in a multi-
device btrfs file system can and does relocate extents to other devices
(redundancy requirements of chunks permitting) it's minimum size is
virtually impossible to calculate and may not restrict how small the
btrfs device can be shrunk anyway. If it turns out that this new
calculation causes problems it's been made a separate commit from the
previous commit for easier reverting.
Closes!105 - Update used btrfs file system commands, new minimum is
btrfs-progs 4.5
'btrfs filesystem show' only used to report rounded human readable size
figures. Therefore the actual figure could have been anywhere within
the rounding limit. GParted also applied a heuristic to snap the file
system size figure to the partition size if the partition size was
within the rounding limit of the reported file system size [1].
btrfs-progs v4.1 added the --raw option to print the figures in bytes
[2][3][4].
# btrfs filesystem show --raw /dev/sdb1
Label: none uuid: 003a619e-856f-4b9c-bd29-4d0ae0296d66
Total devices 2 FS bytes used 178765824
devid 1 size 2147483648 used 239861760 path /dev/sdb1
devid 2 size 2147483648 used 436207616 path /dev/sdc1
Since the oldest supported distributions now use btrfs-progs v4.5.3 and
later (see the distribution End-of-Life table in the previous commit
message), unconditionally use this to get accurate figures.
[1] 7fc16a1b69
Handle btrfs tools rounding of figures (#499202)
[2] btrfs-progs: Allow "filesystem show" command to handle different units
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git/commit/?id=15379fa2257bf937cf7830c0b1b79f2daf5df72c
[3] btrfs-progs: docs: new size options for fi show
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git/commit/?id=81225f11d9ea58590476612e69211113ddb9b943
[4] Btrfs progs release 4.1
https://lore.kernel.org/linux-btrfs/20150622150023.GX6761@twin.jikos.cz/Closes!105 - Update used btrfs file system commands, new minimum is
btrfs-progs 4.5
GParted so far uses 'btrfs filesystem show' to read the file system
UUID. But this doesn't work on a file system image so doesn't work in
the GitLab CI test jobs, as discussed in the earlier commit "Use btrfs
filesystem label to read the FS label (!105)".
$ truncate -s 256M /tmp/test.img
$ mkfs.btrfs /tmp/test.img
...
UUID: 5ea62f88-fef3-4ece-a726-b88f3d81fe1c
...
$ btrfs filesystem show /tmp/test.img
ERROR: not a valid btrfs filesystem: /tmp/test.img
Instead use 'btrfs inspect-internal dump-super' which works on image
files too.
$ btrfs inspect-internal dump-super /tmp/test.img
...
fsid 5ea62f88-fef3-4ece-a726-b88f3d81fe1c
...
'btrfs inspect-internal dump-super' was added in btrfs-progs 4.5 [1][2]
so is available in the oldest supported distributions and can be used
unconditionally.
Distro EOL btrfs --version
Debian 10 2022-Jun v4.20
RHEL / CentOS 7 2024-Jun v4.9.1
Ubuntu 18.04 LTS 2023-Apr v4.15.1
SLES 12 SP5 2024-Oct v4.5.3 [3][4]
Unfortunately it returns 0 status on failure so use non-empty stderr to
identify failure.
$ rm /tmp/test.img
$ truncate -s 256M /tmp/test.img
$ btrfs inspect-internal dump-super /tmp/test.img 1> /dev/null
ERROR: bad magic on superblock on /tmp/test.img at 65536
$ echo $?
0
[1] btrfs-progs: introduce inspect-internal dump-super
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git/commit/?id=eaa93e3b0295fc94c774ec73056559a6b8c78b42
[2] Btrfs progs release 4.5
https://lore.kernel.org/linux-btrfs/20160320235330.GG21722@suse.cz/
"* new/moved commands
* btrfs-show-super -> btrfs inspect-internal dump-super
"
[3] SUSE Long Term Service Pack Support
https://links.imagerelay.com/cdn/3404/ql/f3a083e9bcd34c76addd096d7f60ec00/long_term_service_pack_support_flyer.pdf
[4] SUSE package search
https://scc.suse.com/packages?name=SUSE%20Linux%20Enterprise%20Server&version=12.5&arch=x86_64&query=btrfsprogs&module=Closes!105 - Update used btrfs file system commands, new minimum is
btrfs-progs 4.5
Create function to replace repeated code which optionally removes
trailing new line character from a string.
Closes!105 - Update used btrfs file system commands, new minimum is
btrfs-progs 4.5
Until now GParted has run 'btrfs filesystem show' to read the file
system label. This has a number of issues and limitations:
1. Doesn't work on a file system image so can't be unit tested in the
GitLab CI test job where a loop device can't be created [1] or as a
non-root user.
2. Reported failed exit status 1 when successfully showing a mounted
btrfs, but only when using btrfs-progs v3.14 and 3.14.1 [2][3].
3. Failed to distinguish between label set to "none" and no label
reported as none, but only when mounted and with btrfs-progs v3.12
[3].
As non-root user run btrfs read label unit test:
$ tests/test_SupportedFileSystems --gtest_filter='*CreateAndReadLabel/btrfs'
...
[ RUN ] My/SupportedFileSystemsTest.CreateAndReadLabel/btrfs
test_SupportedFileSystem.cc:539: Skip test. Not root to be able to create required loop device
[ OK ] My/SupportedFileSystemsTest.CreateAndReadLabel/btrfs (0 ms)
Even as root, 'btrfs filesystem show' fails to work on an image file:
# truncate -s 256M /tmp/test.img
# mkfs.btrfs /tmp/test.img
# btrfs filesystem show /tmp/test.img
ERROR: not a valid btrfs filesystem: /tmp/test.img
# echo $?
1
# rm /tmp/test.img
Instead use 'btrfs filesystem label' to read the label. It also works
on an image file, the exit status is informative and output is just the
label followed by a new line character [4] so very simple to parse.
Error case:
$ truncate -s 256M /tmp/test.img
$ btrfs filesystem label /tmp/test.img
No valid Btrfs found on /tmp/test.img
$ echo $?
255
No label case:
$ mkfs.btrfs /tmp/test.img
$ btrfs filesystem label /tmp/test.img
$ echo $?
0
$ btrfs filesystem label /tmp/test.img | hexdump -C
00000000 0a |.|
00000001
Label case:
$ mkfs.btrfs -L 'label with
> new line and trailing space ' /tmp/test.img
$ btrfs filesystem label /tmp/test.img
label with
new line and trailing space
$ echo $?
0
$ btrfs filesystem label /tmp/test.img | hexdump -C
00000000 6c 61 62 65 6c 20 77 69 74 68 0a 6e 65 77 20 6c |label with.new l|
00000010 69 6e 65 20 61 6e 64 20 74 72 61 69 6c 69 6e 67 |ine and trailing|
00000020 20 73 70 61 63 65 20 0a | space .|
00000028
Run 'btrfs filesystem label' always passing the block device as this
works for both mounted and unmounted file systems. This is in
contrast to writing the label for a mounted btrfs where the mount
mount must be used [5].
# mkfs.btrfs -L 'test label' /dev/sdb1
# btrfs filesystem label /dev/sdb1
test label
# mount /dev/sdb1 /mnt/1
# btrfs filesystem label /dev/sdb1
test label
[1] 07ad43a107
Create loop devices for BTRFS read file system interface tests (!49)
[2] 82c6265fa5
Update parsing of btrfs filesystem show for the UUID (#733601)
[3] eca732fb0c
Update parsing of btrfs filesystem show for the label (#733601)
[4] btrfs-progs v3.14, cmd_label() function
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git/tree/cmds-filesystem.c?h=v3.14#n984
[5] eb034b1759
Add labelling of mounted btrfs (#163)
Closes!105 - Update used btrfs file system commands, new minimum is
btrfs-progs 4.5
Now fix the error with GParted clearing the label when setting a new
UUID on a FAT16/32 file system. Reproduce the issue on the command
line:
# mkfs.fat -F 16 -v -I -n TEST_LABEL /dev/sdb1
# mdir -f -i /dev/sdb1 ::/
Volume in drive : is TEST_LABEL
Volume Serial Number is 5D4C-6E6E
...
# mlabel -n -i /dev/sdb1 ::
# mdir -f -i /dev/sdb1 ::/
Volume in drive : has no label
Volume Serial Number is 77BB-A883
...
This was broken by commit "Fix writing FAT16/32 FS UUID on Alpine Linux
(!104)" earlier in this patchset, which included this comment:
"... Also drop the '-s' option
as showing the current label is unrelated to writing a new UUID."
It is not mentioned in the mlabel[1] manual page that option -s is
needed in order to avoid clearing the label when assigning a new UUID.
Anyway add the option back.
[1] mlabel(1)
https://linux.die.net/man/1/mlabel
"s Shows the existing label, without prompting the user.
n Assigns a new (random) serial number to the disk
"
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
Unit test writing FAT16/32 file system UUIDs fails on Alpine Linux like
this:
$ ./test_SupportedFileSystems --gtest_filter='*CreateAndWriteUUID/fat16'
...
[ RUN ] My/SupportedFileSystemsTest.CreateAndWriteUUID/fat16
test_SupportedFileSystems.cc:616: Failure
Value of: m_fs_object->write_uuid(m_partition, m_operation_detail)
Actual: false
Expected: true
Operation details:
mkfs.fat -F16 -v -I '/home/alpine/programming/c/gparted/tests/test_SupportedFileSystems.img' 00:00:00 (SUCCESS)
...
mlabel -s -n :: -i '/home/alpine/programming/c/gparted/tests/test_SupportedFileSystems.img' 00:00:00 (ERROR)
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
[ FAILED ] My/SupportedFileSystemsTest.CreateAndWriteUUID/fat16, where GetParam() = 13 (38 ms)
Using GParted on Alpine Linux to perform the same action produces the
same error in the operation results. Reproduce this on the command
line:
# mkfs.fat -F 16 -v -I /dev/sdb1
# mlabel -s -n :: -i /dev/sdb1
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
# echo $?
1
Again fix the same way, by moving the non-option '::' drive
specification to the end of the command line. Also drop the '-s' option
as showing the current label is unrelated to writing a new UUID.
# mdir -f -i /dev/sdb1 ::/ | grep 'Volume Serial Number is'
Volume Serial Number is B97E-59A3
# mlabel -n -i /dev/sdb1 ::
# echo $?
0
# mdir -f -i /dev/sdb1 ::/ | grep 'Volume Serial Number is'
Volume Serial Number is 1552-96A6
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
Unit test reading FAT16/32 file system UUIDs fails on Alpine Linux like
this:
$ ./test_SupportedFileSystems --gtest_filter='*CreateAndReadUUID/fat16'
....
[ RUN ] My/SupportedFileSystemsTest.CreateAndReadUUID/fat16
test_SupportedFileSystems.cc:581: Failure
Expected: (m_partition.uuid.size()) >= (9U), actual: 0 vs 9
test_SupportedFileSystems.cc:584: Failure
Value of: m_partition.get_messages().empty()
Actual: false
Expected: true
Partition messages:
Drive '::' not supported
Cannot initialize '::'
Drive 'A:' not supported
Cannot initialize 'A:'
Drive 'A:' not supported
Cannot initialize 'A:'
[ FAILED ] My/SupportedFileSystemsTest.CreateAndReadUUID/fat16, where GetParam() = 13 (28 ms)
This doesn't normally affect GParted because it uses blkid as first
choice to read file system UUIDs, only using file system specific
commands when blkid isn't available. Reproduce this on the command
line:
# mkfs.fat -F 16 -v -I /dev/sdb1
# mdir -f :: -i /dev/sdb1
Drive '::' not supported
Cannot initialize '::'
Drive 'A:' not supported
Cannot initialize 'A:'
Drive 'A:' not supported
Cannot initialize 'A:'
Again, this is caused by having non-option '::' drive specification
before all the options on the mdir command line, which isn't supported
by the POSIX strict getopt(3) on Alpine Linux. Apply the same fix of
moving the non-option argument to the end.
# mdir -f -i /dev/sdb1 ::/
Volume is drive : has no label
Volume Serial Number is 7DC9-BCD9
Director for ::/
No files
# echo $?
0
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
Unit test writing FAT16/32 file system labels fails on Alpine Linux like
this:
$ ./test_SupportedFileSystems --gtest_filter='*CreateAndWriteLabel/fat16'
...
[ RUN ] My/SupportedFileSystemsTest.CreateAndWriteLabel/fat16
test_SupportedFileSystems.cc:601: Failure
Value of: m_fs_object->write_label(m_partition, m_operation_detail)
Actual: false
Expected: true
Operation details:
mkfs.fat -F16 -v -I -n 'FIRST ' '/home/alpine/programming/c/gparted/tests/test_SupportedFileSystems.img' 00:00:00 (SUCCESS)
...
mlabel ::'SECOND ' -i '/home/alpine/programming/c/gparted/tests/test_SupportedFileSystems.img' 00:00:00 (ERROR)
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
[ FAILED ] My/SupportedFileSystemsTest.CreateAndWriteLabel/fat16, where GetParam() = 13 (29 ms)
Using GParted on Alpine Linux to perform the same action produces the
same error in the operation results. Reproduce this on the command
line:
# mkfs.fat -F 16 -v -I -n FIRST /dev/sdb1
# mlabel ::SECOND -i /dev/sdb1
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
# echo $?
1
Again, this is because musl libc's getopt(3) is POSIX compliant and
stops parsing options at '::', the first non-option argument. Apply the
same fix of moving the non-option argument to the end of the mlabel
command line:
# mlabel -i /dev/sdb1 ::SECOND
# echo $?
0
# mlabel -s -i /dev/sdb1
Volume label is SECOND
And for the clearing label case:
# mlabel -c -i /dev/sdb1 ::
# echo $?
0
# mlabel -s -i /dev/sdb1
Volume has no label
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
Several of the FAT16/32 file system unit tests fail on Alpine Linux. In
this commit we are just looking at the failure to read the label. The
test fails like this:
$ ./test_SupportedFileSystems --gtest_filter='*CreateAndReadLabel/fat16'
...
[ RUN ] My/SupportedFileSystemsTest.CreateAndReadLabel/fat16
test_SupportedFileSystems.cc:551: Failure
Expected equality of these values:
fs_label
Which is: "TEST_LABEL"
m_partition.get_filesystem_label().c_str()
Which is: ""
test_SupportedFileSystems.cc:554: Failure
Value of: m_partition.get_messages().empty()
Actual: false
Expected: true
Partition messages:
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
[ FAILED ] My/SupportedFileSystemsTest.CreateAndReadLabel/fat16, where GetParam() = 13 (21 ms)
The same error can be seen by using GParted to display a FAT16 or FAT32
file system on Alpine Linux. The Partition Information dialog displays
this warning:
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
Reproduce this on the command line:
# mkfs.fat -F 16 -v -I -n TEST_LABEL /dev/sdb1
# mlabel -s :: -i /dev/sdb1
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
# echo $?
1
The mlabel.c source [1] uses getopt(3) to parse the command line
arguments. musl libc's [2] getopt(3) must be strictly POSIX compliant
[3][4] and stops reading options at the first non-option argument, '::'
in this case. Move the non-option argument to the end of the command
line and it works:
# mlabel -s -i /dev/sdb1 ::
Volume label is TEST_LABEL
Where as GNU Libc's getopt(3) [5] says that by default it reorders argv
eventually moving all non-option arguments to the end, hence why this
has worked on every Linux distribution using GNU Libc. This can be
broken on any Linux distribution using GNU Libc by enforcing strict
POSIX behaviour from getopt(3). For example on Fedora 36:
# mkfs.fat -F 16 -v -I -n TEST_LABEL /dev/sdb1
# export POSIXLY_CORRECT=1
# mlabel -s :: -i /dev/sdb1
Mtools version 4.0.39, dated April 10th, 2022
Usage: mlabel [-vscVn] [-N serial] drive:
# echo $?
1
# mlabel -s -i /dev/sdb1 ::
Hidden (2048) does not match sectors (63)
Volume label is TEST_LABEL
# echo $?
0
Fix by moving the non-option (image file drive specification) '::' to
the end of the mlabel command line.
[1] Mtools
https://www.gnu.org/software/mtools/
[2] musl libc
https://musl.libc.org/
"musl is an implementation of the C standard library built on top of
the Linux system call API, including interfaces defined in the base
language standard, POSIX, and widely agreed-upon extensions.
"
[3] POSIX.1-2017, Functions, getopt
https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html
[4] getopt(3p)
https://man7.org/linux/man-pages/man3/getopt.3p.html
[5] getopt(3)
https://www.man7.org/linux/man-pages/man3/getopt.3.html
"By default, getopt() permutes the contents of argv as it scans, so
that eventually all the nonoptions are at the end. Two other
scanning modes are also implemented. If the first character of
optstring is '+' or the environment variable POSIXLY_CORRECT is set,
then option processing stops as soon as a nonoption argument is
encountered.
"
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
Follows the "Return Early" design pattern making the code easier to
understand without having to remember cases for elses or cascading ifs.
Refactor before the following commit's fix so that capture of output on
failure can be confirmed as still working.
Closes!104 - Add Alpine Linux CI jobs and resolve label and UUID issues
with FAT16/32
add_mountpoint_entry() doesn't modify the passed strings so use
pass-by-constant-reference. This avoids pass-by-value and having to
construct copies of the strings just to pass them to this method.
A user received the following error when attempting to resize a mounted
btrfs file system on their NixOS distribution:
Shrink /dev/nvme0n1p3 from 933.38 GiB to 894.32 GiB (ERROR)
+ calibrate /dev/nvme0n1p3 00:00:00 (SUCCESS)
+ btrfs filesystem resize 1:937759744K '/etc/machine-id' (ERROR)
ERROR: not a directory: /etc/machine-id
ERROR: resize works on mounted filesystems and accepts only
directories as argument. Passing file containing a btrfs image
would resize the underlying filesystem instead of the image.
In the partition table section of the gparted_details /dev/nvme0n1p3 was
reported with these mount points:
/etc/machine-id, /etc/NetworkManager/system-connections,
/etc/ssh/ssh_host_ed25519_key, /etc/ssh/ssh_host_ed25519_key.pub,
/etc/ssh/ssh_host_rsa_key, /etc/ssh/ssh_host_rsa_key.pub, /home,
/nix, /nix/store, /state, /var
The user had a common configuration of NixOS which boots with an empty
tmpfs as root with a few bind mounted files and directories to provide
the needed persistent data [1][2].
Re-create an equivalent situation:
1. Create a btrfs file system and mount it:
# mkfs.btrfs /dev/sdb1
# mkdir /mnt/store
# mount /dev/sdb1 /mnt/store
2. Bind mount a file from this file system else where in the hierarchy.
The only criteria is that this mount point sorts before /mnt/store.
# echo 'Test contents' > /mnt/store/test
# touch /boot/test
# mount --bind /mnt/store/test /boot/test
The kernel reports these mount mounts:
# grep sdb1 /proc/mounts
/dev/sdb1 /mnt/store btrfs rw,seclabel,relatime,space_cache=v2,subvolid=5,subvol=/ 0 0
/dev/sdb1 /boot/test btrfs rw,seclabel,relatime,space_cache=v2,subvolid=5,subvol=/ 0 0
3. Use GParted to resize this mounted btrfs file system. It fails with
the above error.
GParted read the mount points from /proc/mounts and sorted them. (See
the end of Mount_Info::load_cache() for the sorting). When resizing the
btrfs file system GParted just used the first sorted mount point. This
was the file /etc/machine-id for the user and file /boot/test in the
re-creation, hence the error.
Fix by selecting the first directory mount point to pass to the btrfs
resize command.
[1] NixOS tmpfs as root
https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
[2] Erase your darlings
https://grahamc.com/blog/erase-your-darlingsCloses#193 - path used to resize btrfs needs to be a directory
GParted fails to build on Alpine Linux Edge (development tree for the
next release) like this:
GParted_Core.cc: In constructor 'GParted::GParted_Core::GParted_Core()':
GParted_Core.cc:75:64: error: invalid 'static_cast' from type 'std::nullptr_t' to type 'PedPartitionFlag'
75 | for ( PedPartitionFlag flag = ped_partition_flag_next( static_cast<PedPartitionFlag>( NULL ) ) ;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The code is failing to compile now because musl libc 1.2.3 has became
more C++11 strict [1][2] by defining NULL [3] as nullptr [4] rather than
as 0. The parameter to ped_partition_flag_next() [5] should always have
been numeral 0 cast to an enumeration and never the NULL pointer.
Fixes this commit [6] from 2004-12-27 which changed the parameter from 0
to NULL.
[1] define NULL as nullptr when used in C++11 or later
https://git.musl-libc.org/cgit/musl/commit?id=98e688a9da5e7b2925dda17a2d6820dddf1fb28
[2] NULL vs nullptr (Why was it replaced?) [duplicate]
https://stackoverflow.com/questions/20509734/null-vs-nullptr-why-was-it-replaced
[3] C++ reference, NULL
https://en.cppreference.com/w/cpp/types/NULL
[4] C++ reference, nullptr
https://en.cppreference.com/w/cpp/language/nullptr
[5] libparted Documentation, ped_partition_flag_next()
https://www.gnu.org/software/parted/api/group__PedPartition.html#g0ce9ce4247b320011bc8e9d957c8cdbb
[6] Added cylsize to Device and made Operation contain a Device instead
commit 174f0cff77Closes!100 - Fix NULL == 0 assumption in call to
ped_partition_flag_next()
Musl libc [1][2] doesn't implement the GNU variant of basename() [3][4],
obtained via #include <string.h>. Therefore GParted fails to build on
such distributions:
fdebug-prefix-map=TOPDIR/build/tmp/work/cortexa57-yoe-linux-musl/gparted/1.4.0-r0/recipe-sysroot-native=-fvisibility-inlines-hidden -c -o ../../gparted-1.4.0/src/BCache_Info.cc:52:33:
error: use of undeclared identifier 'basename'; did you mean 'g_basename'?
return "/dev/" + Glib::ustring(basename(buf));
^~~~~~~~
g_basename
Fix by using the POSIX implementation of basename() [5] instead,
obtained via #include <libgen.h>, which musl libc does implement [6].
Note that the POSIX implementation of basename() is allowed to modify
the string passed to it. This is okay because
BCache_Info::get_bcache_device() is using a modifiable local character
buffer.
[1] musl libc
https://musl.libc.org/
[2] Projects using musl
https://wiki.musl-libc.org/projects-using-musl.html
[3] The GNU C Library, 5.10 Finding Tokens in a String
https://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html
[4] basename(3) - Linux manual page
https://man7.org/linux/man-pages/man3/basename.3.html
[5] POSIX basename()
https://pubs.opengroup.org/onlinepubs/009695399/functions/basename.html
[6] musl source, basename.c
http://git.musl-libc.org/cgit/musl/tree/src/misc/basename.cCloses!99 - Fix undeclared identifier 'basename' build failure with
musl libc
GParted automatically enables the Partition > Unmount action for busy
partitions. This is not going to be supported for jbds so disable it.
Closes#89 - GParted doesn't recognise EXT4 fs journal partition
Continuing from the state in the previous commit, create an ext4 file
system using the previously created external journal and mount it.
# mke2fs -t ext4 -J device=/dev/sdb1 -L test-ext4 /dev/sdb2
# mount /dev/sdb2 /mnt/2
Did some experimenting with trying to create a second file system using
the same external journal which is already in use.
# mke2fs -t ext4 -J device=/dev/sdb1 -L 2nd-test-ext4 /dev/sdb3
...
/dev/sdb1 is apparently in use by the system; will not make a journal here!
# exit $?
1
Examined the source code of mke2fs and found that it performs an
exclusive read-only open of the named journal block device to check if
it is in use by the system or not [1]. Use the same method in GParted.
Not used alternative method would be to mark the jbd active when the
ext3/4 file system using it is active, but that requires working out the
linkage between them. That can be done using either blkid or dumpe2fs
output but that involves parsing more fields and caching more data so is
much more code than just testing the block device busy status using the
same method which mke2fs uses.
Matching UUIDs via blkid output.
# blkid /dev/sdb1 /dev/sdb2
/dev/sdb1: LABEL="test-jbd" UUID="6e52858e-0479-432f-80a1-de42f9a4093e" TYPE="jbd"
/dev/sdb2: LABEL="test-ext4" UUID="cea5c2cd-b21c-4abf-a497-8c073bb12300" EXT_JOURNAL="6e52858e-0479-432f-80a1-de42f9a4093e" TYPE="ext4"
Matching UUIDs via dumpe2fs output.
# dumpe2fs -h /dev/sdb1 | egrep 'Filesystem UUID|Journal users'
dumpe2fs 1.46.3 (27-Jul-2021)
Filesystem UUID: 6e52858e-0479-432f-80a1-de42f9a4093e
Journal users: cea5c2cd-b21c-4abf-a497-8c073bb12300
# dumpe2fs -h /dev/sdb2 | egrep 'Filesystem UUID|Journal UUID'
dumpe2fs 1.46.3 (27-Jul-2021)
Filesystem UUID: cea5c2cd-b21c-4abf-a497-8c073bb12300
Journal UUID: 6e52858e-0479-432f-80a1-de42f9a4093e
If GParted was going to show the journal to file system linkage in the
UI then doing this would be needed. However so far there has only been
a single reported case of a GParted user using an external journal,
therefore adding the code complexity for this feature is not currently
justified. The simple busy detection method used by mke2fs is all that
is needed.
[1] mke2fs source code
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/
misc/mke2fs.c:main()
check_mount(journal_device, force, _("journal"));
misc/util.c:check_mount()
ext2fs_check_if_mounted(device, &mount_flags);
lib/ext2fs/ismounted.c:ext2fs_check_if_mounted()
ext2fs_check_mount_point(file, mount_flags, NULL, 0);
lib/ext2fs/ismounted.c:ext2fs_check_if_mounted()
if (stat(device, &st_buf) == 0 &&
ext2fsP_is_disk_device(st_buf.st_mode)) {
int fd = open(device, O_RDONLY | O_EXCL);
if (fd >= 0) {
/*
* The device is not busy so it's
* definitelly not mounted. No need to
* to perform any more checks.
*/
close(fd);
*mount_flags = 0;
return 0;
} else if (errno == EBUSY) {
busy = 1;
}
}
Closes#89 - GParted doesn't recognise EXT4 fs journal partition
A user reported that they were using an external journal with an ext4
file system, but that GParted didn't recognise it. (They had the jbd
on an Intel Optane drive and the ext4 file system on an SSD).
Create a jbd like this:
# mke2fs -O journal_dev -L test-jbd /dev/sdb1
# blkid /dev/sdb1
/dev/sdb1: LABEL="test-jbd" UUID="6e52858e-0479-432f-80a1-de42f9a4093e" TYPE="jbd"
Add recognition of jbd. Use Blue Shadow colour, the same as ext4,
because jbd is primarily used by ext3/4 [1][2]. jbd is also used by
ocfs2 [1][3] and lustre [4][5] clustered file systems, but they are very
unlikely to encountered by GParted users. Also xfs [6] and jfs [7] can
have external journals so if recognition of them is ever added they will
get the same colour as their respective file systems too.
[1] Journaling block device
https://en.wikipedia.org/wiki/Journaling_block_device
"JBD is filesystem-independent. ext3, ext4 and OCFS2 are known to
use JBD"
[2] https://ext4.wiki.kernel.org/index.php/Frequently_Asked_Questions#What_are_the_key_differences_between_jbd_and_jbd2.3F
[3] OCFS2: The Oracle Clustered File System, Version 2
https://www.kernel.org/doc/ols/2006/ols2006v1-pages-289-302.pdf
"Metadata journaling is done on a per node basis with JBD"
[4] Efficient Object Storage Journaling in a Distributed Parallel File
System
https://www.usenix.org/legacy/event/fast10/tech/full_papers/oral.pdf
[5] Lustre Software Release 2.x Operations Manual
https://doc.lustre.org/lustre_manual.pdf
6.4.2. Choosing Parameters for an External Journal
[6] mkfs.xfs(8) - construct an XFS filesystem
https://man7.org/linux/man-pages/man8/mkfs.xfs.8.html
"OPTIONS
...
logdev=device
This is used to specify that the log section should reside on
the device separate from the data section. The internal=1 and
logdev options are mutually exclusive.
"
[7] jfs_mkfs(8) - create a JFS formatted partition
https://manpages.debian.org/testing/jfsutils/jfs_mkfs.8.en.html
"OPTIONS
...
-j journal_device
Create the external JFS journal on journal_device, ...
"
Closes#89 - GParted doesn't recognise EXT4 fs journal partition
A bcache device provides accelerated access to a backing device in a one
to one relationship. Multiple bcache backing devices can be attached to
and accelerated by the same cache device. Extending the setup from the
previous commit, create an additional backing device and attach it to
the same cache.
# bcache make -B /dev/sdb2
# bcache attach /dev/sdc1 /dev/sdb2
# bcache show
Name Type State Bname AttachToDev
/dev/sdb2 1 (data) clean(running) bcache1 /dev/sdc1
/dev/sdb1 1 (data) clean(running) bcache0 /dev/sdc1
/dev/sdc1 3 (cache) active N/A N/A
List a couple of bcache specific sysfs files which identify registered
(active) bcache devices (components).
# ls -l /sys/block/sd?/sd??/bcache/{dev,set}
lrwxrwxrwx. 1 root root 0 Jan 7 10:08 /sys/block/sdb/sdb1/bcache/dev -> ../../../../../../../../../../virtual/block/bcache0
lrwxrwxrwx. 1 root root 0 Jan 7 11:53 /sys/block/sdb/sdb2/bcache/dev -> ../../../../../../../../../../virtual/block/bcache1
lrwxrwxrwx. 1 root root 0 Jan 7 11:53 /sys/block/sdc/sdc1/bcache/set -> ../../../../../../../../../../../fs/bcache/9945e165-0604-4f29-94bd-b155d01080ad
As was done with previous software block devices [1][2][3][4] show the
bcache (access) device as the mount point of a backing device
(component). Use the /sys/block/DEV[/PTN]/bcache/dev sysfs symlinks to
provide the bcache device names. Bcache cache devices (components)
don't get mount points because they aren't accessible.
[1] commit 8083f11d84
Display LVM2 VGNAME as the PV's mount point (#160787)
[2] commit f6c2f00df7
Populate member mount point with SWRaid array device (#756829)
[3] commit 538c866d09
Display array device as mount point of mdadm started ATARAID members
(#75)
[4] commit 538c866d09
Display array device as mount point of mdadm started ATARAID members
(#75)
Closes#183 - Basic support for bcache
GParted automatically enables the Partition > Unmount action for busy
partitions. Unregister (deactivate) bcache devices is not going to be
supported so disable it.
Closes#183 - Basic support for bcache
Make (format as) bcache backing device (-B) and cache device (-C) and
implicitly attach the backing device to the cache to enable caching, all
in one.
# bcache make -B /dev/sdb1 -C /dev/sdc1
# bcache show
Name Type State Bname AttachToDev
/dev/sdb1 1 (data) clean(running) bcache0 /dev/sdc1
/dev/sdc1 3 (cache) active N/A N/A
After experimenting with 'bcache unregister', 'bcache register' and
stracing 'bcache show' the bcache kernel module creates the sysfs
directory /sys/block/DEV[/PTN]/bcache and it's contents only when the
bcache device is registered with the kernel (bcache component is
active). Use this to identify whether any bcache device (component)
should be displayed as active or not in GParted.
# ls -ld /sys/block/sd?/sd?1/bcache
drwxr-xr-x. 6 root root 0 Jan 7 10:08 /sys/block/sdb/sdb1/bcache
drwxr-xr-x. 2 root root 0 Jan 7 10:08 /sys/block/sdc/sdc1/bcache
Closes#183 - Basic support for bcache
Add pattern to recognise block cache devices as valid devices for
GParted to work with. Devices are named by the Linux kernel device
driver like /dev/bcache0 [1] with partitions named like /dev/bcache0p1
[2].
Note bcache devices can be partitioned but all the documents I have seen
guide users to create file systems directly in a bcache device and not
partition it [3][4] (plus all other Internet search results I looked
at). This might be because bcache is a specialist use case and the
bcache backing device can be a partition itself.
[1] linux 5.15 drivers/md/bcache/super.c bcache_device_init()
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/md/bcache/super.c?h=v5.15#n945
[2] Contents of /proc/partitions for a bcache partitioned backing device
$ grep bcache /proc/partitions
251 0 524280 bcache0
251 1 523256 bcache0p1
[3] Linux kernel document: A block layer cache (bcache)
https://www.kernel.org/doc/Documentation/bcache.txt
[4] The Linux kernel user's and administrator's guide > A block layer
cache (bcache)
https://www.kernel.org/doc/html/latest/admin-guide/bcache.htmlCloses#183 - Basic support for bcache
Use blkid to detect bcache formatted devices. Requires blkid from
util-linux >= 2.24 for detection of bcache devices [1].
Use util-linux's FS images when testing GParted detection.
# wget http://git.kernel.org/cgit/utils/util-linux/util-linux.git/plain/tests/ts/blkid/images-fs/bcache-B.img.xz
# xzcat bcache-B.img.xz > /dev/sdb1
# wget http://git.kernel.org/cgit/utils/util-linux/util-linux.git/plain/tests/ts/blkid/images-fs/bcache-C.img.xz
# xzcat bcache-C.img.xz > /dev/sdc1
# blkid /dev/sdb1 /dev/sdc1
/dev/sdb1: UUID="8fb7f716-4c19-4517-bfbb-6f4a2becad60" TYPE="bcache" PARTUUID="f8f1485e-01"
/dev/sdc1: UUID="7a343627-ac87-4bf0-b76f-46067cbc9b8c" TYPE="bcache" PARTUUID="f46e8c86-01"
To tidy-up after testing GParted detection, stop the bcache device in
case it was automatically started and wipe the signatures. This is to
prevent udev rules from automatically starting the bcache device on
every subsequent reboot.
# echo 1 > /sys/block/sdb/sdb1/bcache/stop
# wipefs -a /dev/sdb1 /dev/sdc1
Closes#183 - Basic support for bcache
Currently the Face Skin colour range from the GNOME palette represent a
mixture of file systems and software block devices:
JFS - Face Skin Medium
LVM2_PV - Face Skin Dark
NILFS2 - Face Skin Shadow
LINUX_SWRAID - Dark Brown
ATARAID - Dark Brown
We are about to add recognition of bcache [1][2][3], another software
block device. Reorganise the colour assignments so that Face Skin
colour range is exclusively used by types of software block devices and
assign JFS and NILFS2 new colours.
Face Skin Light (#EFE0CD) -
Face Skin Medium (#E0C39E) - BCACHE [New assignment]
Face Skin Dark (#B39169) - LVM2_PV
Face Skin Shadow (#826647) - LINUX_SWRAID [New assignment]
Brown Dark (#5A4733) - ATARAID
NILFS2 has flash friendly characteristics [4] so use Accent Red colours
along with F2FS.
Accent Red (#DF421E) - F2FS
Accent Red Dark (#990000) - NILFS2 [New assignment]
Move JFS to a group with XFS and UFS. The hue of the GNOME palette
Accent Yellow Dark colour, as used by UFS, was more Orange compared to
Accent Yellow and a bit too close to the Orange used by BTRFS. So use
the hue of the GNOME Accent Yellow and extend the range and assign like
this.
Accent Yellow (#EED680) - XFS
Accent Yellow Dark (#D6B129) - JFS [Updated hue.
New assignment]
Accent Yellow Shadow (#AA8F2C) - UFS [New colour.
New assignment]
Accent Yellow Dark Shadow (#826F2B) - [New colour]
[1] bcache
https://bcache.evilpiepirate.org/
[2] Linux kernel document: A block layer cache (bcache)
https://www.kernel.org/doc/Documentation/bcache.txt
[3] The Linux kernel user's and administrator's guide > A block layer
cache (bcache)
https://www.kernel.org/doc/html/latest/admin-guide/bcache.html
[4] NILFS, Relative performance
https://en.wikipedia.org/wiki/NILFS#Relative_performanceCloses#183 - Basic support for bcache
GParted currently performs these relevant steps for a copy operation:
1. Check source file system
2. Copy to destination
3. If destination partition is larger than source
3.1. check destination file system
3.2. grow destination file system
Always checking the source was added by this commit:
a54b52ea33
xfs copy now uses xfsdump and xfsrestore. icw some hacks in the other 2
...
Also the source file system is now checked before the actual copy
is performed. If damaged beyond repair, the copy won't start.
I think users have an expectation that a copy operation accesses the
source read-only and performing a file system check on the source
breaks that expectation. Also uses may accidentally or deliberately try
to copy a file system off a failing drive, where trying to check the
source could lead to further data loss. (ddrescue is the proper tool
for recovering data from damaged drives but ddrescue is not a tool the
user may know -- they know about GParted).
For a failing drive, not checking the source first means the copy might
fail instead, however a failed copy is preferable to greater chance of
further damaging the source file system by checking it.
In order to keep the new process as similar as possible to before,
always check the destination instead. This changes the copy operation
from performing one or two file system checks to always performing only
one check. The new relevant copy operation steps are:
1. Copy to destination
2. check destination file system
3. If destination partition is larger than source
3.1. grow destination file system
This has exactly the same error handling as before, if the check of the
destination file system fails the operation stops and any grow step
won't be performed. This represents the minimum change in behaviour
from before.
Bug 723571 - Copying ext4 partitions should not fix the source but the
destination
Closes!95 - Check copy destination instead of source
Since the only use of SWRaid_Info::get_uuid() assign the returned value
this doesn't actually save any copy construction. Do it for consistency
with the other get_*() methods in SWRaid_Info.
Closes!94 - Make more getter methods use return-by-constant-reference
Have to use a second constant reference variable array_path_2 in
GParted_Core::set_mountpoints() because by design C++ does not implement
rebinding of references [1].
[1] why doesn't C++ allow rebinding a reference?
https://stackoverflow.com/questions/27037744/why-doesnt-c-allow-rebinding-a-referenceCloses!94 - Make more getter methods use return-by-constant-reference
All uses of get_description() copy construct to a local variable, not
assign to a reference, so this doesn't save anything. It is just being
done to be consistent with making other getters return a constant
reference.
Closes!94 - Make more getter methods use return-by-constant-reference
Method already returned a constant reference. Change local variables to
constant references to avoid copy constructing them.
Closes!94 - Make more getter methods use return-by-constant-reference
A number of GParted methods named get_*() are returning properties and
are return-by-value. For objects this implies the returned value is
copy constructed and later destroyed when it goes out of scope. Change
them to use return-by-constant-reference to avoid unnecessary object
duplication. Just have to make sure the reference goes out of scope
before the referenced object is destroyed to avoid having a reference
(pointer) to freed memory. Assigning to a local variable instead of
of a local reference still duplicates the object so can be used when the
lifetime of the gotten object needs to be longer that the referenced
object.
Previously done for other getters here:
d948cbcb91
Make get_custom_text() and get_generic_text() return by reference
This change just makes Device::get_path() return a constant reference
and updates uses to match to avoid copy constructing the returned value.
Closes!94 - Make more getter methods use return-by-constant-reference
The first two clauses say is the partition object type unallocated
inside an extended partition, or a logical partition (which only occur
inside an extended partition). As these are the only two partition
object types which can occur inside an extended partition, this is
equivalent to saying is the partition object inside an extended
partition. Simplify.
The create new and paste into new dialogs are both composing a new
partition into the unallocated selected_partition they are passed. The
starting sector of this unallocated partition is the first sector the
newly composed partition could possibly have. To ensure it doesn't
overlay with the partition table or EBR (Extended Boot Records) it calls
MB_Needed_for_Boot_Record() to indicate if the first 1 MiB needs to be
reserved in the dialog or not.
Code:
Dialog_Partition_New::set_data(..., selected_partition, ...)
...
MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record(selected_partition);
Dialog_Partition_Copy::set_data(selected_partition, ...)
...
MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record(selected_partition);
However the resize/move dialog is different. It is passed the existing
selected_partition object and the vector of partitions from which it
determines if there is a previous unallocated partition object or not.
When there is no previous unallocated partition object, there is no need
to reserve 1 MiB because the selected_partition can't be moved further
to the left. In the other case when there is a previous unallocated
partition object the start of the existing selected_partition can be
moved to the left. However the code passes the selected_partition
object to MB_Needed_for_Boot_Record() so doesn't have the first sector
the newly composed partition could possibly have so doesn't reserve the
first 1 MiB to prevent it overlapping with the partition table at the
start of the drive. These two commits addressed this limitation:
* Ensure 1 MiB reserved when moving extended partition to start of drive
* Ensure 1 MiB reserved when moving partition to start of disk
Code:
Dialog_Partition_Resize_Move::set_data(selected_partition, ...)
new_partition = selected_partition.clone();
if (selected_partition.type == TYPE_EXTENDED)
Resize_Move_Extended(...);
else
Resoze_Move_Normal(...);
Dialog_Partition_Resize_Move::Resize_Move_Normal(...)
...
if (previous <= 0)
MIN_SPACE_BEFORE_MB = 0;
else
{
if (START < MEBIBYTE / new_partition->sector_size)
MIN_SPACE_BEFORE_MB = 1;
else
MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record(*new_partition);
}
Dialog_Partition_Resize_Move::Resize_Move_Extended(...)
...
if (previous <= 0)
MIN_SPACE_BEFORE_MB = 0;
else
{
if (START < MEBIBYTE / new_partition->sector_size)
MIN_SPACE_BEFORE_MB = 1;
else
MIN_SPACE_BEFORE_MB = Dialog_Base_Partition::MB_Needed_for_Boot_Record(*new_partition);
}
So instead pass the previous unallocated partition object as that
contains the left most start sector the newly composed partition could
possibly have, therefore the check for overlapping the partition table
in the first 1 MiB of the drive can succeed.
On an MSDOS partitioned drive, create an extended partition at least 2
MiBs from the start of the drive. Like this:
dd if=/dev/zero bs=1M of=dev/sdb
echo 4096,4096,5 | sfdisk -uS --force /dev/sdb
The resize/move dialog appears to allow the extended partition to be
moved and/or resized to the very start of the drive, over the top of the
partition table, without reserving the first 1 MiB. Ensure the dialog
reserves the first 1 MiB, like it does when moving normal partitions.
Reference:
ceab9bde57
Ensure 1 MiB reserved when moving partition to start of disk
This case is an extension of the setup in the previous commit. Add a
second partition several megabytes after the first. It looks like this:
[TABLE][PTN#1] [PTN#2]
<-- 1st MiB -><- several MiBs ->
Just need to make the gap 2 MiB or more so that it can be seen what the
resize/move dialog is allowing. Setup like this using an 8 MiB gap and
8 MiB partition #2.
For MSDOS use:
dd if=/dev/zero bs=1M of=/dev/sdb
(echo 1,2047; echo 18432,16384) | sfdisk -uS --force /dev/sdb
mkswap /dev/sdb2
For GPT use:
sgdisk --zap-all /dev/sdb
sgdisk --set-alignment=1 --new 1:34:2047 /dev/sdb
sgdisk --new 2:18432:34815 /dev/sdb
mkswap /dev/sdb2
In GParted try to move partition sdb2 to the left as much as possible,
or try to resize the start to the left as much as possible. GParted
insists on having a 1 MiB of padding before the start of sdb2, forcing
it to start at sector 4096, even though sector 2048 is free and aligns
to whole megabytes.
Delete the preceding partition. Now GParted allows sdb2 to be moved or
resize to start at sector 2048.
Fix another off by one error in the sector comparison for the
resizing/moving of partitions.
Closes#172 - GParted doesn't allow creation of a partition starting at
sector 2048 if there is a partition before it
[This commit message and test case is written assuming a drive with a
(logical) sector size of 512 bytes. GParted equally well works with
other sector sizes because the limit is expressed as 1 MiB / sector
size. Adjust the test case sector counts as needed when testing with
different sector sized drives.]
Prepare an MSDOS or GPT partitioned disk with the first partition within
the first 1 MiB.
For MSDOS use:
dd if=/dev/zero bs=1M of=/dev/sdb
echo 1,2047 | sfdisk -uS --force /dev/sdb
For GPT use:
sgdisk --zap-all /dev/sdb
sgdisk --set-alignment=1 --new 1:34:2047 /dev/sdb
In GParted create a new partition on /dev/sdb as near to the start of
the drive as possible. GParted insists on added an extra 1 MiB of space
before the new partition, making it start at sector 4096, even though
sector 2048 is free and aligns to whole megabytes.
Delete the preceding partition. Now GParted allows the new partition
to be created starting at sector 2048.
GParted only needs to add padding of 1 MiB to account for the partition
table at the start of the drive when the possible start is within the
first MiB, not already at the first MiB. Fix this off by one error in
the sector comparison.
The reason this has bug has never occurred before is because it is very
unusual to have the first partition entirely within the first 1 MiB.
Normally either the (start of) the drive is free so GParted creates an
unallocated partition object starting at sector 0, so adding 1 MiB of
padding to preserve the partition table is correct; or the first
partition starts at 1 MiB so the possible start for a second partition
is much later in the drive.
Closes#172 - GParted doesn't allow creation of a partition starting at
sector 2048 if there is a partition before it
Accessibility relations are essential for usage with screen readers. It
enables the screen reader to read the corresponding label along with the
value of a widget when it gains focus, rather than just the value of the
widget itself.
Test by running Orca screen reader and tab around the elements of the UI
and listen to what is read out. For example before it would be
"500 GiB", where as after it would be "Unused 500 GiB".
Closes!92 - Add accessibility relations
A user reported that scrolling the mouse wheel quickly crashes GParted
[1]. The minimum setup required to reproduce this crash is 3 drives
partitioned like this:
sda - any
sdb - some unallocated space
sdc - no unallocated space
Then move the pointer over the drive selection combobox and scroll the
mouse wheel quickly downwards.
The sequence of events goes like this:
1. The first scroll wheel down event causes the device combobox
selection to change to the second entry (sdb), which calls
combo_devices_changed() -> Refresh_Visual().
2. Refresh_Visual() sets selected_partition_ptr to point to the largest
unallocated space partition object in sdb.
3. The first Gtk event processing loop in Refresh_Visual() comes
across the next scroll wheel down event. This changes the selection
to the third entry (sdc), which makes a recursive call to
combo_devices_changed() -> Refresh_Visual().
4. Refresh_Visual() sets selected_partition_ptr to NULL as sdc has no
unallocated space and returns.
5. The first call to Refresh_Visual() resumes after the first Gtk event
loop, continuing the processing for drive sdb. As sdb has a
largest unallocated space partition (largest_unalloc_size >= 0),
DrawingAreaVisualDisk::set_selected() is called with the now
NULL selected_partition_ptr.
6. One of the DrawingAreaVisualDisk::set_selected() methods
dereferences the NULL pointer, crashing GParted.
As a comment says of selected_partition_ptr "Lifetime: Valid until the
next call to Refresh_Visual()." It just wasn't expected that the next
call to Refresh_Visual() would be half way through Refresh_Visual()
itself.
Considered but rejected fixes:
1. Remove automatic selection of the largest unallocated space.
Removes functionality.
2. Return at the top of Refresh_Visual() when called recursively.
This causes GParted to not update the main window with the latest
selected drive. In the above example the combobox would show sdc,
but the main window graphic and partition list would have only been
updated once showing sdb, the first scrolled selection.
Fix by only having a single Gtk event processing loop at the end of
Refresh_Visual() with the optional calls to select the largest
unallocated partition before that loop. This makes it impossible to
call the ::set_selected() methods with selected_partition_ptr modified
by a recursive call.
This fix reverts this earlier commit:
0fb8cce699
Reduce flashing redraw from automatic partition selection (#696149)
That commit was in GParted 0.20.0 when Gtk 2 was still used. Re-testing
now doesn't see any flashing redrawing from the automatic partition
selection, with GParted currently using Gtk 3.
[1] Debian bug #991998 - gparted segfaults if scrolling quickly the
device dropdown list
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=991998Closes#165 - GParted segfaults if scrolling quickly in the device
dropdown
Compiling GParted on an older distribution with gtk+-3.0 < 3.22.0, where
HAVE_GTK_SHOW_URI_ON_WINDOW is undefined, produces this warning message:
Win_GParted.cc: In member function 'void GParted::Win_GParted::show_help(const Glib::ustring&, const Glib::ustring&)':
Win_GParted.cc:1822:56: warning: operation on 'gscreen' may be undefined [-Wsequence-point]
GdkScreen *gscreen = gscreen = gdk_screen_get_default();
^
Found on Ubuntu 16.04 LTS with gtk+ 3.18.0.
Stop double assigning to gscreen. Fixes commit:
26f4dc504a
Replace deprecated gtk_show_uri() method for help window (!82)
Creating a grep process to check if a particular mount is still mounted
is an unnecessary overhead. All that is needed is for the Mount_Info
module to refresh it's copy of /proc/mounts and query that.
To keep the code as simple as possible just reload the whole of the
Mount_Info module and query the mount cache to determine if the
particular block device is still mounted at this particular mount point.
This therefore re-reads /proc/mounts (necessary) and /proc/swaps and
/etc/fstab (unnecessary). This is still much less overhead than
creating a separate grep process.
Closes!89 - Fix unmount error when unmounting below a bind mount point
Bind mounts duplicate part of the file system hierarchy to an additional
mount point [1]. When mounting and unmounting a second file system
below a duplicating bind mount Linux automatically presents this lower
file system as being mounted multiple times. GParted displays these
multiple mount points. However using GParted to unmount this lower file
system reports an error that the second mount point is no longer
mounted, because all were unmounted by the first unmount command.
Setup:
1. Mount an upper file system
# mkdir /mnt/1
# mount /dev/sdb1 /mnt/1
# fgrep sdb /proc/mounts
/dev/sdb1 /mnt/1 ext4 rw,seclabel,relatime,data=ordered 0 0
2. Bind mount it to a second directory
# mkdir /mnt/b1
# mount --bind /mnt/1 /mnt/b1
# fgrep sdb /proc/mounts
/dev/sdb1 /mnt/1 ext4 rw,seclabel,relatime,data=ordered 0 0
/dev/sdb1 /mnt/b1 ext4 rw,seclabel,relatime,data=ordered 0 0
3. Mount a file system below the first
# mkdir /mnt/1/lower
# mount /dev/sdb2 /mnt/1/lower
# fgrep sdb /proc/mounts
/dev/sdb1 /mnt/1 ext4 rw,seclabel,relatime,data=ordered 0 0
/dev/sdb1 /mnt/b1 ext4 rw,seclabel,relatime,data=ordered 0 0
/dev/sdb2 /mnt/1/lower xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0
/dev/sdb2 /mnt/b1/lower xfs rw,seclabel,relatime,attr2,inode64,noquota 0 0
Two mount records for sdb2 were added to /proc/mounts from one mount
command.
Then use GParted to unmount sdb2. It reports this error dialog:
+-------------------------------------+
| |
| Could not unmount /dev/sdb2 |
| |
| # umount -v '/mnt/b1/lower' |
| umount: /mnt/b1/lower: not mounted. |
+-------------------------------------+
| [ OK ] |
+-------------------------------------+
Fix by checking that the file system is still mounted before each
unmount attempt.
[1] mount (8), Description, Bind mount operation
https://man7.org/linux/man-pages/man8/mount.8.html#DESCRIPTIONCloses!89 - Fix unmount error when unmounting below a bind mount point
Extra hint of warning status being represented by enumeration constant
STATUS_N_A is no longer needed since commit:
8c5c13d613
Rename OperationDetailStatus STATUS_N_A to STATUS_WARNING
Implemented the second half of the solution described in the previous
commit. Resolve UUID= and LABEL= references when searching in the
Mount_Info cache so that mount points of encrypted file systems listed
in /etc/fstab can be found using the later added FS_Info details.
Closes#162 - It is no longer possible to mount a LUKS encrypted file
system
ISSUE DETAILS
GParted no longer enables Partition > Mount on, for unmounted encrypted
file systems listed in /etc/fstab.
Steps to reproduce:
1. Create LUKS mapping and open.
# cryptsetup luksFormat /dev/sdb1 -
# cryptsetup luksOpen /dev/sdb1 sdb1_crypt
2. Create any file system.
# mkfs.ext4 /dev/mapper/sdb1_crypt
# uuid=`blkid -o value -s UUID /dev/mapper/sdb1_crypt`
3. Add /etc/fstab entry.
# mkdir /mnt/1
# echo "UUID=$uuid /mnt/1 ext4 defaults 0 0" >> /etc/fstab
4. Run GParted and try Partition > Mount on.
With GParted >= 1.3 no mount point is available. With GParted <= 1.2
mount point /mnt/1 is available.
EXPLANATION
Up until GParted 1.2.0 it worked like this:
1. Ran blkid and loaded the details for every file system into the
FS_Info cache. This included results for file systems in open LUKS
mappings, such as /dev/mapper/sdb1_crypt in the above example.
2. Read /etc/fstab, resolved UUID= and LABEL= references into block
device names and added those into the Mount_Info cache.
3. Looped through all partitions adding mount points known by the
Mount_Info cache.
After the changes for issue #131 "GParted hangs when non-named device is
hung" and issue #148 "Encrypted file systems are no longer recognised"
it works like this instead:
1. Runs blkid for specified devices and partitions only and loads file
system details into the FS_Info cache. Does not include open LUKS
mappings so no results for those file systems.
2. Loading of /etc/fstab into the Mount_Info cache is unable to resolve
UUID= and LABEL= references for file systems in LUKS mappings, so
they aren't included.
3. No mount points known for encrypted file systems.
Note that currently when an encrypted file system is added into the data
model it extends the FS_Info cache <2>, but this is after the Mount_Info
cache has been loaded <1>. Call flow is like this:
GParted_Core::set_devices_thread()
FS_Info::clear_cache()
FS_Info::load_cache_for_paths()
1> Mount_Info::load_cache()
...
set_device_from_disk()
set_device_one_partition() / set_device_partitions()
set_luks_partition()
detect_filesystem_in_encryption_mapping()
2> FS_Info::load_cache_for_paths()
...
set_mountpoints()
partition.add_mountpoints(Mount_Info::get_fstab_mountpoints())
SOLUTION
Also save unresolved UUID= and LABEL= references from /etc/fstab into
the Mount_Info cache. Then when searching the Mount_Info /etc/fstab
cache resolve encountered UUID= and LABEL= references.
THIS COMMIT
Also save unresolved UUID= and LABEL= references into the Mount_Info
cache.
Closes#162 - It is no longer possible to mount a LUKS encrypted file
system
E2label works the same way whether an ext2/3/4 file system is mounted or
not, by directly reading and writing the superblock from the partition
block device. (Technically the superblock will already be in the kernel
device buffer cache because the kernel has the ext2/3/4 file system
mounted and a reference to the superblock in the device buffer cache).
This is safe and supported as confirmed here [1]. As this method has
always worked, even on the oldest distributions, unconditionally enable
this feature.
# mkfs.ext4 -L label_1 /dev/sdb3
# mount /dev/sdb3 /mnt/3
# e2label /dev/sdb3 label_2
# blkid /dev/sdb3
/dev/sdb3: LABEL="label_2" ...
[1] Is labelling a mounted ext2/3/4 file system safe and supported?
https://lore.kernel.org/linux-ext4/CAMU1PDgNvW_3Jr91iii-Nh=DCuRytVG8ka3-J+a43gKwigx8Yg@mail.gmail.com/T/#u
Bug 600496 - Allow changing ext2/3 label without unmounting
Closes#163 - Feature request: set label on a mounted btrfs
XFS also supports labelling of the file system while it is mounted.
This was added into Linux kernel 4.18 [1] and xfsprogs 4.17.0 [2].
These versions are newer than available in older but still supported
distributions so we'll need to detect versions before enabling support.
These are the oldest releases of distributions which meet the
requirements.
Distro EOL Linux kernel xfsprogs
Debian 10 2024-Jun 4.19.0 4.20.0
RHEL 8 2029-May 4.18.0 5.0.0
Ubuntu 20.04 LTS 2030-Apr 5.4 5.3.0
As with btrfs a mounted XFS is labelled via it's mount point:
# mkfs.xfs -L label_1 /dev/sdb2
# mount /dev/sdb2 /mnt/2
# xfs_io -c 'label -s mnt_label_2' /mnt/2
label = "mnt_label_2"
# blkid /dev/sdb2
/dev/sdb2: LABEL="mnt_label_2" ...
And cleared with:
# xfs_io -c 'label -c' /mnt/2
label = ""
Note that in some error situations xfs_io reports exit status zero and
writes the failure message to stdout.
# xfs_io -c 'label -s "mnt label 3"' /mnt/2
bad argument count 4 to label, expected between 0 and 3 arguments
# echo $?
0
Therefore determine success based on stdout starting with 'label = "'
reporting the new label or not.
Also note that as seen in this failure case, it is not possible to set
an XFS label which contains a space character using xfs_io. However
that is nothing new as that can't be done using the existing xfs_admin
command for an unmounted XFS either.
# umount /mnt/2
# xfs_admin -L 'umnt label 4' /dev/sdb2
Usage: xfs_admin [-efjlpuV] [-c 0|1] [-L label] [-U uuid] device
# echo $?
2
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f7664b31975bd893190708e76b2c424328f0c49b
xfs: implement online get/set fs label
[2] https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/commit/?id=cfa10b0f972005b38ed294bca66cebf2f65298ec
xfs_io: add label command
Closes#163 - Feature request: set label on a mounted btrfs
Show support for online labelling using a second tick mark in the
Features dialog. This matches how online grow and shrink are shown.
Closes#163 - Feature request: set label on a mounted btrfs
Btrfs supports labelling of the file system while it is mounted. This
was added into Linux kernel 3.10 [1] and btrfs-progs 3.12 [2]. As the
oldest supported distributions have the needed versions or newer,
unconditionally enable without any checking for availability.
Distro EOL Linux kernel btrfs-progs
Debian 9 2022-Jun 4.19 4.7.3
RHEL / CentOS 7 2024-Jun 3.10.0 4.9.1
Ubuntu 18.04 LTS 2023-Apr 4.15.0 4.15.1
Unmounted btrfs is labelled via the block device containing it, where as
a mounted btrfs is labelled via it's mount point.
# mkfs.btrfs -L initial_label /dev/sdb1
# blkid /dev/sdb1
/dev/sdb1: LABEL="initial_label" ...
# btrfs filesystem label /dev/sdb1 unmounted_label_2
# blkid /dev/sdb1
/dev/sdb1: LABEL="unmounted_label_2" ...
# mount /dev/sdb1 /mnt/1
# btrfs filesystem label /dev/sdb1 mounted_label_3
# blkid /dev/sdb1
/dev/sdb1: LABEL="mounted_label_3" ...
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a8bfd4abea3da0e28f215e2a2b8c2f1ca27ebe80
Btrfs: set/change the label of a mounted file system
[2] https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git/commit/?id=619dc61cae1420da2dec48f689d49b9b346abc15
Btrfs-progs: Change the label of a mounted file system
Closes#163 - Feature request: set label on a mounted btrfs
For the same reason as in the previous commit, the UUID is copied when
copying every file system type except for XFS, where a new XFS is
created with a new UUID.
Again preview of the copy operation expects the UUID to be copied.
(Look in Partition > Information of the source and pasted partitions
before the operation is applied).
Fix as before by specifying the desired file system UUID when creating
the new XFS as part of the copy operation.
However there is an extra complication. The XFS kernel driver refuses
to mount a file system with a duplicate UUID of an already mounted XFS.
# mkfs.xfs -L xfs_copy /dev/sdb1
# mount /dev/sdb1 /mnt/1
# tail -2 /var/log/messages
Jun 3 21:41:24 localhost kernel: XFS (sdb1): Mounting V5 Filesystem
Jun 3 21:41:24 localhost kernel: XFS (sdb1): Ending clean mount
# /dev/sdb1: LABEL="xfs_copy" UUID="d654fc7f-e417-4ec6-88e8-8a7d0d46b7d8" TYPE="xfs"
# mkfs.xfs -L xfs_copy -m uuid="d654fc7f-e417-4ec6-88e8-8a7d0d46b7d8" /dev/sdb2
# mount /dev/sdb2 /mnt/2
mount: wrong fs type, bad option, bad superblock on /dev/sdb2,
missing codepage or helper program, or other error.
In some cases useful info is found in syslog - try
dmesg | tail or so.
# echo $?
32
# tail -1 /var/log/messages
Jun 3 21:41:31 localhost kernel: XFS (sdb2): File system has duplicate UUID d654fc7f-e417-4ec6-88e8-8a7d0d46b7d8 - can't mount
Handle this specifying the needed option [1] when mounting the second
XFS during the copy.
# mount -o nouuid /dev/sdb2 /mnt/2
# mount | grep /dev/sdb
/dev/sdb1 on /mnt/1 type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/sdb1 on /mnt/1 type xfs (rw,relatime,seclabel,nouuid,attr2,inode64,noquota)
Duplicating the UUID may seem troublesome, but it is being done:
1. To make the GParted copied XFS be as much a clone as possible, to
match what is does with other file systems.
2. It has a valid use case; of cloning a Linux installation to a new
drive or restoring a partition image backup. In these cases it is
much simpler if the UUID of the copy remains the same because it
avoids having to edit GRUB2 configuration and fstab file system
mounting which is nearly always done via UUID.
3. GParted has the new UUID operation, to change the UUID for cases
when a copied file system needs to be mounted at the same time as
the source.
[1] xfs(5) - xfs - layout, mount options, and supported file attributes
for the XFS filesystem
https://man7.org/linux/man-pages/man5/xfs.5.html
"MOUNT OPTIONS
...
nouuid Don't check for double mounted file systems using the file
system uuid.
"
Closes!85 - Make XFS copy duplicate the file system label and UUID
As GParted performs block copy of partitions then the label, which is
stored in the file system superblock, is also copied. This is true for
copies performed using the GParted internal block copy and for EXT2/3/4
and NTFS which are copied using the file system specific commands
e2image and ntfsclone respectively. However when an XFS file system is
copied the label is not copied because a new file system is created
using mkfs.xfs and the files copied using xfsdump | xfsrestore.
Preview of the copy operation in GParted also reflects the fact that the
label will be copied.
Fix this by simply specifying the desired label when creating the new
destination XFS.
Closes!85 - Make XFS copy duplicate the file system label and UUID
User reported that GParted didn't detect their eMMC drive [1]. Not
recognised device name was /dev/mmcblk0. Confirmed that the regression
was introduced by this commit [2]. Fix the code and regular expression
used to recognise SD/MMC device names.
[1] GParted forum thread: eMMC drive not detected...?
http://gparted-forum.surf4.info/viewtopic.php?id=17994
[2] 52930f30ae
Refactor load_proc_partitions_info_cache() a bit (#131)
Closes!83 - Fix recognition of SD/MMC device names
So far when prompting for the LUKS passphrase the dialog always looks
like this:
+------------------------------------------------+
| LUKS Passphrase /dev/sdb1 |
+------------------------------------------------+
| Enter LUKS passphrase to open /dev/sdb1 |
| Passphrase: [ ] |
| |
| |
| [ Cancel ] [ Unlock ] |
+------------------------------------------------+
Specifically the first line of the dialog says the reason to provide the
passphrase is to open the encryption mapping. Now the passphrase may
also be requested when resizing the encryption mapping, as part of a
resize of check operation, show the appropriate reason in the password
dialog.
Closes#59 - Resize of LUKS2 encrypted file system fails with "Nothing
to read on input"
A check operation involves (1) checking the file system, (2) growing the
encryption volume and (3) growing the file system. Therefore prompt for
the LUKS passphrase as required when composing a check operation too.
Closes#59 - Resize of LUKS2 encrypted file system fails with "Nothing
to read on input"
This is the final piece which enables GParted to pass the LUKS
passphrase when resizing an open LUKS encryption mapping when
'cryptsetup resize' will prompt for it.
Closes#59 - Resize of LUKS2 encrypted file system fails with "Nothing
to read on input"
This is the equivalent to what was previously done when adding opening
of LUKS mappings. Namely to add a way to pass the LUKS passphrase to
'cryptsetup luksOpen' via standard input. Previously the functionality
was added to Utils::execute_command() [1]. Now it is also needed to
pass the LUKS passphrase to 'cryptsetup resize', which is executed as
part of applying resize and check operations to an encrypted file
system. So add this functionality to FileSystem::execute_command().
For now writing to stdin is only needed for the one variant of
FileSystem::execute_command() which doesn't have progress tracking
callbacks. Writing to stdin can easily be added to the other progress
tracking callback variants of execute_command() when needed.
[1] 8dff80edc6
Add ability for small writes to stdin of child processes (#795617)
Closes#59 - Resize of LUKS2 encrypted file system fails with "Nothing
to read on input"
When composing a resize operation on an open encryption mapping, use the
existing LUKS password dialog to prompt for the passphrase, if and only
if 'cryptsetup resize' will prompt and GParted doesn't already have a
password. 'cryptsetup resize' will prompt for a LUKS passphrase when
the passphrase was stored in the kernel keyring service,
key_loc == KEYLOC_KeyRing. See the previous commit "Capture LUKS
mapping master encryption key location (#59)" for more details.
As commented in the code GParted specifically doesn't support the case
where the LUKS passphrase is changed while GParted is running and it
knew the old passphrase. When resizing an open encryption mapping
GParted will just pass the old out of date passphrase it knows and the
resize will fail like this:
# cryptsetup status sdb2_crypt | egrep 'type|key location'
type: LUKS2
key location: keyring
# dmsetup table --target crypt
sdb2_crypt: 0 491520 crypt aes-xts-plain64 :64:logon:cryptsetup:3d040240-97ba-4559-af98-72c3be500498-d0 0 8:18 32768
# echo -n oldpassword | cryptsetup -v --size 491520 resize sdb2_crypt
No key available with this passphrase.
Command failed with code -2 (no permission or bad passphrase).
# echo $?
2
To work around this either close and restart GParted or close and reopen
the encryption mapping. The former because GParted doesn't save
passwords across a restart so will prompt and the latter because GParted
will use the wrong old passphrase to try to open the mapping and then
prompt for the correct passphrase until successfully opened.
Closes#59 - Resize of LUKS2 encrypted file system fails with "Nothing
to read on input"