When blanking of udev rules was first tested [1][2] and added [3] all
the distributions at the time (CentOS 6, Debian 6, Fedora 19,
openSUSE 12.2, Ubuntu 12.04 LTS) stored the system default rules in
directory /lib/udev/rules.d. Now most distributions (CentOS Stream 9,
Debian 11, Fedora 38, Ubuntu 22.04 LTS, openSUSE Leap 15.4) store the
system default rules in directory /usr/lib/udev/rules.d. Most of these
distributions have a merged /usr file system [4][5] so /lib is a symlink
to /usr/lib and the system default rules can still found using the
original directory. But openSUSE 15.4 doesn't have a merged /usr so the
gparted shell wrapper doesn't find the system default rules in directory
/usr/lib/udev/rules.d and doesn't prevent auto starting of Linux
Software RAID arrays and bcache devices during a storage probe.
An extra consideration is that Alpine Linux 3.17 doesn't have a merged
/usr file system, but has both /lib/udev/rules.d and
/usr/lib/udev/rules.d directories with different rules files. Therefore
fix this by checking for system default udev rules in both directories.
[1] Bug 709640 - Linux Swap Suspend and Software RAID partitions not
recognised, comment 7
https://bugzilla.gnome.org/show_bug.cgi?id=709640#c7
[2] Bug 709640 - Linux Swap Suspend and Software RAID partitions not
recognised, comment 12
https://bugzilla.gnome.org/show_bug.cgi?id=709640#c12
[3] a255abf343
Prevent GParted starting stopped Linux Software RAID arrays (#709640)
[4] The Case for the /usr Merge
http://0pointer.de/blog/projects/the-usr-merge
[5] The Case for the /usr Merge
https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/Closes!116 - Systemd mount masking and udev rule location updates
After the previous commit "Stop masking the root file system mount
unit", GParted now reports this error to the terminal on Debian 10 and
11:
# gparted
To few arguments.
GParted 1.5.0-git
configuration --enable-online-resize
libparted 3.2
Debian installations, at least on PC hardware and using BIOS booting so
they don't have a /boot/efi file system, only have a single file system,
/ (root). That is now excluded from masking so gparted shell wrapper
runs systemctl without any mount units to mask. Hence the error.
# systemctl --runtime mask --quiet --
Too few arguments.
# echo $?
1
Fix this by only masking and unmasking units when the list of mounts
is non-empty.
Closes!116 - Systemd mount masking and udev rule location updates
Masking the root file system (-.mount) unit lead to a Debian package
upgrade failing as reported here [1]. This was fixed in systemd 245
[2][3] by not allowing perpetual units to be masked. As the root file
system can't be mounted or unmounted while GParted is running, it
doesn't need to be prevented by masking the unit. Therefore stop
masking the root file system mount unit.
[1] Debian bug #948710 - handle masked .mount unit more gracefully
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=948710
[2] systemd issue #14550 - Handle masked .mount units more gracefully
https://github.com/systemd/systemd/issues/14550
[3] core: never allow perpetual units to be masked
88414eed6fCloses!116 - Systemd mount masking and udev rule location updates
On RHEL / CentOS 8 GParted reports this error to the terminal when it is
closed:
# gparted
GParted 1.5.0-git
configuration --enable-online-resize
libparted 3.2
>> --runtime cannot be used with unmask
# $?
0
and leaves mount units masked:
# systemctl list-units '*.mount'
UNIT LOAD ACTIVE SUB DESCRIPTION
------------------------------------------------------------------
* -.mount masked active mounted Root Mount
* boot.mount masked active mounted boot.mount
...
This is because of this change [1] released in systemd 239. Systemd bug
9393 [2] was raised and the change was reverted [3] in systemd 240.
According to repology.org only RHEL / CentOS 8 (and clones) and Fedora
29 shipped with systemd 239 [4].
Fix by detecting non-zero exit status from systemctl and falling back to
directly removing the runtime mount unit mask files instead. Then have
to use systemctl daemon-reload to inform systemd to reload it's
configuration from disk to discover the masks have been removed.
[1] systemctl: when removing enablement or mask symlinks, cover both
/run and /etc
4910b35078
[2] systemctl no longer allows unmask in combination with --runtime
#9393https://github.com/systemd/systemd/issues/9393
[3] Revert "systemctl: when removing enablement or mask symlinks, cover
both /run and /etc"
1830ac51a4
[4] Versions for systemd
https://repology.org/project/systemd/versionsCloses!116 - Systemd mount masking and udev rule location updates
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
Fragment of a failed CI test job from a GiLab job runner which didn't
allow creation of block special devices looked like:
$ tests/makedev.sh
mknod -m 0660 /dev/sda b 8 0
mknod: '/dev/sda': Operation not permitted
chown: cannot access '/dev/sda': No such file or directory
mknod -m 0660 /dev/sda1 b 8 1
mknod: '/dev/sda1': Operation not permitted
chown: cannot access '/dev/sda1': No such file or directory
mkdir: created directory '/dev/disk'
mkdir: created directory '/dev/disk/by-id/'
'/dev/disk/by-id/gparted-sda' -> '/dev/sda'
test/makedev.sh attempted to create two block devices it wanted for
testing, but that failed with "Operation not permitted". It then
created dangling symbolic link /dev/disk/by-id/gparted-sda -> /dev/sda;
gparted-sda pointed to a name which didn't exist.
Despite the previous commit testing and skipping every test where the
block device doesn't exist this unit test still failed:
[ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches
test_BlockSpecial.cc:186: Failure
Failed
follow_link_name(): Failed to resolve symbolic link '/dev/disk/by-id/gparted-sda'
test_BlockSpecial.cc:271: Skip test. Block device '' does not exist
[ FAILED ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (0 ms)
The unit test called get_link_name() which read the directory
/dev/disk/by-id and found symbolic link gparted-sda. It then called
follow_link_name() passing /dev/disk/by-id/gparted-sda which used
realpath(3) to get the canonicalised absolute pathname, which includes
following links. But as gparted-sda pointed to a non-existent file it
failed and reported message "Failed to resolve symbolic link ...". Then
after that the unit test skips the non-existent block device, but the
test has already failed at that point.
Fix the unit test by also checking the symbolic link points to an
existing block device before calling follow_link_name() on it. This
works because SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST() uses stat(3), which
follows symbolic links, in it's verification.
Also put SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST() immediately after each
initialisation of a block device name for some sort of consistency with
it's need in this fixed NamedBlockSpecialObjectBySymlinkMatches unit
test.
Closed!113 - Fix occasional GitLab CI test jobs failures on
BlockSpecial unit tests
Since November 2022 test_BlockSpecial has been occasionally failing in
GNOME GitLab Docker CI test jobs like this:
[ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBlockDevice
test_BlockSpecial.cc:216: Failure
Value of: bs.m_major > 0 || bs.m_minor > 0
Actual: false
Expected: true
[ FAILED ] BlockSpecialTest.NamedBlockSpecialObjectBlockDevice (0 ms)
...
[ RUN ] BlockSpecialTest.TwoNamedBlockSpecialObjectBlockDevices
test_BlockSpecial.cc:244: Failure
Value of: bs1.m_major != bs2.m_major || bs1.m_minor != bs2.m_minor
Actual: false
Expected: true
[ FAILED ] BlockSpecialTest.TwoNamedBlockSpecialObjectBlockDevices (0 ms)
[ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches
test_BlockSpecial.cc:170: Failure
Failed
follow_link_name(): Failed to resolve symbolic link '/dev/disk/by-id/gparted-sda'
[ FAILED ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (0 ms)
...
3 FAILED TESTS
FAIL test_BlockSpecial (exit status: 1)
As identified previously [1] the Docker CI images no longer have any
block devices in /dev. test/makedev.sh script was added to create block
devices test_BlockSpecial needs for it's testing. Now a subset of the
GNOME GitLab job runners additionally prevent creation of block special
device nodes. test/makedev.sh reports this:
$ tests/makedev.sh
mknod -m 0660 /dev/sda b 8 0
mknod: /dev/sda: Operation not permitted
chown: cannot access '/dev/sda': No such file or directory
mknod -m 0660 /dev/sda1 b 8 1
mknod: /dev/sda1: Operation not permitted
chown: cannot access '/dev/sda1': No such file or directory
Alternative rejected solutions:
1. Use fakeroot [2]. Package is available for the 3 distributions used
in CI jobs. Does fake stat() call. Works when run like this in the
CI test jobs:
fakeroot -s test/fakeroot.env tests/makedev.sh
fakeroot -i test/fakeroot.env make check
fakeroot -i test/fakeroot.env make distcheck
But if you run fakeroot ... make check on our development machines
as a non-root user it causes the test_SupportedFileSystems unit
tests which use losetup to fail. This is because
test_SupportedFileSystems thinks it's root inside the fakeroot
environment but fakeroot doesn't fake enough for losetup to work.
This makes running tests in the GitLab CI jobs different from how we
would have to run them on our development machines. Prefer not to
do that.
2. Use GNU ld --wrap [3] to call our own __wrap_stat() allowing
test_BlockSpecial to provide mocked results to the stat() call in
constructor BlockSpecial::BlockSpecial(). This works with
GNU C Library >= 2.33, released 01-Feb-2021, and musl libc,
therefore it works on CI tested distributions Ubuntu LTS >= 22.04
and Alpine Linux respectively. However this fails on earlier glibc
releases, so will fail on CentOS 7 CI image, as the compiler emits a
call to __xstat() rather than stat(). This is something to do with
how glibc's /usr/include/sys/stat.h supported multiple versions of
stat(). Don't use this as it's doesn't work everywhere.
Additional useful implementation hints. [4][5]
Choose to fix by just skipping unit tests which need block special names
to exist in the file system, but don't exist. This is the same
technique that test_SupportedFileSystems uses. So tests/makedev.sh
creates block devices if it can in the GNOME GitLab CI test images [1]
and now if that fails the individual unit tests are skipped.
[1] 57983b9fc2
Create block special devices needed by test_BlockSpecial in GitLab
CI jobs (!59)
[2] FakeRoot
https://wiki.debian.org/FakeRoot
[3] ld(1) - Linux manual page
https://man7.org/linux/man-pages/man1/ld.1.html
"--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to
symbol will be resolved to "__wrap_symbol". Any undefined
reference to "__real_symbol" will be resolved to symbol.
This can be used to provide a wrapper for a system function.
The wrapper function should be called "__wrap_symbol". If it
wishes to call the system function, it should call
"__real_symbol".
...
"
[4] gcc: error: unrecognized option --wrap
https://stackoverflow.com/questions/33278164/gcc-error-unrecognized-option-wrap
[5] C++ ld linker --wrap option does not work for internal function calls
https://stackoverflow.com/questions/44464961/c-ld-linker-wrap-option-does-not-work-for-internal-function-callsClosed!113 - Fix occasional GitLab CI test jobs failures on
BlockSpecial unit tests
Add these simple debugging aids to the GNOME GitLab CI test job.
They've been needed before [1] so add them permanently.
[1] 57983b9fc2
Create block special devices needed by test_BlockSpecial in GitLab
CI jobs (!59)
"Contents of /proc/partitions inside the Docker image when this
test CI job failed:
...
And the listing of /dev/:
"
Closed!113 - Fix occasional GitLab CI test jobs failures on
BlockSpecial unit tests
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
More recent g++ versions produce these warnings:
test_PasswordRAMStore.cc: In member function ‘virtual void GParted::PasswordRAMStoreTest_TotalErasure_Test::TestBody()’:
test_PasswordRAMStore.cc:61:32: warning: ‘ ’ directive output truncated writing 20 bytes into a region of size 10 [-Wformat-truncation=]
snprintf( buf, sizeof( buf ), "password%03u ", i );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_PasswordRAMStore.cc:61:10: note: ‘snprintf’ output 32 bytes into a destination of size 21
snprintf( buf, sizeof( buf ), "password%03u ", i );
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
snprintf() [1] truncates the printed string to the specified size so
didn't overflow the buffer. However clear the warning by making the
formatted string always exactly 20 characters long, followed by the
terminating NUL character to exactly fill the buffer.
[1] print3(f) - Linux manual page
https://man7.org/linux/man-pages/man3/snprintf.3.html
"The functions snprintf() and vsnprintf() write at most size bytes
(including the terminating null byte ('\0')) to str.
"