Commit Graph

1864 Commits

Author SHA1 Message Date
Mike Fleetwood 6617e6f3a3 Use face skin colours exclusively for software block devices (#183)
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_performance

Closes #183 - Basic support for bcache
2022-03-01 16:58:46 +00:00
Mike Fleetwood e85c00edb6 Remove unnecessary Glib::ustring::compose() of constant string 2022-02-22 20:04:25 +00:00
Mike Fleetwood 824287e678 Check copy destination instead of source (!95)(#723571)
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
2022-02-22 20:04:25 +00:00
Mike Fleetwood 29a4fb4504 Return and use constant reference from SWRaid_Info::get_label() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 0eedfa6030 Return constant reference from SWRaid_Info::get_uuid() (!94)
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
2021-11-16 16:08:17 +00:00
Mike Fleetwood b38ee9c8ac Return and use constant reference from SWRaid::get_array() (!94)
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-reference

Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 901f03c19d Return constant reference from OperationDetail::get_treepath() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 1f6e81295b Return constant reference from OperationDetail::get_description() (!94)
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
2021-11-16 16:08:17 +00:00
Mike Fleetwood 81f5dc3ead Use constant reference from LVM2_PV_Info::get_vg_cache_entry_by_name() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood c35422a70c Return and use constant reference from LVM2_PV_Info::get_vg_name() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 55a74645f6 Use constant reference from LVM2_PV_Info::get_pv_cache_entry_by_name() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 0a4761df46 Use constant reference from btrfs::get_cache_entry() (!94)
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
2021-11-16 16:08:17 +00:00
Mike Fleetwood a8e8e9fd97 Return and use constant reference from Partition::get_mountpoints() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood b3f1c22860 Return and use constant reference from Partition::get_mountpoint() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood c8f640655c Return and use constant reference from Partition::get_filesystem_label() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 301690d9ff Return and use constant reference from Partition::get_path() (!94)
Closes !94 - Make more getter methods use return-by-constant-reference
2021-11-16 16:08:17 +00:00
Mike Fleetwood 21dd6fe00b Return and use constant reference from Device::get_path() (!94)
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
2021-11-16 16:08:17 +00:00
Mike Fleetwood 67b07f86e6 Simplify logic in MB_Needed_for_Boot_Record() et al
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.
2021-10-27 15:04:20 +00:00
Mike Fleetwood eaaa5719f7 Use previous unallocated partition when determining 1 MiB reservation in resize/move dialog
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.
2021-10-27 15:04:20 +00:00
Mike Fleetwood 37689eef9d Ensure 1 MiB reserved when moving extended partition to start of drive
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
2021-10-27 15:04:20 +00:00
Mike Fleetwood 19186e2152 Allow resize/move of partition to sector 2048 when following another (#172)
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
2021-10-27 15:04:20 +00:00
Mike Fleetwood 0bfbac4f65 Allow creation of partition at sector 2048 when following another (#172)
[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
2021-10-27 15:04:20 +00:00
Pascal Engélibert f31fbb986f Add accessibility relations (!92)
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
2021-10-09 08:34:41 +00:00
Mike Fleetwood 3665bd5da7 Fix crash scrolling quickly in the drive selection combobox (#165)
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=991998

Closes #165 - GParted segfaults if scrolling quickly in the device
              dropdown
2021-09-27 15:11:37 +00:00
Mike Fleetwood 1b85bf00cf Fix warning from double assigning to gscreen
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)
2021-09-17 15:56:10 +00:00
Mike Fleetwood ba1bafc5ac Replace /proc/mounts grep with Mount_Info cache reload and query (!89)
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
2021-09-17 15:56:10 +00:00
Movie Ma 6f811cfaca Fix unmount error when unmounting below a bind mount point (!89)
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#DESCRIPTION

Closes !89 - Fix unmount error when unmounting below a bind mount point
2021-09-17 15:56:10 +00:00
Mike Fleetwood d123d36b67 Remove (N/A) from comment in FileSystem::rm_temp_dir()
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
2021-08-14 15:57:20 +00:00
Mike Fleetwood 48fd3cba74 Add missing includes to Mount_Info.cc 2021-08-14 15:57:20 +00:00
Mike Fleetwood a7f9ec65cf Reorder parameters to add_mountpoint_entry()
To match the order of the members in struct MountEntry.
2021-08-14 15:57:20 +00:00
Mike Fleetwood f5e6239452 Extract common code into Mount_Info::lookup_uuid_or_label() (#162)
Closes #162 - It is no longer possible to mount a LUKS encrypted file
              system
2021-08-14 15:57:20 +00:00
Mike Fleetwood 49747f656d Resolve UUID= and LABEL= refs when searching Mount_Info (#162)
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
2021-08-14 15:57:20 +00:00
Mike Fleetwood 5bede18e58 Load unresolved UUID= and LABEL= refs into Mount_Info cache (#162)
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
2021-08-14 15:57:20 +00:00
Mike Fleetwood cf5a264b0e White space tidy-up some of DialogFeatures 2021-07-30 16:12:24 +00:00
Mike Fleetwood e4427b0333 Add labelling of mounted ext2/3/4 (#163)(#600496)
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
2021-07-30 16:12:24 +00:00
Mike Fleetwood 0c13855d52 Add labelling of mounted xfs (#163)
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
2021-07-30 16:12:24 +00:00
Mike Fleetwood 47960037f7 Show online file system labelling in the Features dialog (#163)
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
2021-07-30 16:12:24 +00:00
Mike Fleetwood eb034b1759 Add labelling of mounted btrfs (#163)
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
2021-07-30 16:12:24 +00:00
Mike Fleetwood 39e7156697 Copy XFS UUID when copying the file system (!85)
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
2021-06-06 18:52:13 +00:00
Mike Fleetwood 36e43ed7a7 Copy XFS label when copying the file system (!85)
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
2021-06-06 18:52:13 +00:00
Mike Fleetwood f230ecc7a2 Fix recognition of SD/MMC device names (!83)
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
2021-05-19 15:36:23 +00:00
Curtis Gedak 26f4dc504a Replace deprecated gtk_show_uri() method for help window (!82)
The gtk_show_uri() [1] method was deprecated as of gtk3 version 3.22 and
has been replaced with gtk_show_uri_on_window [2].

[1] https://developer.gnome.org/gtk3/stable/gtk3-Filesystem-utilities.html#gtk-show-uri
[2] https://developer.gnome.org/gtk3/stable/gtk-migrating-2-to-3.html#id-1.6.3.3.7

Note that AppInfo::launch_uris() has been removed because with
glib/glibmm >= 2.58 AppInfo::launch_uris() always reports success even
when yelp is not launched and in such cases prevents the dialog
reporting the error from being displayed.

Closes !82 - Replace deprecated gtk_show_uri() method for help window
2021-05-17 20:24:54 +00:00
Mike Fleetwood c4ca7d3e9b Mark remaining get_filesystem_*() methods as returning const strings 2021-04-25 15:49:35 +00:00
Mike Fleetwood 1c9ecc5dbd Comment differences between FileSystem::execute_command() methods 2021-04-25 15:49:35 +00:00
Mike Fleetwood 5752682c12 Report this LUKS passphrase request reason as resize (#59)
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"
2021-04-25 15:49:35 +00:00
Mike Fleetwood 80624bfd40 Also prompt for LUKS password as required for check operation (#59)
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"
2021-04-25 15:49:35 +00:00
Mike Fleetwood f3aec1f4b1 Pass LUKS password as needed when resizing open mapping (#59)
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"
2021-04-25 15:49:35 +00:00
Mike Fleetwood d7503fd5ed Add ability for small writes to stdin of operation tracked child processes (#59)
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"
2021-04-25 15:49:35 +00:00
Mike Fleetwood 2ccbc3ec92 Extract common code into generate_encryption_mapping_name() (#59)
Closes #59 - Resize of LUKS2 encrypted file system fails with "Nothing
             to read on input"
2021-04-25 15:49:35 +00:00
Mike Fleetwood 83abcd8873 Ask for LUKS passphrase for resize operation as required (#59)
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"
2021-04-25 15:49:35 +00:00
Mike Fleetwood 099b85fe18 Capture LUKS mapping master encryption key location (#59)
ISSUE OVERVIEW

When GParted tries to resize an open LUKS encryption mapping and the
volume (master) key was stored in the kernel keyring service [1] it
fails like this:

    Check and repair file system ([Encrypted] ext4) on /dev/...(ERROR)
    + calibrate /dev/sdd1                                      (SUCCESS)
    + check file system on /dev/mapper/sdd1_crypt for errors...(SUCCESS)
    + grow encryption volume to fill the partition             (ERROR)
      + cryptsetup -v resize 'sdd1_crypt'                      (ERROR)
          Command failed with code -1 (wrong or missing parameters).
          Nothing to read on input.

This error occurs with cryptsetup >= 2.0, kernel >= 4.10 and LUKS2
format because the crypt Device-Mapper target no longer has the volume
key so cryptsetup resize prompts for a passphrase, but GParted doesn't
provide it.

THIS COMMIT

Additionally capture the location of the volume (master) key location
for active encryption mappings.  Do this the using the same method that
cryptsetup uses [2][3].  Namely if the first character of the KEY is a
":" then the key *was* stored in the kernel keyring service, otherwise
it *is* store in the Device-Mapper crypt target as previously.

    # echo -n badpassword | cryptsetup luksFormat --type luks1 /dev/sdb1 -
    # echo -n badpassword | cryptsetup luksOpen /dev/sdb1 sdb1_crypt
    # cryptsetup status sdb1_crypt | egrep 'type|key location'
      type:         LUKS1
      key location: dm-crypt

    # echo -n badpassword | cryptsetup luksFormat --type luks2 /dev/sdb2 -
    # echo -n badpassword | cryptsetup luksOpen /dev/sdb2 sdb2_crypt
    # cryptsetup status sdb2_crypt | egrep 'type|key location'
      type:         LUKS2
      key location: keyring

    # dmsetup table --target crypt
    sdb1_crypt: 0 520192 crypt aes-xts-plain64 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0 8:17 4096
    sdb2_crypt: 0 491520 crypt aes-xts-plain64 :64:logon:cryptsetup:3d040240-97ba-4559-af98-72c3be500498-d0 0 8:18 32768
                                               ^
First character of the KEY field --------------'

[1] Integration with the kernel keyring service
    https://gitlab.com/cryptsetup/cryptsetup/blob/v2.0.0/docs/Keyring.txt
    "
    Starting with cryptsetup 2.0 we load [Volume Key] VK in kernel
    keyring by default for LUKSv2 devices ...

    In summary, the key description visible in dm-crypt table line is a
    reference to VK that usually no longer exists in kernel keyring
    service if you used cryptsetup to for device activation.
    "

[2] cryptsetup/v2.3.5/lib/libdevmapper.c:_dm_target_query_crypt()
    https://gitlab.com/cryptsetup/cryptsetup/-/blob/v2.3.5/lib/libdevmapper.c#L2031
        if (key_[0] == ':')
            *act_flags |= CRYPT_ACTIVATE_KEYRING_KEY;

[3] cryptsetup/v2.3.5/src/cryptsetup.c:action_status()
    https://gitlab.com/cryptsetup/cryptsetup/-/blob/v2.3.5/src/cryptsetup.c#L839
        log_std("  key location: %s\n", (cad.flags & CRYPT_ACTIVATE_KEYRING_KEY) ? "keyring" : "dm-crypt");

Closes #59 - Resize of LUKS2 encrypted file system fails with "Nothing
             to read on input"
2021-04-25 15:49:35 +00:00
Mike Fleetwood 1f65b2547a Ignore libparted unrecognised disk label message from encryption mappings (#152)
When GParted probes an open encryption mapping which is either blank or
contains a file system which libparted doesn't recognise, such as:
exfat, f2fs, lvm2 pv, minix or reiser4, then the partition also gets
this warning message:
    /dev/mapper/sdb11_crypt: unrecognised disk label

Clear the message so that it isn't shown in the GUI.

Note that the message is still written to stderr by GParted, like all
libparted exceptions are.  This is done by GParted's libparted exception
handler:
    GParted_Core::ped_exception_handler()
      _ped_exception_handler()

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood c830c6770d Reorder cases in set_device_and_disk() and use if fail return early (#152)
The previous commit "Resolve empty drive displayed as blank minor logic
issue (#152)" left the code in set_device_and_disk() some what
unsightly.

Refactor the whole function.  Use if fail return early pattern for
failure of the get_device() call at the start of the function.  Reorder
the 4 cases into a single depth if then else if chain.  Hopefully this
is a little easier to follow.

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood b3ff339ac7 Resolve empty drive displayed as blank minor logic issue (#152)
The previous commit "Remove coding landmine in get_disk() (#152)" made
an empty drive without a disk label (partition table) display as
nothing, instead of the normal single unallocated partition with warning
"unrecognised disk label".

The previous commit said:
    3. The two remaining direct calls to get_disk() where the strict
       parameter is explicitly set to false, from set_device_from_disk()
       and detect_filesystem_in_encryption_mapping(), are when scanning.
       As the pass strict=false they don't allow the PedDevice deletion
       to occur if no recognised disk label is found.

This is true, but get_disk(..., strict=false) additionally returned true
even if there was no disk label.  And in set_device_from_disk() the
empty drive case is inside the if branch of the get_disk() call
returning true.

Simply fix this by calling get_disk(), ignoring the return value.

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood c4ef43aa5d Remove coding landmine in get_disk() (#152)
get_disk() is the wrapper around libparted's ped_disk_new() which reads
a disk label from the specified device and if successful creates the in
memory PedDisk object to represent it.  In the case that libparted
doesn't recognise a disk label or a file system, having get_disk() go
and destroy the passed in PedDevice object via parameter lp_device is
very unexpected behaviour hence describing it as a coding landmine.

BACKGROUND

1. Early on GParted only worked with devices with valid disk labels.
   FileSystem.h:open_device_and_disk() required both ped_device_get()
   and ped_disk_new() to succeed or neither to succeed.
2. Commit [1] added support for devices which didn't yet have a disk
   label.  open_device_and_disk() had default parameter strict=true
   added.  While scanning strict=false was passed which allowed
   open_device_and_disk() to return success if only ped_device_get()
   succeeded and ped_disk_new() failed when the disk was empty.  All
   other times open_device_and_disk() was called with default
   strict=true, still requiring both or neither to succeed.
3. Commit [2] added support for whole disk file systems.  The now named
   get_device_and_disk() had it's functionality split between
   get_device() and get_disk().  This result in the code landmine being
   left behind: get_disk() destroying the passed device object if
   default parameter strict=true and no disk label or file system was
   detected.

ANALYSIS

1. Since support for whole disk file systems [2] all current calls to
   get_device_and_disk() let the strict parameter default to true and
   are only called on known partitions within disk labels when applying
   a change to that partition.  Therefore they don't care about the
   behaviour of get_disk(), just that get_device_and_disk() maintains
   that both ped_device_get() and ped_disk_new() succeed or neither
   succeed.
2. Two direct calls to get_disk() where the strict parameter defaults to
   true, from calibrate_partition() and erase_filesystem_signatures(),
   only do so on known partitions within disk labels as part of applying
   a change to that partition.  Therefore ped_disk_new() will succeed
   and so PedDevice isn't deleted when not wanted.
3. The two remaining direct calls to get_disk() where the strict
   parameter is explicitly set to false, from set_device_from_disk() and
   detect_filesystem_in_encryption_mapping(), are when scanning.  As the
   pass strict=false they don't allow the PedDevice deletion to occur if
   no recognised disk label is found.

FIX

Remove the strict parameter from get_disk() and get_device_and_disk() as
it's no longer needed.  Remove the code landmine by removing the side
affect of destroying the PedDevice object if a disk label isn't found.
Make sure get_device_and_disk() maintains the all or nothing behaviour.

Also don't pass lp_device by reference to a pointer to get_disk() so the
code can't change where lp_device points.

[1] 038c5c5d99
    P (special thanks to mantiena-baltix for bringing this issue to my

[2] 51ac4d5648
    Split get_device_and_disk() into two (#743181)

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood 8280f3eedc Correctly const and assert detect_filesystem() parameters (#152)
As discussed in the previous commit "Don't crash probing libparted
unrecognised encrypted file system (#152)", detect_filesystem() accepted
a NULL lp_device pointer and dereferenced it leading to the crash.
Document the requirement for lp_device parameter to be non-NULL via an
assert and also correctly const the parameters.

This forces needing to const the lp_partition parameter to
get_partition_path() too.  Also assert it's non-NULL requirement.

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood 09df074a92 Don't crash probing libparted unrecognised encrypted file system (#152)
Create a LUKS encrypted partition and open it.  Then either leave the
contents blank or create a file system which libparted doesn't
recognise, such as: exfat, f2fs, lvm2 pv, minix or reiser4.  When
GParted probes the disk device it crashes.

    # echo -n badpassword | cryptsetup luksFormat /dev/sdb11
    # echo -n badpassword | cryptsetup luksOpen /dev/sdb11 sdb11_crypt
    # ./gpartedbin /dev/sdb
    GParted 1.2.0-git
    configuration (none)
    libparted 3.1
    /dev/mapper/sdb11_crypt: unrecognised disk label
    Segmentation fault (core dumped)

Backtrace:
    #0  0x0000000000460f68 in GParted::GParted_Core::detect_filesystem(_PedDevice*, _PedPartition*, std::vector<Glib::ustring, std::allocator<Glib::ustring> >&)
        (lp_device=0x0, lp_partition=0x0, messages=std::vector of length 0, capacity 0)
        at GParted_Core.cc:1235
    #1  0x00000000004615a6 in GParted::GParted_Core::detect_filesystem_in_encryption_mapping(Glib::ustring const&, std::vector<Glib::ustring, std::allocator<Glib::ustring> >&)
        (path=..., messages=std::vector of length 0, capacity 0)
        at GParted_Core.cc:1096
    #2  0x00000000004647c8 in GParted::GParted_Core::set_luks_partition(GParted::PartitionLUKS&)
        (this=this@entry=0x7fff43f974e0, partition=...)
        at GParted_Core.cc:1011
    #3  0x000000000046511b in GParted::GParted_Core::set_device_partitions(GParted::Device&, _PedDevice*, _PedDisk*)
        (this=this@entry=0x7fff43f974e0, device=..., lp_device=0x7efc780008c0, lp_disk=0x7efc78000d10)
        at GParted_Core.cc:883
    #4  0x00000000004658e3 in GParted::GParted_Core::set_device_from_disk(GParted::Device&, Glib::ustring const&)
        (this=this@entry=0x7fff43f974e0, device=..., device_path=...)
        at GParted_Core.cc:704
    #5  0x0000000000465fff in GParted::GParted_Core::set_devices_thread(std::vector<GParted::Device, std::allocator<GParted::Device> >*)
        (this=0x7fff43f974e0, pdevices=0x7fff43f96bc8)
        at GParted_Core.cc:266
    #6  0x00007efc99ba413d in call_thread_entry_slot ()
        at /lib64/libglibmm-2.4.so.1
    #7  0x00007efc97dc8555 in g_thread_proxy ()
        at /lib64/libglib-2.0.so.0
    #8  0x00007efc96ab4ea5 in start_thread () at /lib64/libpthread.so.0
    #9  0x00007efc967dd9fd in clone () at /lib64/libc.so.6

The relevant sequence of events goes like this:
    detect_filesystem_in_encryption_mapping(path, ...)
      lp_device = NULL
      get_device(path, lp_device)
        lp_device = ped_device_get(path.c_str())
        return true
      lp_disk = NULL
      lp_partition = NULL
      get_disk(lp_device, lp_disk)  // + default parameter strict=true
        lp_disk = ped_disk_new(lp_device)
          // No libparted recognised disk label or file system found, so
          // NULL returned.
        destroy_device_and_disk(lp_device, lp_disk)
          ped_device_destroy(lp_device)
          lp_device = NULL
        return false
      detect_filesystem(lp_device, lp_partition, ...)
        path = lp_device->path

The key points are:
1. get_device() created a PedDevice object pointed to by lp_device;
2. get_disk() didn't find a libparted recognised disk label or file
   system but also unexpectedly destroyed the PedDevice object and
   assigned NULL to lp_device;
3. detect_filesystem() dereferenced lp_device assuming it was still
   valid.

Implement the simplest possible fix by telling get_disk() to not
destroy the needed PedDevice object when there's no recognised content.
This is the same as how get_disk() is called in set_device_from_disk().

Closes #152 - GParted crashed when trying to probe an encrypted
              partition containing content that libparted doesn't
              recognise
2021-04-15 16:33:01 +00:00
Mike Fleetwood 1adb28b0c4 Also use libparted to probe for encrypted file systems (#148)
Even though blkid is considered mandatory [1] GParted should still
perform reasonably when blkid is not available, provided that is not too
onerous a task.  Also use libparted file system identification inside
encryption mappings.

[1] 749a249571
    Document blkid command as a mandatory requirement (#753436)

Closes 148 - Encrypted file systems are no longer recognised
2021-04-03 17:02:04 +00:00
Mike Fleetwood 555cea10cf Probe encryption mappings as needed using blkid (#148)
GParted no longer recognises file systems inside LUKS encryption, apart
from the few recognised by GParted's internal detection.  Bisected to
this commit:
    8b35892ea5
    Pass device and partition names to blkid (#131)

Prior to this commit blkid was run querying all known block devices
including active encryption mappings, hence prior recognition.  With
this commit blkid was run only for named or found disk devices and
associated found partitions from /proc/partitions, so no more
recognition of encrypted file systems.

Fix by running blkid on the encryption mapping just before querying for
the file system.  This restores the level of functionality that existed
before.

Closes 148 - Encrypted file systems are no longer recognised
2021-04-03 17:02:04 +00:00
Mike Fleetwood 1e91cb831b Extract some code into detect_filesystem_in_encryption_mapping() (#148)
To avoid making set_luks_partition() more complicated extract the file
system detection portion into a new function.

Closes 148 - Encrypted file systems are no longer recognised
2021-04-03 17:02:04 +00:00
Mike Fleetwood 1e813d83a5 Make FS_Info (blkid) cache incrementally loadable (#148)
Since changes for issue #131 "GParted hangs when non-named device is
hung" FS_Info cache is initialised, cleared and loaded via one call to
load_cache_for_paths().  It runs blkid for named or found disk devices
and associated found partitions from /proc/partitions, rather than
running blkid and letting it report for all block devices.

To avoid the possibility of using blkid on an encryption mapping on a
non-specified and possibly hung block device GParted can't just specify
all encryption mappings.  Instead only encryption mappings which belong
to the above identified block devices should be probed.  That requires
identifying LUKS encryption data in the block devices first so will
require subsequently loading additional data into the FS_Info cache and
running blkid again.

To accommodate this make the FS_Info cache incrementally loadable,
rather than doing everything in a single call to load_cache_for_paths().
Have a separate clear_cache() call which initialises and clears the
cache and make load_cache_for_paths() just run blkid and insert data for
the named paths.

Closes 148 - Encrypted file systems are no longer recognised
2021-04-03 17:02:04 +00:00
Mike Fleetwood 690fedfff9 Explicitly read the reiser4 volume UUID when present
Reiser4 has introduced new disk format which includes support for
spanning the file system over multiple block devices (subvolumes)
[1][2].  As such the output of the debugfs.reiser4 for the UUID has
changed slightly.  So far the new reiser4progs package (version 2.0.x)
is only available as a Debian experimental package.

Using reiser4progs 1.2.1 the old output was like this:

    $ debugfs.reiser4 test.img
    debugfs.reiser4 1.2.1
    Format release: 4.0.2
    Copyright (C) 2001-2005 by Hans Reiser, licensing governed by reiser4progs/COPYING.

    Master super block (16):
    magic:          ReIsEr4
    blksize:        4096
    format:         0x0 (format40)
    uuid:           1116afce-99fd-4a6e-94cb-2d9f19c91d67
    label:          <none>

    ...

With reiser4progs 2.0.4 the new output is like this:

    $ debugfs.reiser4 test.img
    debugfs.reiser4
    Package Version: 2.0.4
    Software Framework Release: 5.1.3
    Copyright (C) 2001-2005 by Hans Reiser, licensing governed by reiser4progs/COPYING.
    Master super block (16):
    magic:          ReIsEr4
    blksize:        4096
    volume:         0x1 (asym)
    distrib:        0x1 (fsx32m)
    format:         0x1 (format41)
    description:    Standard layout for logical volumes.
    stripe bits:    14
    mirror id:      0
    replicas:       0
    volume uuid:    9538bfa3-5694-4abe-864c-edc288a9d801
    subvol uuid:    d841c692-2042-49e6-ac55-57e454691782
    label:          <none>

    ...

GParted happens to read the correct UUID just because the first matching
"uuid" string in the output is the volume UUID.  Make the code more
robust by explicitly reading the volume uuid when labelled as such.

[1] Logical Volumes Howto
    https://reiser4.wiki.kernel.org/index.php/Logical_Volumes_Howto
[2] Logical Volumes Background
    https://reiser4.wiki.kernel.org/index.php/Logical_Volumes_Background
2021-03-24 16:22:41 +00:00
Mike Fleetwood 2a76af5beb Refactor reiser4::read_uuid() into if fail return early pattern 2021-03-24 16:22:41 +00:00
Mike Fleetwood a41e8b03ec Pass constant string by reference to lvm2_pv_size_to_num()
It is common C++ practice to pass a constant object by reference to
avoid constructing a duplicate object for pass by value [1].

[1] How to pass objects to functions in C++?
    https://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254
2021-03-10 16:40:44 +00:00
Mike Fleetwood c0a7aa438a Install gpartedbin into @libexecdir@ (#85)
Executables which are not intended for execution by users, but by other
programs, should be installed into /usr/libexec [1][2].  gpartedbin
falls into this category.  Update it's installation accordingly.

Standard Autotools details: gpartedbin will be installed into
EPREFIX/libexec by default.  To install gpartedbin into a different
directory set libexecdir when configuring the build system.  Like this
from git:
    ./autogen.sh --libexecdir=DIR
or like this from tar release:
    ./configure --libexecdir=DIR

[1] Filesystem Hierarchy Standard, version 3.0,
    4.7. /usr/libexec : Binaries run by other programs (optional)
    https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html
    "/usr/libexec includes internal binaries that are not intended to be
    executed directly by users or shell scripts.
    "

[2] GNU Coding Standards, June 12, 2020,
    7.2.5 Variables for Installation Directories
    https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
    "libexecdir
    The directory for installing executable programs to be run by other
    programs rather than by users.  This directory should normally be
    /usr/local/libexec, but write it as $(exec_prefix)/libexec.  (If you
    are using Autoconf, write it as '@libexecdir@'.)
    "

Closes #85 - Please install gpartedbin under /usr/libexec instead of
             /usr/sbin
2021-03-10 16:40:44 +00:00
Mike Fleetwood 4a6e79daa3 Update "Detecting BitLocker" URL code comment
The document has moved on the Microsoft website.  Update the URL
accordingly, and add an Internet Archive URL too, for good measure.
2021-03-04 16:55:06 +00:00
Mike Fleetwood a11c16445b Rename member variable to default_fs
... in class Dialog_Partition_New and slightly refactor the code in
build_filesystems_combo() method which sets it.

Change the name from first_creatable_fs to default_fs to be more
immediately obvious what the variable represents.  As default_fs is used
to index the items in the combo_filesystem derived ComboBox, make it's
type an int to match the type of the parameter passed to
Gtk::ComboBox::set_active() [1].  Initialise default_fs to -1 (no
selection) in the class constructor [2], which also allows removal of
local variable set_first just used to track whether first_creatable_fs
had been assigned yet or not.

[1] gtkmm: Gtk::ComboBox Class Reference, set_active()
    https://developer.gnome.org/gtkmm/stable/classGtk_1_1ComboBox.html#a4f23cf08e85733d23f120935b235096d

[2] C++ FAQ / Should my constructors use "initialization lists" or
    "assignment"?
    https://isocpp.org/wiki/faq/ctors#init-lists
2021-03-04 16:55:06 +00:00
Mike Fleetwood a97f1240fb Don't rely on unformatted being the last file system type
... in Dialog_Partition_New::build_filesystems_combo().  set_data()
populates this->FILESYSTEMS[] vector with supported file systems with
cleared, unformatted and extended added to the end.  Then
build_filesystems_combo() adds those items to combo_filesystem, skipping
extended.  It then makes the last item in the combobox sensitive,
relying on the fact that it is unformatted.

Refactor the code so build_filesystems_combo() no longer relies on
unformatted being the last item in combo_filesystem to always enable it.
2021-03-04 16:55:06 +00:00
Mike Fleetwood eb5ad50fef Remove unnecessary call to combobox_changed(false)
... in Dialog_Partition_New::set_data().  As the change signal for
combo_filesystem has already been connected, combo_changed(false) is
automatically called by setting the active selection.  Therefore remove
the unnecessary call.
2021-03-04 16:55:06 +00:00
Mike Fleetwood 400fc8397e Rename combobox_change() parameter to combo_type_changed
"Type" was rather a generic name.  Use "combo_type_changed" which makes
it clear that the boolean parameter indicates whether a change to
combo_type or one of the other ComboBoxes triggered this callback.
2021-03-04 16:55:06 +00:00
Mike Fleetwood e91db19e30 Fix crash in Create New Partition dialog when changing type (#101)
On an MSDOS partitioned drive, open the Create New Partition dialog and
change "created as" from Primary Partition to Extended Partition and
back to Primary Partition.  On Fedora and RHEL/CentOS 8, which builds
packages with FORTIFY_SOURCE [1][2] and GLIBXX_Assertions [3][4]
enabled, GParted will crash.

Run GParted built with the default compilation options under valgrind
and repeat the test.  Multiple out of bounds reads are reported like
this:
  # valgrind --track-origins=yes ./gpartedbin
  ...
  ==232613== Invalid read of size 8
  ==232613==    at 0x441AF6: GParted::Dialog_Partition_New::combobox_changed(bool) (Dialog_Partition_New.cc:354)
  ==232613==    by 0x443DBD: sigc::bound_mem_functor1<void, GParted::Dialog_Partition_New, bool>::operator()(bool const&) const (mem_fun.h:2066)

Coming from Dialog_Partition_New.cc:
  328  void Dialog_Partition_New::combobox_changed(bool type)
  329  {
  ...
  351      // combo_filesystem and combo_alignment
  352      if ( ! type )
  353      {
> 354          fs = FILESYSTEMS[combo_filesystem.get_active_row_number()];

When the partition type is changed to Extended the file system is forced
to be "Extended" too.  This is done in ::combobox_changed() method by
modifying combo_filesystem to add "Extended", making that the selected
item and setting the widget as inactive.

Then when the partition type is changed back to primary the file system
combobox is returned to it's previous state.  This is done by first
removing the last "Extended" item, making the widget active and setting
the selected item.  However as "Extended" is the currently selected
item, removing it forces their to be no selected item and triggers a
change to combo_filesystem triggering a recursive call to
::combobox_changed() where combo_filesystem.get_active_row_number()
returns -1 (no selection) [5] and on line 354 the code accesses item -1
of the FILESYSTEMS[] vector.

Fix by setting the new combo_filesystem selection before removing the
currently selected "Extended" item.  This has the added benefit of only
triggering a change to combo_filesystem once when the default item is
selected rather than twice when the currently "Extended" item is removed
and again when the default item is selected.

[1] [Fedora] Security Features, Compile Time Buffer Checks
    (FORTIFY_SOURCE)
    https://fedoraproject.org/wiki/Security_Features#Compile_Time_Buffer_Checks_.28FORTIFY_SOURCE.29

[2] Enhance application security with FORTIFY_SOURCE
    https://access.redhat.com/blogs/766093/posts/1976213

[3] Security Features Matrix (GLIBXX_Assertions)
    https://fedoraproject.org/wiki/Security_Features_Matrix

[4] GParted 1.2.0-1.fc33 package build.log for Fedora 33
    https://kojipkgs.fedoraproject.org/packages/gparted/1.2.0/1.fc33/data/logs/x86_64/build.log
    CXXFLAGS='-O2 -g ... -Wp,-D_FORTIFY_SOURCE=2
    -Wp,-D_GLIBCXX_ASSERTIONS ...'

[5] gtkmm: Gtk::ComboBox Class Reference, get_active_row_number()
    https://developer.gnome.org/gtkmm/stable/classGtk_1_1ComboBox.html#a53531bc041b5a460826babb8496c363b

Closes #101 - Crash changing Partition type in "Create new partition"
              dialog
2021-03-04 16:55:06 +00:00
Yuri Chornoivan 85c76b75d2 Fix minor typos in comments (!71)
Closes !71 - Fix minor typos in comments
2021-02-26 14:20:16 +00:00
Mike Fleetwood 7dbf0691f1 Accept NUL as a valid UTF-8 character again (#136)
On newer distributions the PipeCapture tests have been failing like
this:
    $ ./test_PipeCapture
    ...
    [ RUN      ] PipeCaptureTest.ReadEmbeddedNULCharacter
    test_PipeCapture.cc:336: Failure
          Expected: inputstr
         Of length: 6
    To be equal to: capturedstr.raw()
         Of length: 5
    With first binary difference:
    < 0x00000000  "ABC.EF"            41 42 43 00 45 46
    --
    > 0x00000000  "ABCEF"             41 42 43 45 46
    [  FAILED  ] PipeCaptureTest.ReadEmbeddedNULCharacter (0 ms)
    [ RUN      ] PipeCaptureTest.ReadNULByteInMiddleOfMultiByteUTF8Character
    test_PipeCapture.cc:353: Failure
          Expected: expectedstr
         Of length: 7
    To be equal to: capturedstr.raw()
         Of length: 6
    With first binary difference:
    < 0x00000000  "._45678"           00 5F 34 35 36 37 38
    --
    > 0x00000000  "_45678"            5F 34 35 36 37 38
    [  FAILED  ] PipeCaptureTest.ReadNULByteInMiddleOfMultiByteUTF8Character (0 ms)
    ...

Found that test_PipeCapture succeeds on Fedora 31 and fails on
Fedora 32.  Also test_PipeCapture binary from Fedora 31 and 32 both pass
on Fedora 31 and both fail on Fedora 32.  So something outside of the
GParted code and tests is the cause.

Confirmed that this GLib change "Add a missing check to
g_utf8_get_char_validated()" [1], first released in GLib 2.63.0, made
the difference.  On Fedora 32 with GLib 2.64.6, rebuilt GLib with that
change reverted and the tests passed.  Anyway fix the wrapper GParted
has around g_utf8_get_char_validated() to also handle this case of
reading a NUL character.

[1] 568720006c
    Add a missing check to g_utf8_get_char_validated()

Closes #136 - 1.2.0: test suite is failing in test_PipeCapture
2021-02-22 16:14:35 +00:00
Mike Fleetwood b1cad17a14 Refactor ::OnReadable() creating get_utf8_char_validated() (#136)
Extract call to GLib's g_utf8_get_char_validated() and the associated
workaround to also read NUL characters into a separate function to make
PipeCapture::OnReadable() a little smaller and simpler, so easier to
understand.

Add max_len > 0 clause into get_utf8_char_validated() like this:
    if (uc == UTF8_PARTIAL && max_len > 0)
so that the NUL character reading workaround is only applied when
max_len specifies the maximum number of bytes to read, rather than
when -1 specifies reading a NUL termination string.  This makes
get_utf8_char_validated() a complete wrapper of
g_utf8_get_char_validated() [1], even though GParted always specifies
the maximum number of bytes to read.

No longer describe the inability to read NUL characters as a bug [2]
since the GLib author's said it wasn't [3].

[1] GLib Reference Manual, Unicode Manipulation Functions,
    g_utf8_get_char_validated ()
    https://developer.gnome.org/glib/stable/glib-Unicode-Manipulation.html#g-utf8-get-char-validated

[2] 8dbbb47ce2
    Workaround g_utf8_get_char_validate() bug with embedded NUL bytes
    (#777973)

[3] Bug 780095 - g_utf8_get_char_validated() stopping at nul byte even
    for length specified buffers
    https://bugzilla.gnome.org/show_bug.cgi?id=780095#18
        "If g_utf8_get_char_validated() encounters a nul byte in the
        middle of a string of given longer length, it returns -2,
        indicating a partial gunichar.  That is not the obvious
        behaviour, but since g_utf8_get_char_validated() has been API
        for a long time, the behaviour cannot be changed.
        "

Closes #136 - 1.2.0: test suite is failing in test_PipeCapture
2021-02-22 16:14:35 +00:00
Mike Fleetwood ec9b39cc9c Update allow partition deletion comment in set_valid_operations()
This previous commit [1] suggested that in future partition deletion
might be allowed even while a LUKS mapping was active in that partition.
To allow deletion of a partition while it has active content is wrong.
That is a significant reason GParted has busy detection of otherwise
unrecognised file systems [2] and recognition and busy detection of, but
otherwise not controllable support for, Linux Software RAID [3] and
ATARAID [4][5] arrays.

To automatically close the LUKS partition first would be against the
pattern of behaviour that GParted has established, of requiring explicit
deactivation of file systems, swap and volume groups before allowing
deletion.  Therefore update the comment accordingly.

[1] f1e3d42b56
    Prevent deletion of open LUKS mappings (#774818)

[2] 49a2e19462
    Restore busy detection of unknown mounted file systems (#723842)

[3] d2e1130ad2
    Detect busy status of Linux Software RAID members (#709640)

[4] 6e990ea48a
    Detect busy status of mdadm started ATARAID members (#75)

[5] caec22871e
    Detect busy status of dmraid started ATARAID members (#75)
2021-02-17 17:16:48 +00:00
Mike Fleetwood b7c9b3e5a6 Add support for updating the exFAT UUID (!67)
Also with exfatprogs 1.1.0 [1], tune.exfat and exfatlabel gained the
capability to report and set the exFAT Volume Serial Number [2][3][4].
This is what blkid and therefore GParted reports as the UUID.

Report serial number:

    # tune.exfat -i /dev/sdb1
    exfatprogs version : 1.1.0
    volume serial : 0x772ffe5d
    # echo $?
    0

    # blkid /dev/sdb1
    /dev/sdb1: LABEL="test exfat" UUID="772F-FE5D" TYPE="exfat" PTTYPE="dos"

Set serial number:

    # tune.exfat -I 0xf96ef190 /dev/sdb1
    exfatprogs version : 1.1.0
    New volume serial : 0xf96ef190
    # echo $?
    0

tune.exfat exists in earlier releases of exfatprogs so check it has the
capability by searching for "Set volume serial" in the help output
before enabling this capability.

    # tune.exfat
    exfatprogs version : 1.1.0
    Usage: tune.exfat
            -l | --print-label                    Print volume label
            -L | --set-label=label                Set volume label
            -i | --print-serial                   Print volume serial
            -L | --set-serial=value               Set volume serial
            -V | --version                        Show version
            -v | --verbose                        Print debug
            -h | --help                           Show help

(Note the cut and paste error reporting the set volume serial flag as
'-L' rather than actually '-S').

[1] exfatprogs-1.1.0 version released
    http://github.com/exfaoprogs/exfatprogs/releases/tag/1.1.0

[2] [tools][feature request] Allow To Change Volume Serial Number ("ID")
    #138
    https://github.com/exfatprogs/exfatprogs/issues/138

[3] exfatlabel:add get/set volume serial option
    b4d9c9eeb5

[4] exFAT file system specification, 3.1.11 VolumeSerialNumber Field
    https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#3111-volumeserialnumber-field

Closes !67 - Add support for reading exFAT usage and updating the UUID
2021-02-17 17:16:48 +00:00
Mike Fleetwood 57507e21e2 Add exFAT file system usage reporting (!67)
exfatprogs 1.1.0 released 2021-02-09 [1] has gained support for
reporting file system usage [2][3] so add that capability to GParted.
It works like this:

    # dump.exfat /dev/sdb1 | egrep 'Volume Length\(sectors\):|Sector Size Bits:|Sector per Cluster bits:|Free Clusters:'
    Volume Length(sectors):                 524288
    Sector Size Bits:                       9
    Sector per Cluster bits:                3
    Free Clusters:                          23585

Unfortunately dump.exfat returns a non-zero status on success so that
can't be used to check for failure:

    # dump.exfat /dev/sdb1
    exfatprogs version : 1.1.0
    -------------- Dump Boot sector region --------------
    Volume Length(sectors):                 524288
    ...
    # echo $?
    192

dump.exfat only writes errors to stderr, so use this to identify failure:

    # dump.exfat /dev/sdb1 1> /dev/null
    # echo $?
    192

    # dump.exfat /dev/zero 1> /dev/null
    invalid block device size(/dev/zero)
    bogus sector size bits : 0
    # echo $?
    234

[1] exfatprogs-1.1.0 version released
    http://github.com/exfaoprogs/exfatprogs/releases/tag/1.1.0

[2] [feature request] File system usage reporting
    https://github.com/exfatprogs/exfatprogs/issues/139

[3] exfatprogs: add dump.exfat
    7ce9b2336b

Closes !67 - Add support for reading exFAT usage and updating the UUID
2021-02-17 17:16:48 +00:00
Mike Fleetwood 4a84952058 Avoid detecting exfat-utils commands as exfatprogs commands (#137)
A user had exfat-utils installed and tried to use GParted to create an
exfat file system.  GParted ran this command but it failed:
    # mkfs.exfat -L '' '/dev/sdb1'
    mkexfatfs 1.3.0
    mkfs.exfat: invalid option -- 'L'
    Usage: mkfs.exfat [-i volume-id] [-n label] [-p partition-first-sector] [-s sectors-per-cluster] [-V] <device>

The problem is that both exfat-utils and exfatprogs packages provide
mkfs.exfat and fsck.exfat commands but they have incompatible command
line options and GParted is programmed for exfatprogs.  So far GParted
just checks the executable exists, hence the mis-identification.

Reported version of exfat-utils commands:
    $ mkfs.exfat -V 2> /dev/null
    mkexfatfs 1.3.0
    Copyright (C) 2011-2018  Andrew Nayenko
    $ fsck.exfat -V 2> /dev/null
    exfatfsck 1.3.0
    Copyright (C) 2011-2018  Andrew Nayenko

Reported versions of exfatprogs commands:
    $ mkfs.exfat -V 2> /dev/null
    exfatprogs version : 1.0.4
    $ fsck.exfat -V 2> /dev/null
    exfatprogs version : 1.0.4

Fix this by only enabling exfat support also when the version string of
each command starts "exfatprogs version".  Note that this extra checking
is not needed for tune.exfat because only exfatprogs provides that
executable.

Closes #137 - Creating exfat partition with a label fails with error
2021-02-10 16:51:19 +00:00
Mike Fleetwood 25b7382d03 Replace Win_GParted::hbox member with local variables
hbox is a very generic name for a member variable and used as a local
variable in two methods.  Change to local variables instead.
2021-02-10 16:30:14 +00:00
Mike Fleetwood f3740c7ac9 Remove now unused return value from run_blkid_load_cache() (#131)
Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:14 +00:00
Mike Fleetwood e9d4a21bfb Remove now superfluous load_fs_info_cache() (#131)
This method is now only called from one location in the code so put it's
two lines of code there.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 52ed42de28 Ensure FS_Info (blkid) cache is populated before first use (#131)
Now we always want to run blkid naming all paths, ensure the FS_Info
cache is explicitly loaded first.  Report an error if not done so and
remove the cache loading code from running blkid without naming all
paths.  Fewer code paths to consider and reason about.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood d86d9ae830 Remove extra execution of blkid for any unreported paths (#131)
Again on Fedora 31 with a slightly different disk layout to the previous
commit.  sdb is partitioned with 1 empty partition and sdc remains
completely empty:
    # lsblk -o name,maj:min,rm,size,ro,type,fstype,label,mountpoint
    NAME            MAJ:MIN RM  SIZE RO TYPE FSTYPE      LABEL MOUNTPOINT
    sda               8:0    0   20G  0 disk
    |-sda1            8:1    0    1G  0 part ext4              /boot
    \-sda2            8:2    0   19G  0 part LVM2_member
      |-fedora-root 253:0    0   17G  0 lvm  ext4              /
      \-fedora-swap 253:1    0    2G  0 lvm  swap              [SWAP]
    sdb               8:16   0    8G  0 disk
    \-sdb1            8:17   0    1G  0 part
    sdc               8:32   0    8G  0 disk
    sr0              11:0    1 1024M  0 rom
    # blkid -v
    blkid from util-linux 2.34  (libblkid 2.34.0, 14-Jun-2019)
    # blkid /dev/sda /dev/sda1 /dev/sda2 /dev/sdb /dev/sdb1 /dev/sdc
    /dev/sda: PTUUID="5012fb1f" PTTYPE="dos"
    /dev/sda1: UUID="3cd48816-7817-4636-9fec-5f1afe76c1b2" TYPE="ext4" PARTUUID="5012fb1f-01"
    /dev/sda2: UUID="PH94ej-C8xU-bnMJ-UIh8-ZimI-4B7f-dHlZxh" TYPE="LVM2_member" PARTUUID="5012fb1f-02"
    /dev/sdb: PTUUID="1d120b57" PTTYPE="dos"
    /dev/sdb1: PARTUUID="1d120b57-01"

Stracing GParted shows these executions of blkid:
    # strace -f -q -bexecve -eexecve ./gpartedbin 2>&1 1> /dev/null | egrep -v 'ENOENT|SIGCHLD'
    ...
    [pid 160040] execve("/usr/sbin/blkid", ["blkid", "/dev/sda", "/dev/sda1", "/dev/sda2", "/dev/sdb", "/dev/sdb1", "/dev/sdc"], 0xa4e1b0 /* 32 vars */ <detached ...>
    [pid 160041] execve("/usr/sbin/blkid", ["blkid", "/dev/sdc"], 0xa4e1b0 /* 32 vars */ <detached ...>
    ...

On Fedora 31 with blkid from util-linux 2.34 it reports information for
sdb (partitioned drive) and sdb1 (empty partition) with only no
information for sdc (empty whole disk drive).  Hence no FS_Info cache
entry and re-execution of blkid just for sdc.

On older CentOS 7 with the same disk layout blkid reports this:
    # blkid -v
    blkid from util-linux 2.23.2  (libblkid 2.23.0, 25-Apr-2013)
    # blkid /dev/sda /dev/sda1 /dev/sda2 /dev/sdb /dev/sdb1 /dev/sdc
    /dev/sda: PTTYPE="dos"
    /dev/sda1: UUID="e7d559e4-3e1d-4fbc-b034-3fdeb498f44d" TYPE="xfs"
    /dev/sda2: UUID="B7ODFx-BfTE-hq7N-UlrF-f5ML-CPRe-klSy26" TYPE="LVM2_member"
    /dev/sdb: PTTYPE="dos"

And stracing GParted shows these executions of blkid:
    # strace -f -q -bexecve -eexecve ./gpartedbin 2>&1 1> /dev/null | egrep -v 'ENOENT|SIGCHLD'
    ...
    [pid  1889] execve("/sbin/blkid", ["blkid", "/dev/sda", "/dev/sda1", "/dev/sda2", "/dev/sdb", "/dev/sdb1", "/dev/sdc"], 0x10b8b10 /* 26 vars */ <detached ...>
    [pid  1890] execve("/sbin/blkid", ["blkid", "/dev/sdb1"], 0x10b8b10 /* 26 vars */ <detached ...>
    [pid  1891] execve("/sbin/blkid", ["blkid", "/dev/sdc"], 0x10b8b10 /* 26 vars */ <detached ...>
...

This time on CentOS 7 with blkid from util-linux 2.23.2 it reports
information for only sdb (partitioned drive), but not sdb1 (empty
partition) or sdc (empty whole disk drive).  Hence no FS_info cache
entries and re-execution of blkid for both sdb1 and sdc.

GParted needs blkid identification of file system images, LVM Logical
Volumes or any other partitions named on the command line which it
wouldn't normally scan [1].  Now every name of interest is passed to
blkid, additional executions of blkid won't get any extra information
and are redundant.  Therefore remove this unnecessary code.

Note that these last 2 commits remove creation of "blank" cache entries
(just block special with blank fstype and other attributes) when blkid
reports no information for a particular path.  Those entry were needed
to suppress unnecessary additional execution of blkid.  However now that
blkid is only executed once (excluding querying the label) this is no
longer necessary.  All the getter functions return suitable blank values
when no cache entry is found.

[1] e8f0504b13
    Make sure that FS_Info cache is loaded for all named paths (#787181)

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 416027de64 Remove extra execution of blkid for whole disk devices (#131)
On Fedora 31 with this simple disk layout where both sdb and sdc are
completely empty:
    # lsblk -o name,maj:min,rm,size,ro,type,fstype,label,mountpoint
    NAME            MAJ:MIN RM  SIZE RO TYPE FSTYPE      LABEL MOUNTPOINT
    sda               8:0    0   20G  0 disk
    |-sda1            8:1    0    1G  0 part ext4              /boot
    \-sda2            8:2    0   19G  0 part LVM2_member
      |-fedora-root 253:0    0   17G  0 lvm  ext4              /
      \-fedora-swap 253:1    0    2G  0 lvm  swap              [SWAP]
    sdb               8:16   0    8G  0 disk
    sdc               8:32   0    8G  0 disk
    sr0              11:0    1 1024M  0 rom
    # blkid /dev/sda /dev/sda1 /dev/sda2 /dev/sdb /dev/sdc
    /dev/sda: PTUUID="5012fb1f" PTTYPE="dos"
    /dev/sda1: UUID="3cd48816-7817-4636-9fec-5f1afe76c1b2" TYPE="ext4" PARTUUID="5012fb1f-01"
    /dev/sda2: UUID="PH94ej-C8xU-bnMJ-UIh8-ZimI-4B7f-dHlZxh" TYPE="LVM2_member" PARTUUID="5012fb1f-02"

Stracing GParted shows extra executions of blkid:
    # strace -f -q -bexecve -eexecve ./gpartedbin 2>&1 1> /dev/null | egrep -v 'ENOENT|SIGCHLD'
    ...
    [pid  7659] execve("/usr/sbin/blkid", ["blkid", "/dev/sda", "/dev/sda1", "/dev/sda2", "/dev/sdb", "/dev/sdc"], 0x1d300f0 /* 32 vars */ <detached ...>
    [pid  7660] execve("/usr/sbin/blkid", ["blkid", "/dev/sdb"], 0x1d300f0 /* 32 vars */ <detached ...>
    [pid  7661] execve("/usr/sbin/blkid", ["blkid", "/dev/sdc"], 0x1d300f0 /* 32 vars */ <detached ...>
    ...

blkid is only run again for sdb and sdc, not sda, because blkid didn't
report anything for them from the first execution.  GParted needs blkid
identification of whole disk devices to ensure that ISO9660 images on
whole disk devices are correctly identified [1].  Now the first run of
blkid passes all the device names, so this additional execution of blkid
won't get any extra information and is redundant.  Therefore remove this
unnecessary code.

[1] b2190372d0
    Ensure blkid FS_Info cache has entries for all whole disk devices
    (#771244)

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 8b35892ea5 Pass device and partition names to blkid (#131)
A user reported that GParted would hang at "scanning all devices...",
when a fully working disk was named on the command line, but another
device on the machine was hung.

This can be replicated like this:
(on Ubuntu 20.04 LTS for it's NBD support)

1. Export and import NBD:
    # truncate -s 1G /tmp/disk-1G.img
    # nbd-server -C /dev/null 9000 /tmp/disk-1G.img
    # nbd-client localhost 9000 /dev/nbd0

2. Hang the NBD server and therefore /dev/nbd0:
    # killall -STOP nbd-server

3. Run GParted:
    $ gparted /dev/sda

Tracing GParted shows that execution of blkid never returns.

    # strace -f -tt -q -bexecve -eexecve ./gpartedbin 2>&1 1> /dev/null | fgrep -v ENOENT
    ...
    [pid 37823] 13:56:24.814139 execve("/usr/sbin/mkudffs", ["mkudffs", "--help"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>
    [pid 37814] 13:56:24.829246 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37823, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---
    [pid 37825] 13:56:25.376796 execve("/usr/sbin/blkid", ["blkid", "-v"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>
    [pid 37824] 13:56:25.380824 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37825, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
    [pid 37826] 13:56:25.402512 execve("/usr/sbin/blkid", ["blkid"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>

Tracking of blkid shows that it hangs on either the open of or first
read from /dev/nbd0.

    # strace blkid
    ...
    lstat("/dev", {st_mode=S_IFDIR|0755, st_size=4560, ...}) = 0
    lstat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    stat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    lstat("/dev", {st_mode=S_IFDIR|0755, st_size=4560, ...}) = 0
    lstat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    access("/dev/nbd0", F_OK)               = 0
    stat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    openat(AT_FDCWD, "/sys/dev/block/43:0", O_RDONLY|O_CLOEXEC) = 4
    openat(4, "dm/uuid", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    close(4)                                = 0
    openat(AT_FDCWD, "/dev/nbd0", O_RDONLY|O_CLOEXEC

Clean up:

1. Resume NBD server:
    # killall -CONT nbd-server

2. Delete NBD setup:
    # nbd-client -d /dev/nbd0
    # killall nbd-server
    # rm /tmp/disk-1G.img

Fix this by making GParted specify the whole disk device and partition
names that it is interested in to blkid, rather than letting blkid scan
and report all block devices.  Do this both when GParted determines the
devices for itself and when they are named on the command line.

Also update example blkid command output being parsed and cache value
with this change to how blkid is executed.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 75bda733bb Refactor run_blkid_load_cache() into if fail return early (#131)
... code pattern.  Simplifies the code a little.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 884cd5a352 Read partition names from /proc/partitions too (#131)
GParted already always reads /proc/partitions for whole disk device
names no matter whether it uses whole disk devices named on the command
line, from /proc/partitions or from libparted.  As /proc/partitions
lists all the block devices that the kernel knows about, and therefore
all the possible ones blkid could probe, so use it to provide partition
names and device to partition mapping.  See code comments for more
details about the assumptions the /proc/partition parsing code makes and
the fact that these are confirmed by examining the Linux kernel source.

This commit just adds debugging to print the existing vector of
validated devices GParted shows in the UI and the vector with all
partitions added, ready for but not yet passed to blkid.
    # ./gpartedbin
    ...
    DEBUG: device_paths=["/dev/sda","/dev/sdb"]
    DEBUG: device_and_partition_paths=["/dev/sda","/dev/sda1","/dev/sda2","/dev/sdb","/dev/sdb1"]

Also demonstrating that this continues to support named devices,
including file system image files [1].
    # truncate -s 256M /tmp/ext4.img
    # mkfs.ext4 /tmp/ext4.img
    # ./gpartedbin /dev/sda /tmp/ext4.img
    ...
    DEBUG: device_paths=["/dev/sda","/tmp/ext4.img"]
    DEBUG: device_and_partition_paths=["/dev/sda","/dev/sda1","/dev/sda2","/tmp/ext4.img"]

[1] e8f0504b13
    Make sure that FS_Info cache is loaded for all named paths (#787181)

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 52930f30ae Refactor load_proc_partitions_info_cache() a bit (#131)
Put whole disk device name matching code into a helper function to make
the /proc/partition parsing code easier to understand.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood 45f88c3274 Merge FS_Info load cache calls (#131)
Now FS_Info::load_cache() and ::load_cache_for_paths() are nearly next
to each other, merge them together to simplify the code a little.  This
makes the special case to ensure that file system images named on the
command line were queried by blkid and loaded into the FS_Info cache [1]
become the normal cache loading method.  Already passing all discovered
or named devices to load_cache_for_paths() is also a step on the way to
doing it for all devices and partitions of interest.

Just need to ensure that load_cache_for_paths() always loads the cache
as load_cache() did, rather than only when it hadn't already been
loaded.  Otherwise GParted will only ever run blkid and load the cache
once at startup and not on each refresh.

[1] e8f0504b13
    Make sure that FS_Info cache is loaded for all named paths (#787181)

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Mike Fleetwood a8cd7a4e80 Initialise partition content discovery caches a bit later (#131)
PATCHSET OVERVIEW

A user reported that GParted would hang at "scanning all devices...",
when a fully working disk was named on the command line, but another
device on the machine was hung.

This can be replicated like this:
(on Ubuntu 20.04 LTS for it's NBD support)

1. Export and import NBD:
    # truncate -s 1G /tmp/disk-1G.img
    # nbd-server -C /dev/null 9000 /tmp/disk-1G.img
    # nbd-client localhost 9000 /dev/nbd0

2. Hang the NBD server and therefore /dev/nbd0:
    # killall -STOP nbd-server

3. Run GParted:
    $ gparted /dev/sda

Tracing GParted shows that execution of blkid never returns.

    # strace -f -tt -q -bexecve -eexecve /usr/sbin/gpartedbin 2>&1 1> /dev/null | fgrep -v ENOENT
    ...
    [pid 37823] 13:56:24.814139 execve("/usr/sbin/mkudffs", ["mkudffs", "--help"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>
    [pid 37814] 13:56:24.829246 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37823, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---
    [pid 37825] 13:56:25.376796 execve("/usr/sbin/blkid", ["blkid", "-v"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>
    [pid 37824] 13:56:25.380824 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37825, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
    [pid 37826] 13:56:25.402512 execve("/usr/sbin/blkid", ["blkid"], 0x55e2a3f2d230 /* 20 vars */ <detached ...>

Tracing of blkid shows that it hangs on either the open of or first
read from /dev/nbd0.

    # strace blkid
    ...
    lstat("/dev", {st_mode=S_IFDIR|0755, st_size=4560, ...}) = 0
    lstat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    stat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    lstat("/dev", {st_mode=S_IFDIR|0755, st_size=4560, ...}) = 0
    lstat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    access("/dev/nbd0", F_OK)               = 0
    stat("/dev/nbd0", {st_mode=S_IFBLK|0660, st_rdev=makedev(0x2b, 0), ...}) = 0
    openat(AT_FDCWD, "/sys/dev/block/43:0", O_RDONLY|O_CLOEXEC) = 4
    openat(4, "dm/uuid", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    close(4)                                = 0
    openat(AT_FDCWD, "/dev/nbd0", O_RDONLY|O_CLOEXEC

Clean up:

1. Resume NBD server:
    # killall -CONT nbd-server

2. Delete NBD setup:
    # nbd-client -d /dev/nbd0
    # killall nbd-server
    # rm /tmp/disk-1G.img

Going to fix this by making GParted specify the device and partition
names that it is interested in to blkid, rather than letting blkid scan
and report all block devices.  Do this both when GParted determines the
devices for itself and when they are named on the command line.

THIS PATCH

Move the loading and initialising of caches used during content
discovery to after device and partition discovery and just before
content discovery.  Just makes the code ready for the next change.

Closes #131 - GParted hangs when non-named device is hung
2021-02-10 16:30:13 +00:00
Curtis Gedak 423a57ec4a Update copyright years 2021-01-25 09:55:15 -07:00
Mike Fleetwood 3c95419ed2 White space tidy-up of Utils::get_filesystem_software()
Put colon directly after case value and list cases in enumeration order.
2021-01-15 19:55:17 +00:00
Mike Fleetwood b0a061cf7a Set the partition type for exFAT correctly (!30)
Libparted only allows selection of the partition type indirectly by
specifying the type of the file system it will contain [1] and so far
doesn't know about the exFAT file system.  Therefore when GParted is
creating a new exFAT partition, it gets the GParted default of 83
(Linux file system) on MBR partition tables.

Example operation details:
    Create Primary Partition #1 (exfat, 512.00 MiB) on /dev/sdb
    * create empty partition
    * clear old file system signatures in /dev/sdb1
    * set partition type on /dev/sdb1
        new partition type: ext2
    * create new exfat file system

fdisk report:
    # fdisk -l /dev/sdb
    Disk /dev/sdb: 8 GiB, 8589934592 bytes, 16777216 sectors
    Disk model: VBOX HARDDISK
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0xa2aab629

    Device     Boot Start     End Sectors  Size Id Type
    /dev/sdb1        2048 1050623 1048576  512M 83 Linux

However the "exFAT file system specification" says:
    https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
    "10.2 Partition Tables

    To ensure interoperability of exFAT volumes in a broad set of usage
    scenarios, implementations should use partition type 07h for MBR
    partitioned storage and partition GUID
    {EBD0A0A2-B9E5-4433-87C0-68B6B72699C7} for GPT partitioned storage.
    "

Fix this.

[1] ped_partition_new(..., const PedFileSystemType* fs_type, ...)
    https://www.gnu.org/software/parted/api/group__PedPartition.html#g2f94ca75880f9e0c3ce57f7a4b72faf5
    ped_partition_set_system(..., const PedFileSystemType* fs_type)
    https://www.gnu.org/software/parted/api/group__PedPartition.html#g2f94ca75880f9e0c3ce57f7a4b72faf5

Closes !30 - Add exFAT support
2021-01-15 19:55:17 +00:00
Mike Fleetwood bd386f445d Add exFAT support (!30)
With exfatprogs (https://github.com/exfatprogs/exfatprogs) installed the
following operations on exFAT file systems are supported:
- Creation
- Checking
- Labelling
As of the current exfatprogs 1.0.4 the following are not supported:
- Reading usage
- Resizing
- Updating the UUID

Closes !30 - Add exFAT support
2021-01-15 19:55:17 +00:00
Curtis Gedak cca15b4c9f Set default partition alignment to cylinder for amiga partition table (#116)
Closes #116 - Fails to create partitions on disks with Amiga partition
              tables using default settings
2020-11-24 14:42:12 +00:00
Mike Fleetwood 201f5f2f2f Add missing includes into Devices module 2020-05-27 16:02:47 +00:00
Mike Fleetwood b74b05f334 Replace TRUE #define with C++ true literal
Everywhere else in the code the lower case true C++ boolean literal is
used.  Change this one place where upper case TRUE #define was used to
match.
2020-03-14 15:31:37 +00:00
Mike Fleetwood 21a2da9e24 Rename local variables to mke2fs_*_ver
To better reflect that they represent the version of mke2fs executable
from the e2fsprogs package, even though the executable is called as
mkfs.ext4.

    $ ls -il /sbin/mke2fs /sbin/mkfs.ext*
    1978670 -rwxr-xr-x. 4 root root 96384 Aug  9  2019 /sbin/mke2fs
    1978670 -rwxr-xr-x. 4 root root 96384 Aug  9  2019 /sbin/mkfs.ext2
    1978670 -rwxr-xr-x. 4 root root 96384 Aug  9  2019 /sbin/mkfs.ext3
    1978670 -rwxr-xr-x. 4 root root 96384 Aug  9  2019 /sbin/mkfs.ext4
    $ mkfs.ext4 -V
    mke2fs 1.42.9 (28-Dec-2013)
            Using EXT2FS Library version 1.42.9
    $ mke2fs -V
    mke2fs 1.42.9 (28-Dec-2013)
            Using EXT2FS Library version 1.42.9
2020-02-28 17:37:32 +00:00
Mike Fleetwood bfbd324d39 Simplify sscanf("mke2fs ...") text match
With removal of support for RHEL / CentOS 5 and it's e4fsprogs package
[1][2] it is no longer necessary to accept:
    mke4fs 1.41.12 (17-May-2010)
only:
    mke2fs 1.42.9 (28-Dec-2013)

[1] 6c4ab5dc28
    Remove checks for e4fsprogs commands (#794253)

[2] de6e70d933
    Simplify ext2::get_filesystem_support() with regard ext4 support (#794253)
2020-02-28 17:37:32 +00:00
Mike Fleetwood a9015111b9 Raise minimum supported dosfstools to 3.0.18 (!57)
This earlier commit [1] from 2013 recognised the new names for programs
in dosfstools >= 3.0.18, specifically mkfs.fat and fsck.fat.  Now that
the oldest supported distributions use dosfstools >= 3.0.18 it is no
longer necessary to support using the old names of mkdosfs and dosfsck,
so remove that code.

    Oldest supported   Dosfstools
    distributions      Version

    Debian 8           3.0.27
    RHEL / CentOS 7    3.0.20
    SLES 12            3.0.26
    Ubuntu 14.04 LTS   3.0.26

[1] 1ae03dee95
    Recognise new dosfstools program names (#704629)

Closes !57 - Raise minimum support dosfstools to 3.0.18 released
             2013-06-06
2020-02-28 17:37:32 +00:00
Mike Fleetwood 8ae9abada4 Wait for udev change on /dev/DISK when erasing signatures (#83)
A user reported that formatting a whole disk device with a file system
failed like this:

    Format /dev/sdd as ext4                                    (ERROR)
    + calibrate /dev/sdd                                       (SUCCESS)
        path: /dev/sdd (device)
        start: 0
        end: 15633407
        size: 15633408 (7.45 GiB)
    + clear old file system signatures in /dev/sdd             (SUCCESS)
      + write 512.00 KiB of zeros at byte offset 0             (SUCCESS)
      + write 4.00 KiB of zeros at byte offset 67108864        (SUCCESS)
      + write 512.00 KiB of zeros at byte offset 8003780608    (SUCCESS)
      + write 4.00 KiB of zeros at byte offset 8004239360      (SUCCESS)
      + write 8.00 KiB of zeros at byte offset 8004296704      (SUCCESS)
      + flush operating system cache of /dev/sdd               (SUCCESS)
    + create new ext4 file system                              (ERROR)
      + mkfs.ext4 -F -O ^64bit -L '' '/dev/sdd'                (ERROR)
        mke2fs 1.44.1 (24-Mar-2018)
        /dev/sdd is apparently in use by the system; will not make a filesystem here!

Opening the whole disk block device exclusively causes mkfs.ext4 to
report that error like this:

    # python
    >>> import os
    >>> f = os.open('/dev/sdb',os.O_RDONLY|os.O_EXCL)
    >>> ^Z
    [1]+  Stopped                 python
    # mkfs.ext4 -F -O ^64bit -L '' '/dev/sdb'
    mke2fs 1.42.9 (28-Dec-2013)
    /dev/sdb is apparently in use by the system; will not make a filesystem here!
    # echo $?
    1

I have not been able to reproduce this error, but with debugging and
sleeping in GParted, stracing GParted and using 'udevadm monitor' to
watch udev events the following sequence of events is seen:

  gparted    |format(partition, operationdetail)
  gparted    |  erase_filesystem_signatures(partition, operationdetail)
  gparted    |    get_device(device_path="/dev/sdb", lp_device, flush=false)
  gparted    |      ped_device_get("/dev/sdb")
  libparted  |        open("/dev/sdb", O_RDONLY) = 11
  libparted  |        close(11)
  gparted    |    ped_device_open(lp_device)
  libparted  |      open("/dev/sdb", O_RDWR) = 11
  gparted    |    ped_device_sync(lp_device)
  libparted  |      ioctl(11, BLKFLSBUF)
  gparted    |    ped_device_close()
  libparted  |      close(11)
  udev(async)|        KERNEL change /devices/.../sdb (block)
  udev(async)|        UDEV   change /devices/.../sdb (block)
  gparted    |  set_partition_type(partition, operationdetail)
  gparted    |  create_filesystem(partition, operationdetail)
  gparted    |    ext2::create(partition, operationdetail)
  gparted    |      FileSystem::execute_command("mkfs.ext4 -F -O ^64bit -L '' '/dev/sdb')

So it is assumed that the processing of the udev change rule after
closing the block device in erase_filesystem_signatures() overlaps with
the execution mkfs.ext4 and causes the seen error.  Fix by waiting for
those udev events to complete as was previously done by commits [1][2]
[3].

Also note that this is specific to creating file systems on and
formatting unpartitioned whole disk devices because set_partition_type()
is a no-operation.  Where as on a partitioned device
set_partition_type() calls commit() which already waits for udev rules
to complete [3].

[1] 50c8924a8e4d9cc96a2ea45f13291114402affee
    Wait for udev to recreate /dev/PTN entries when querying partition
    FSs (!46)
[2] 4f6c312e3bc68cafb5e6035fd4a5b5bbbfcea992
    Wait for udev change on /dev/DISK when querying whole device FS
    (!46)
[3] 2f53876c0f
    Wait for the kernel and udev to settle partitions for a second time
    (#790418)

Closes #83 - /dev/sdd is apparently in use by the system; will not make
             a filesystem here!
2020-02-26 16:41:43 +00:00
Curtis Gedak 77de664881 Update copyright years 2020-01-20 09:56:07 -07:00
Mike Fleetwood 01826277e3 Rename TreeView_Detail::treeview_filesystems_Columns member to fsname (!52)
Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:38:01 +00:00
Mike Fleetwood e5b92a87f2 Rename DialogFeatures::treeview_filesystems_Columns member to fsname (!52)
Name the structure member to 'fsname' used to store strings like "ext2"
etc.  This is equivalent to what was previously done in this commit:
    a9f08ddc7d
    Rename local variable to fsname in get_filesystem() (#741430)

Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:38:01 +00:00
Mike Fleetwood f53c462c06 Rename create_format_menu_add_item() parameter of type FSType (!52)
Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:38:00 +00:00
Mike Fleetwood 2cbb867508 Rename set_device_partitions() local variable of type FSType (!52)
Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:38:00 +00:00
Mike Fleetwood c85fc66dcf Rename Utils method parameters of type FSType (!52)
Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:38:00 +00:00
Mike Fleetwood 58fb230fb0 Also rename FS.filesystem member to fstype (!52)
Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-04 07:37:19 +00:00
Mike Fleetwood b0f92be638 Rename Partition.filesystem member to fstype (!52)
Previously made this change:
    175d27c55d
    Rename enum FILESYSTEM to FSType

Now complete the renaming exercise of members and variables currently
named 'filesystem'.

Closes !52 - Rename members and variables currently named 'filesystem'
2019-12-03 13:24:44 +00:00
Mike Fleetwood 047a2481bb Stop requesting partition paths of free space and metadata
In GParted_Core::set_device_partitions() the partition path is being
queried from libparted.  However this is done before the switch
statement on the type of the partition, so is called for all libparted
partition objects including PED_PARTITION_FREESPACE and
PED_PARTITION_METADATA ones.  As libparted numbers these partition
objects as -1, it returns paths like "/dev/sda-1".

Additionally when using GParted, with it's default DMRaid handling, on a
dmraid started array this results in paths like
"/dev/mapper/isw_ecccdhhiga_MyArray-1" being passed to
is_dmraid_device() and make_path_dmraid_compatible().  Fortunately
make_path_dmraid_compatible() does nothing and returns the same name.
Call chain looks like:

    GParted_Core::set_device_partitions()
      get_partition_path(lp_partition)
        // where:
        // lp_partition->disk->dev->path = "/dev/mapper/isw_ecccdhhiga_MyArray"
        // lp_partition->type == PED_PARTITION_FREESPACE |
        //                       PED_PARTITION_METADATA
        //              ->num == -1
        ped_partition_get_path(lp_partition)
          return "/dev/mapper/isw_ecccdhhiga_MyArray-1"
        dmraid.is_dmraid_supported()
        dmraid.is_dmraid_device("/dev/mapper/isw_ecccdhhiga_MyArray-1")
          return true
        dmraid.make_path_dmraid_compatible("/dev/mapper/isw_ecccdhhiga_MyArray-1")
          return "/dev/mapper/isw_ecccdhhiga_MyArray-1"

Fix by moving the get_partition_path() call inside the switch statement
so that it is only called for PED_PARTITION_NORMAL,
PED_PARTITION_LOGICAL and PED_PARTITION_EXTENDED partition types.

Relevant commits:
*   53c49349f7
    Simplify logic in set_device_partitions method

*   81986c0990
    Ensure partition path name is compatible with dmraid (#622217)
2019-12-02 16:35:22 +00:00
Mike Fleetwood 21cad97dc7 Recognise ATARAID members started by dmraid (#75)
This is not strictly necessary as members are already recognised using
blkid since this commit earlier in the sequence "Recognise ATARAID
members (#75)".  However it makes sure active members are recognised
even if blkid is not available and matches how file system detection
queries the SWRaid_Info module.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood bb865aaaa4 Display array device as mount point of dmraid started ATARAID members (#75)
This matches how the array device is displayed as the mount point for
mdadm started ATARAID members by "Display array device as mount point of
mdadm started ATARAID members (#75)" earlier in this patchset.

Extend the DMRaid module member cache to save the array device name and
use as needed to display as the mount point.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood caec22871e Detect busy status of dmraid started ATARAID members (#75)
Again this is to stop GParted allowing overwrite operations being
performed on an ATARAID member while the array is actively using the
member.  This time for dmraid started arrays using the kernel DM (Device
Mapper) driver.

The DMRaid module already uses dmraid to report active array names:

    # dmraid -sa -c
    isw_ecccdhhiga_MyArray

To find active members in this array, (1) use udev to lookup the kernel
device name:

    # udevadm info --query=name /dev/mapper/isw_ecccdhhiga_MyArray
    dm-0

(2) list the member names exposed by the kernel DM driver through the
/sys file system.

    # ls /sys/block/dm-0/slaves
    sdc  sdd
    # ls -l /sys/block/dm-0/slaves
    lrwxrwxrwx 1 root root 0 Nov 24 09:52 sdc -> ../../../../pci0000:00/0000:00:0d.0/ata3/host2/target2:0:0/2:0:0:0/block/sdc
    lrwxrwxrwx 1 root root 0 Nov 24 09:52 sdc -> ../../../../pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdd

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood 425dfa3709 Enable basic supported actions for ATARAID members (#75)
When an ATARAID member is inactive allow basic supported actions of
copy and move to be performed like with other recognised but only basic
supported types.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood 1f1f44ff7a Prevent unmount of busy ATARAID members (#75)
Since earlier commit "Display array device as mount point of mdadm
started ATARAID members (#75)" GParted allows attempting to unmout a
busy ATARAID member as if it was a file system.  This is not a valid
thing to do, so disallow it.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood f6c86835eb Display array uuid of mdadm recognised ATARAID members (#75)
Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood 538c866d09 Display array device as mount point of mdadm started ATARAID members (#75)
This matches how other non-file systems are handled, by displaying the
access reference in the mount point column.  For LVM Physical Volumes
the Volume Group name is displayed [1] and for an active Linux Software
RAID array the array device is displayed [2].

[1] 8083f11d84
    Display LVM2 VGNAME as the PV's mount point (#160787)

[2] f6c2f00df7
    Populate member mount point with SWRaid array device (#756829)

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood 6e990ea48a Detect busy status of mdadm started ATARAID members (#75)
This stops GParted allowing overwrite operations (such as create
partition table or format with a whole device file system) being
performed on an ATARAID member while the array is actively using the
member.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood ef6794b7de Display correct type of mdadm recognised ATARAID members (#75)
The previous commit, made mdadm recognised IMSM and DDF type ATARAID
members get displayed as "linux-raid" (Linux Software RAID array
member).  This was because of query method 1 in detect_filesystems().

Fix this now by exposing and using the fstype of the member from the
SWRaid_Info cache.

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood 73bf8bef62 Parse ATARAID members from mdadm output and /proc/mdstat (#75)
Since mdadm release 3.0 (2009-06-02) [1] it has also supported external
metadata formats IMSM (Intel Matrix Storage Manager) and DDF, previously
only managed by dmraid.

A number of distributions have switched to use mdadm and kernel MD
(Multiple Devices) driver for managing these Firmware / BIOS / ATARAID
arrays.  These include: Fedora >= 14 [2], RHEL / CentOS >= 6 [3],
SLES >= 12 [4], Ubuntu >= 16.04 LTS.

Therefore additionally parse members in these ATARAID arrays included in
mdadm output, and when activated using the kernel MD driver, in file
/proc/mdstat.  Add fstype to the SWRaid_Info cache records to
distinguish members apart.  So far the rest of the GParted code
continues to treat all members as FS_LINUX_SWRAID.  This will be
resolved in following commits.

Note that this in no way affects how GParted shows and partitions the
array device itself, even those managed by dmraid and use the GParted
DMRaid module.  It only affects how GParted shows the member drives
themselves.

[1] mdadm ANNOUNCE-3.0 file
    https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/tree/ANNOUNCE-3.0?h=mdadm-3.0

[2] Fedora 14, Storage Administration Guide, 12.5. Linux RAID Subsystem
    https://docs.fedoraproject.org/en-US/Fedora/14/html/Storage_Administration_Guide/raid-subsys.html
    "...  Fedora 14 uses mdraid with external metadata to access ISW /
    IMSM (Intel firmware RAID) sets.  mdraid sets are configured and
    controlled through the mdadm utility."

[3] RHEL 6, Storage Administration Guide, 17.3. Linux RAID Subsystem
    https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/storage_administration_guide/raid-subsys
    "mdraid also supports other metadata formats, known as external
    metadata.  Red Hat Enterprise Linux 6 uses mdraid with external
    metadata to access ISW / IMSM (Intel firmware RAID) sets.  mdraid
    sets are configured and controlled through the mdadm utility."

[4] SUSE Linux Enterprise Server 12 Release Notes, 7.2.3 Driver for IMSM
    and DDF
    https://www.suse.com/releasenotes/x86_64/SUSE-SLES/12/#fate-316007
    "For IMSM and DDF RAIDs the mdadm driver is used unconditionally."

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood aea6200d5f Recognise ATARAID members (#75)
PATCHSET OVERVIEW

A user had a Firmware / BIOS / ATARAID array of 2 devices configured as
a RAID 0 (stripe) set.  On top of that was a GPT with the OS partitions.
GParted displays the following errors on initial load and subsequent
refresh:

        Libparted Error
    (-) Invalid argument during seek for read on /dev/sda
                          [ Retry ] [ Cancel ] [ Ignore ]

        Libparted Error
    (-) The backup GPT table is corrupt, but the
        primary appears OK, so that will be used.
                              [  Ok  ] [ Cancel ]

This is an Intel Software RAID array which stores metadata at the end of
each member device, and so the first 128 KiB stripe of the set is stored
in the first 128 KiB of the first member device /dev/sda which includes
the GPT for the whole RAID 0 device.  Hence when libparted reads member
device /dev/sda it finds a GPT describing a block device twice it's
size and in results the above errors when trying to read the backup GPT.

A more dangerous scenario occurs when using 2 devices configured in an
Intel Software RAID 1 (mirrored) set with GPT on top.  On refresh
GParted display this error for both members, /dev/sda and /dev/sdb:

        Libparted Warning
    /!\ Not all of the space available to /dev/sda appears to be used,
        you can fix the GPT to use all of the space (an extra 9554
        blocks) or continue with the current setting?
                                                  [  Fix  ] [ Ignore ]

Selecting [Fix] gets libparted to re-write the backup GPT to the end of
the member device, overwriting the ISW metadata!  Do that twice and both
copies of the metadata are gone!

Worked example of this more dangerous mirrored set case.  Initial setup:

    # dmraid -s
    *** Group superset isw_caffbiaegi
    --> Subset
    name   : isw_caffbiaegi_MyMirror
    size   : 16768000
    stride : 128
    type   : mirror
    status : ok
    subsets: 0
    devs   : 2
    spares : 0

    # dmraid -r
    /dev/sda: isw, "isw_caffbiaegi", GROUP, ok, 16777214 sectors, data@ 0
    /dev/sdb: isw, "isw_caffbiaegi", GROUP, ok, 16777214 sectors, data@ 0

    # wipefs /dev/sda
    offset               type
    ---------------------------------------------
    0x200                gpt   [partition table]
    0x1fffffc00          isw_raid_member   [raid]

Run GParted and click [Fix] on /dev/sda.  Now the first member has gone:

    # dmraid -s
    *** Group superset isw_caffbiaegi
    --> *Inconsistent* Subset
    name   : isw_caffbiaegi_MyMirror
    size   : 16768000
    stride : 128
    type   : mirror
    status : inconsistent
    subsets: 0
    devs   : 1
    spares : 0

    # dmraid -r
    /dev/sdb: isw, "isw_caffbiaegi", GROUP, ok, 16777214 sectors, data@ 0

    # wipefs /dev/sda
    offset               type
    ---------------------------------------------
    0x200                gpt   [partition table]

Click [Fix] on /dev/sdb.  Now all members of the array are gone:

    # dmraid -s
    no raid disks

    # dmraid -r
    no raid disks

    # wipefs /dev/sdb
    offset               type
    ---------------------------------------------
    0x200                gpt   [partition table]

So GParted must not run libparted partition table scanning on the member
devices in ATARAID arrays.  Only on the array device itself.

In terms of the UI GParted must show disks which are ATARAID members as
whole disk devices with ATARAID member content and detect array busy
status to avoid allowing active members from being overwritten while in
use.

THIS COMMIT

Recognise ATARAID member devices and display in GParted as whole device
"ataraid" file systems.  Because they are recognised as whole device
content ("ataraid" file systems) this alone stops GParted running the
libparted partition table scanning and avoids the above errors.

The list of dmraid supported formats is matched by the signatures
recognised by blkid:

    $ dmraid -l
    asr     : Adaptec HostRAID ASR (0,1,10)
    ddf1    : SNIA DDF1 (0,1,4,5,linear)
    hpt37x  : Highpoint HPT37X (S,0,1,10,01)
    hpt45x  : Highpoint HPT45X (S,0,1,10)
    isw     : Intel Software RAID (0,1,5,01)
    jmicron : JMicron ATARAID (S,0,1)
    lsi     : LSI Logic MegaRAID (0,1,10)
    nvidia  : NVidia RAID (S,0,1,10,5)
    pdc     : Promise FastTrack (S,0,1,10)
    sil     : Silicon Image(tm) Medley(tm) (0,1,10)
    via     : VIA Software RAID (S,0,1,10)
    dos     : DOS partitions on SW RAIDs

    $ fgrep -h _raid_member util-linux/libblkid/src/superblocks/*.c
            .name           = "adaptec_raid_member",
            .name           = "ddf_raid_member",
            .name           = "hpt45x_raid_member",
            .name           = "hpt37x_raid_member",
            .name           = "isw_raid_member",
            .name           = "jmicron_raid_member",
            .name           = "linux_raid_member",
            .name           = "lsi_mega_raid_member",
            .name           = "nvidia_raid_member",
            .name           = "promise_fasttrack_raid_member",
            .name           = "silicon_medley_raid_member",
            .name           = "via_raid_member",

As they are all types of Firmware / BIOS / ATARAID arrays, report all
members as a single "ataraid" file system type.  (Except for
"linux_raid_member" in the above blkid source listing which is Linux
Software RAID).

Closes #75 - Errors with GPT on RAID 0 ATARAID array
2019-12-02 16:35:22 +00:00
Mike Fleetwood af60f91f7f Add missing includes into jfs.cc 2019-11-14 17:12:06 +00:00
Mike Fleetwood 4b8d4be789 Remove unallocated space comment from HACKING file (!50)
The HACKING file should be hints for making changes to the code base and
associated processes.  A overview of how GParted handled unallocated
space was not that.  Also now the size of a JFS is accurately calculated
using JFS as an example of a file system with intrinsic unallocated
space is no longer valid.  Therefore removed from the HACKING file.
Instead add the original commit message as an extended comment to method
calc_significant_unallocated_sectors().

Closes !50 - Calculate JFS size accurately
2019-11-14 17:12:06 +00:00
Mike Fleetwood 2c0572e296 Calculate mounted JFS size accurately (!50)
With the same minimum sized 16 MiB JFS used in the previous commit, now
mounted, GParted once again reports 1.20 MiB of unallocated space.  This
is because the kernel JFS driver is also just reporting the size of the
Aggregate Disk Map (dmap) as the size of the file system [1].

Fix by reading the on disk JFS superblock to calculate the size of the
file system, but query the free space from the kernel using statvfs().
Need to query mounted JFS free space from the kernel because the on disk
dmap is not updated immediately so doesn't reflect recently used or
freed disk space.

For example, start with the 16 MiB JFS empty and mounted.

    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1
    # df -k /mnt/1
    Filesystem     1K-blocks  Used Available Use% Mounted on
    /dev/sdb1          15152   136     15016   1% /mnt/1

Write 10 MiB of data to it:

    # dd if=/dev/zero bs=1M count=10 of=/mnt/1/file_10M
    10+0 records in
    10+0 records out
    1048760 bytes (10 MB, 10 MiB) copied, 0.0415676 s, 252 MB/s

Query the file system free space from the kernel and by reading the on
disk dmap figure:

    # df -k /mnt/1
    Filesystem     1K-blocks  Used Available Use% Mounted on
    /dev/sdb1          15152 10376      4776  69% /mnt/1
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1

    # sync
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1

    # umount /mnt/1
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x000000004aa   [10] dn_agwidth:        1

The kernel reports the updated usage straight away, but the on disk dmap
record doesn't get updated even by sync, only after unmounting.

This is the same fix as was previously done for EXT2/3/4 [2].

[1] Linux jfs_statfs() function
    https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/jfs/super.c?h=v3.10#n142

[2] 3828019030
    Read file system size for mounted ext2/3/4 from superblock (#683255)

Closes !50 - Calculate JFS size accurately
2019-11-14 17:12:06 +00:00
Mike Fleetwood e55d10b919 Calculate unmounted JFS size accurately (!50)
Create the smallest possible JFS (16 MiB) and GParted will report
1.2 MiB of unallocated space.  This is because the size of the Aggregate
Disk Map (dmap) was used as the size of the file system.  However after
reading the source code to mkfs.jfs, it separately accounts for the size
of the Log (Journal) and the FSCK Working Space.  The size of a JFS is
the sum of these 3 components added together.

Using the minimum 16 MiB JFS as an example:

    # jfs_debugfs /dev/sdb1
    jfs_debugfs version 1.1.15, 04-Mar-2011

    Aggregate Block Size: 4096

    > superblock
    [1] s_magic:            'JFS1'          [15] s_ait2.addr1:      0x00
    [2] s_version:          1               [16] s_ait2.addr2:      0x00000018
    [3] s_size:     0x0000000000007660           s_ait2.address:    24
    [4] s_bsize:            4096            [17] s_logdev:          0x00000000
    [5] s_l2bsize:          12              [18] s_logserial:       0x00000000
    [6] s_l2bfactor:        3               [19] s_logpxd.len:      256
    [7] s_pbsize:           512             [20] s_logpxd.addr1:    0x00
    [8] s_l2pbsize:         9               [21] s_logpxd.addr2:    0x00000f00
    [9] pad:                Not Displayed        s_logpxd.address:  3840
    [10] s_agsize:          0x00002000      [22] s_fsckpxd.len:     52
    [11] s_flag:            0x10200900      [23] s_fsckpxd.addr1:   0x00
                            JFS_LINUX       [24] s_fsckpxd.addr2:   0x00000ecc
            JFS_COMMIT      JFS_GROUPCOMMIT      s_fsckpxd.address: 3788
                            JFS_INLINELOG   [25] s_time.tv_sec:     0x5dbbdfa0
                                            [26] s_time.tv_nsec:    0x00000000
                                            [27] s_fpack:           'small_jfs'
    [12] s_state:           0x00000000
                 FM_CLEAN
    [13] s_compress:        0
    [14] s_ait2.len:        4

    display_super: [m]odify or e[x]it: x
    > dmap

    Block allocation map control page at block 16

    [1] dn_mapsize:         0x00000000ecc   [9] dn_agheigth:        0
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1
    [3] dn_l2nbperpage:     0               [11] dn_agstart:        341
    [4] dn_numag:           1               [12] dn_agl2size:       13
    [5] dn_maxlevel:        0               [13] dn_agfree:         type 'f'
    [6] dn_maxag:           0               [14] dn_agsize:         8192
    [7] dn_agpref:          0               [15] pad:               Not Displayed
    [8] dn_aglevel:         0
    display_dbmap: [m]odify, [f]ree count, [t]ree, e[x]it > x
    > quit

Values of interest:
    s_size        - Aggregate size in device (s_pbsize) blocks
    s_bsize       - Aggregate block (aka file system allocation) size in
                    bytes
    s_pbsize      - Physical (device) block size in bytes
    s_logpxd.len  - Log (Journal) size in Aggregate (s_bsize) blocks
    s_fsckpxd.len - FSCK Working Space in Aggregate (s_bsize) blocks
    dn_nfree      - Number of free (s_bsize) blocks in Aggregate

Calculation:
    file system size = s_size * s_pbsize
                     + s_logpxd.len * s_bsize
                     + s_fsckpxd.len * s_bsize
                     = 30304 * 512
                     + 256 * 4096
                     + 52 * 4096
                     =  16777216
                        (Exactly 16 MiB.  The size of the partition.)
    free space = dn_nfree * s_bsize
               = 3754 * 4096
               = 15376384

Rewrite JFS usage querying code to use this updated calculation.

[1] JFS Overview / How the Journaled File System cuts system restart
    times to the quick
    http://jfs.sourceforge.net/project/pub/jfs.pdf
[2] JFS Layout / How the Journaled File systems handles the on-disk
    layout
    http://jfs.sourceforge.net/project/pub/jfslayout.pdf
[3] mkfs.jfs source code
    http://jfs.sourceforge.net/project/pub/jfsutils-1.1.15.tar.gz
    mkfs/mkfs.c
    Selected lines from mkfs/mkfs.c
        create_aggregate(..., number_of_blocks, ..., logsize, ...)
            number_of_blocks -= fsck_wspace_length;
            aggr_superblock.s_size = number_of_blocks * (aggr_block_size / phys_block_size);
            aggr_superblock.s_bsize = aggr_block_size;
            aggr_superblock.s_pbsize = phys_block_size;
            PXDlength(&aggr_superblock.s_logpxd, logsize);
            PXDlength(&aggr_superblock.s_fsckpxd, fsck_wspace_length);
        main()
            number_of_bytes = bytes_on_device;
            number_of_blocks = number_of_bytes / agg_block_size;
            logsize = logsize_in_bytes / aggr_block_size;
            number_of_blocks -= logsize;
            create_aggregate(..., number_of_blocks, ..., logsize, ...);

Closes !50 - Calculate JFS size accurately
2019-11-14 17:12:06 +00:00
Mike Fleetwood 7be6d0967a Update name of the NILFS2 specific package
Upstream NILFS project calls the package nilfs-utils [1][2].  Arch Linux
/ CentOS / Fedora / OpenSUSE use the upstream name.  However Debian /
Ubuntu name it nilfs-tools [3] instead.

Document the needed software as:

    nilfs-utils / nilfs-tools

Upstream name first separated by slash from alternative names
distributions use.

[1] NILFS Download page
    https://nilfs.sourceforge.io/en/download.html
[2] NILFS Public Git Repositories
    https://nilfs.sourceforge.io/en/git_repos.html
[3] Debian package: nilfs-tools
    https://packages.debian.org/sid/nilfs-tools
2019-11-09 17:18:34 +00:00
Mike Fleetwood 7a7d0a2119 Avoid crash reading JFS usage on Fedora 30 (!49)(#794947)
Running JFS read usage test on Fedora 30 fails like this:

    $ ./test_SupportedFileSystems --gtest_filter='*ReadUsage/jfs'
...
    [ RUN      ] My/SupportedFileSystemsTest.CreateAndReadUsage/jfs
    unknown file: Failure
    C++ exception with description "std::bad_alloc" thrown in the test body.
    [  FAILED  ] My/SupportedFileSystemsTest.CreateAndReadUsage/jfs, where GetParam() = 17 (41833 ms)

However the same test passes on Fedora 29, Fedora 31 Beta, CentOS 7,
Debian 10 and Ubuntu 18.04 LTS.

Also running GParted on Fedora 30 crashes just the same when reading JFS
usage:

    # gparted
    GParted 1.0.0
    configuration --enable-libparted-dmraid --enable-online-resize
    libparted 3.2
    terminate called after throwing an instance of 'std::bad_alloc'
      what():  std::bad_alloc
    /usr/bin/gparted: line 202: 19218 Aborted                 (core dumped) $BASE_CMD

Running jfs_debugfs to query the file system usage the same way GParted
does produces an infinite amount of repeating output:

    # echo dm | jfs_debugfs /dev/sdb1

So jfs_debugfs gets stuck in an infinite loop inside the dmap subcommand
when it encounters EOF.  GParted and the read JFS usage test read this
output until memory is exhausted and crash.  This is exactly what was
happening in closed bug 794947.  Even installed jfsutils from Fedora 29
on Fedora 30 and visa versa.  jfs_debugfs still produced an infinite
amount of output on Fedora 30 and worked correctly on Fedora 29.  So
it's not the build of jfsutils, but something in the OS that is making
the difference!

Anyway fix by providing the instruction to exit from the dmap
subcommand, and quit from jfs_debugfs itself, like this:

    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1

Bug 794947 - gparted hangs when sees JFS partition on discovering
             partitions
Closes !49 - Add file system interface tests
2019-11-09 17:18:34 +00:00
Mike Fleetwood 6d121ebb5d Split FILESYSTEMS and FILESYSTEM_MAP into separate module (!49)
GParted_Core::FILESYSTEMS and ::FILESYSTEM_MAP and the methods that
query and manipulate them are self-contained.  Therefore move them into
a separate SupportedFileSystems module.

Also having a single class maintaining all FileSystem interface objects
will make testing all the file system types much easier as there will be
no need to duplicate this functionality in the test.

Closes !49 - Add file system interface tests
2019-11-09 17:18:34 +00:00
Mike Fleetwood dae4deff39 Rename enumeration to MENU_LABEL_FILESYSTEM
To match the renaming performed as part of Bug 741424 "Add support for
GPT partition names".  In particular this is most closely similar to:
    d480800600
    Rename enum to OPERATION_LABEL_FILESYSTEM (#741424)
2019-10-03 15:36:06 +00:00
Mike Fleetwood a4c00e03e6 Drop use of long ago removed udevsettle
Separate udevsettle command was merged into udevadm back in udev 117
(released 2007-11-13) by:
    225cb03bd8
    udevadm: merge all udev tools into a single binary

The oldest supported distributions have these much newer versions of
udev:
  Distro             EOL        udevadm -V
  Debian 8           2020-Apr   215 (combined systemd and udev)
  RHEL / CentOS 7    2024-Jun   219 (combined systemd and udev)
  Ubuntu 16.04 LTS   2021-Apr   229 (combined systemd and udev)
2019-07-18 15:27:43 +00:00
Mike Fleetwood df65fbd468 Order internally detected signatures by increasing offset (!46)(#16)
Along with the previous patch "Avoid reading the same sector multiple
times in a row (!46)(#16)" internal detection of file system signatures
now reads the minimum number of sectors in increasing order.

As the code still considers the sector size, it now also reads less
sectors from devices with larger sectors to get all the signatures.  So
on a 512 byte sector device it maximally seeks to and reads from these
byte offsets:
    0, 512, 1024, 65536
and on a 4096 byte sector device, only from these byte offsets:
    0, 65536

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
Closes #16 - "invalid argument for seek()" error on very small (<=40KiB)
             drives
2019-07-18 15:27:43 +00:00
Mike Fleetwood 869ebb71ea Avoid reading the same sector multiple times in a row (!46)(#16)
GParted internal file system detection is reading the same sector
multiple times in a row.  Add an optimisation to only read a sector if
it is different to the previously read sector.

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
Closes #16 - "invalid argument for seek()" error on very small (<=40KiB)
             drives
2019-07-18 15:27:43 +00:00
Mike Fleetwood 5bb3415bcb Just pass sector size to detect_filesystem_internal() (!46)(#16)
After the previous commit the lp_device structure pointer parameter is
only used to provide sector_size.  Just pass that instead.

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
Closes #16 - "invalid argument for seek()" error on very small (<=40KiB)
             drives
2019-07-18 15:27:43 +00:00
Mike Fleetwood 228374fe50 Switch to POSIX open() in detect_filesystem_internal() (!46)(#16)
For every partitioned device that GParted scans it triggers a set of
udev change events from it's internal file system signature detection
function.  This is the sequence of events:

  gparted    |set_devices_thread(pdevices)  pdevices->["/dev/sdb"]
  gparted    |  ped_device_get("/dev/sdb")
  libparted  |      open("/dev/sdb", O_RDONLY) = 8
  libparted  |      close(8)
  gparted    |  useable_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |    open("/dev/sdb", O_RDONLY|O_NONBLOCK) = 8
  gparted    |    read(8, ...)
  gparted    |    close(8)
  gparted    |  set_device_from_disk(device, device_path="/dev/sdb")
  gparted    |    get_device(device_path="/dev/sdb", ..., flush=true)
  gparted    |      ped_device_get()
  gparted    |      flush_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |        ped_device_open()
  libparted  |          open("/dev/sdb", O_RDWR) = 8
  gparted    |        ped_device_sync()
  gparted    |        ped_device_close()
  libparted  |          close(8)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |        settle_device()
  gparted    |          Utils::execute_command("udevadm settle --timeout=1")
  gparted    |    detect_filesystem(lp_device, NULL, ...)  lp_device->path="/dev/sdb"
  gparted    |      detect_filesystem_internal(lp_device, NULL)  lp_device->path="/dev/sdb"
  gparted    |        ped_device_open()
  libparted  |          open("/dev/sdb", O_RDWR) = 8
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_close()
  libparted  |          close(8)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |    get_disk(lp_device, lp_disk, strict=false)
  gparted    |      ped_disk_new(lp_device)  lp_device->path="/dev/sdb"
  libparted  |        open("/dev/sdb", O_RDWR) = 8
  libparted  |        close(8)
  udev(async)|          KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|          KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|          KERNEL change /devices/.../sdb (block)
  udev(async)|          KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|          KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|          UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|          UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|          UDEV   change /devices/.../sdb (block)
  udev(async)|          UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|          UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |      settle_device()
  gparted    |        Utils::execute_command("udevadm settle --timeout=1")
  gparted    |    set_device_partitions()
  gparted    |      detect_filesystem()
  gparted    |      set_partition_label_and_uuid(partition)  partition.get_path()="/dev/sdb1"
  gparted    |        read_label()
  gparted    |          ext2::read_label()
  gparted    |            Utils::execute_command("e2label /dev/sdb1")

Note that the udev block device change events triggered in
detect_filesystem_internal() occur before the waited for ones in the
second commit "Wait for udev to recreate /dev/PTN entries when querying
partition FSs (!46)" in get_disk() so these were already waited for.

The call chain is:
    set_devices_thread()
        set_device_from_disk()
            detect_filesystem()
                detect_filesystem_internal()
                    ped_device_open()
                    ped_device_read()
                    ped_device_close()

This occurs because file system detection is performed on whole disk
devices, but the device contains a partition table, so blkid won't
report any content GParted accepts as a file system, so it tries it's
own internal detection.

Fix this by changing detect_filesystem_internal() to use POSIX open(),
read() and close() so the file can be opened read-only and udev change
events aren't triggered.

Note that when using detect_filesystem_internal() on a partition this
also changes the device it reads from.  Before it used libparted which
always reads from the whole disk device, now it reads from the partition
device.  This doesn't matter because GParted ensures the data is
consistent between them [2] and blkid reads from each partition device
which GParted already uses.

As the new code avoids using the libparted API, and just skips to the
next signature on lseek() and read() errors, it therefore won't generate
libparted exceptions such as this when scanning very small drives:
    Invalid argument during seek for read on /dev/PTN

[1] f8faee6377
    Avoid whole disk FAT being detected as MSDOS partition table
    (#743181)

[2] 3bea067596
    Flush devices when scanning to prevent reading stale signatures
    (#723842)

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
Closes #16 - "invalid argument for seek()" error on very small (<=40KiB)
             drives
2019-07-18 15:27:43 +00:00
Mike Fleetwood 7289d4c52a Pass unmodified parameter to useable_device() as const (!46)
GParted_Core::useable_device() doesn't change the pointed to PedDevice
structure.  Pass it as const so the compiler enforces this.

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
2019-07-18 15:27:43 +00:00
Mike Fleetwood e5898e5813 Switch to POSIX open() in useable_device() (!46)
For every device that GParted scans, it determines if it can be used by
attempting to read the first sector.  This is the sequence of events,
as reported in the previous two commit messages:

  gparted    |set_devices_thread(pdevices)  pdevices->["/dev/sdb"]
  ...
  gparted    |  useable_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |    ped_device_open()
  libparted  |      open("/dev/sdb", O_RDWR) = 8
  gparted    |    ped_device_read()
  gparted    |    ped_device_close()
  libparted  |      close(8)
  udev(async)|        KERNEL change /devices/.../sdb (block)
  ...
  udev(async)|        UDEV   change /devices/.../sdb (block)

Note that these udev block device change events occur before the ones
waited for in the first commit "Wait for udev change on /dev/DISK when
querying whole device FS (!46)" so these were already waited for.

So because libparted only opens devices read-write this triggers a set
of udev change events for the whole block device, and if the device is
partitioned, also remove and re-add events for all the partitions.

Switch to using POSIX open(), read() and close() calls so read-only
access can be specified to avoid the unnecessary udev change events.

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
2019-07-18 15:27:43 +00:00
Mike Fleetwood f170a1e542 Wait for udev to recreate /dev/PTN entries when querying partition FSs (!46)
After the previous fix, on my CentOS 7 VM, GParted is now often
reporting a warning that the /dev/PTN entry is missing when reading the
label of the file system in the first partition.  This happens
regardless of the file system type.

Example error for EXT4:
    e2label: No such file or directory while trying to open /dev/sdb1
    Couldn't find valid file system superblock.

Example error for XFS:
    /dev/sdb1: No such file or directory
    fatal error -- couldn't initialize XFS library

As with the case in the previous commit, GParted gets the label via
blkid instead.

Again with debugging and sleeping in GParted, stracing the GParted
thread GParted_Core::set_devices_thread() and using 'udevadm monitor' to
watch udev events the following sequence of events is observed:

  gparted    |set_devices_thread(pdevices)  pdevices->["/dev/sdb"]
  gparted    |  ped_device_get("/dev/sdb")
  libparted  |      open("/dev/sdb", O_RDONLY) = 8
  libparted  |      close(8)
  gparted    |  useable_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |    ped_device_open()
  libparted  |      open("/dev/sdb", O_RDWR) = 8
  gparted    |    ped_device_read()
  gparted    |    ped_device_close()
  libparted  |      close(8)
  udev(async)|        KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|        KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|        KERNEL change /devices/.../sdb (block)
  udev(async)|        KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|        KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|        UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|        UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|        UDEV   change /devices/.../sdb (block)
  udev(async)|        UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|        UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |  set_device_from_disk(device, device_path="/dev/sdb")
  gparted    |    get_device(device_path="/dev/sdb", ..., flush=true)
  gparted    |      ped_device_get()
  gparted    |      flush_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |        ped_device_open()
  libparted  |          open("/dev/sdb", O_RDWR) = 8
  gparted    |        ped_device_sync()
  gparted    |        ped_device_close()
  libparted  |          close(8)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |        settle_device()
  gparted    |          Utils::execute_command("udevadm settle --timeout=1")
  gparted    |    detect_filesystem(lp_device, NULL, ...)  lp_device->path="/dev/sdb"
  gparted    |      detect_filesystem_internal(lp_device, NULL)  lp_device->path="/dev/sdb"
  gparted    |        ped_device_open()
  libparted  |          open("/dev/sdb", O_RDWR) = 8
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_read()
  gparted    |        ped_device_close()
  libparted  |          close(8)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|            KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|            UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |    get_disk(lp_device, lp_disk, strict=false)
  gparted    |      ped_disk_new(lp_device)  lp_device->path="/dev/sdb"
  libparted  |        open("/dev/sdb", O_RDWR) = 8
  libparted  |        close(8)
  udev(async)|          KERNEL remove /devices/.../sdb/sdb1 (block)
  udev(async)|          KERNEL remove /devices/.../sdb/sdb2 (block)
  udev(async)|          KERNEL change /devices/.../sdb (block)
  udev(async)|          KERNEL add    /devices/.../sdb/sdb1 (block)
  udev(async)|          KERNEL add    /devices/.../sdb/sdb2 (block)
  udev(async)|          UDEV   remove /devices/.../sdb/sdb1 (block)
  udev(async)|          UDEV   remove /devices/.../sdb/sdb2 (block)
  udev(async)|          UDEV   change /devices/.../sdb (block)
  udev(async)|          UDEV   add    /devices/.../sdb/sdb1 (block)
  udev(async)|          UDEV   add    /devices/.../sdb/sdb2 (block)
  gparted    |    set_device_partitions()
  gparted    |      detect_filesystem()
  gparted    |      set_partition_label_and_uuid(partition)  partition.get_path()="/dev/sdb1"
  gparted    |        read_label()
  gparted    |          ext2::read_label()
  gparted    |            Utils::execute_command("e2label /dev/sdb1")

So in this case for a partitioned drive, libparted ped_disk_new() is
opening and closing the device read-write and triggering the udev remove
and add partition block special entries exactly when the label is trying
to be read from the first partition, causing the device missing error.
The call chain is:
    set_devices_thread()
        set_device_from_disk()
            get_disk()
                ped_disk_new()

Looking at the source for ped_disk_new() [1] it is calling
ped_device_open() and ped_device_close(), hence why it is opening the
device read-write and triggering the udev events.

Looking back at commit "Wait for udev to recreate /dev/PTN entries when
calibrating (#762941)" [2] and re-testing it, the udev events were also
caused by ped_disk_new() with this call chain:
    apply_operation_to_disk()
        calibrate_partition()
            get_disk()
                ped_disk_new()

Fix this probe case and keep the fix for the previous calibrate case by
moving the waiting for udev events from calibrate_partition() into
get_disk(), right after ped_disk_new().  The maximum wait time is
shortened to the shorter 1 second probing timeout to avoid the longer
10 second apply timeout in this probing case.  The calibrate case commit
message said "The longest I have seen udev take to do this is 0.80
seconds in a VM".

[1] parted/libparted/disk.c:ped_disk_new()
    http://git.savannah.gnu.org/cgit/parted.git/tree/libparted/disk.c?id=v3.2#n169

[2] fd9013d5f6
    Wait for udev to recreate /dev/PTN entries when calibrating
    (#762941)

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
2019-07-18 15:27:43 +00:00
Mike Fleetwood 1382e0b828 Wait for udev change on /dev/DISK when querying whole device FS (!46)
GParted nearly always shows this warning against a whole disk device
FAT32 file system on my development VMs:
    plain floppy: device "/dev/sdb" busy (Resource temporarily
    unavailable): Cannot initialize '::'
    mlabel: Cannot initialize drive
However GParted does get the label via blkid instead.  Occasionally
everything works correctly and there is no warning.

Never found a similar error for any other file system on a whole disk
device.  The timing never seems to clash.

Remember that when a writable file descriptor of a disk device is
closed, udev events are triggered which update the kernel and /dev
entries for any partition changes.  (This is in addition to coding which
has always existed in the partitioning tools, including fdisk and
parted, to inform the kernel, but not create /dev entries, of partition
changes).

With debugging and sleeping in GParted, stracing the GParted thread
GParted_Core::set_devices_thread() and using 'udevadm monitor' to watch
udev events the following sequence of events is observed:

  gparted    |set_devices_thread(pdevices)  pdevices->["/dev/sdb"]
  gparted    |  ped_device_get("/dev/sdb")
  libparted  |      open("/dev/sdb", O_RDONLY) = 8
  libparted  |      close(8)
  gparted    |  useable_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |    ped_device_open()
  libparted  |      open("/dev/sdb", O_RDWR) = 8
  gparted    |    ped_device_read()
  gparted    |    ped_device_close()
  libparted  |      close(8)
  udev(async)|        KERNEL change /devices/.../sdb (block)
  udev(async)|        KERNEL change /devices/.../sdb (block)
  udev(async)|        UDEV   change /devices/.../sdb (block)
  udev(async)|        UDEV   change /devices/.../sdb (block)
  gparted    |  set_device_from_disk(device, device_path="/dev/sdb")
  gparted    |    get_device(device_path="/dev/sdb", ..., flush=true)
  gparted    |      ped_device_get()
  gparted    |      flush_device(lp_device)  lp_device->path="/dev/sdb"
  gparted    |        ped_device_open()
  libparted  |          open("/dev/sdb", O_RDWR) = 8
  gparted    |        ped_device_sync()
  gparted    |        ped_device_close()
  libparted  |          close(8)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            KERNEL change /devices/.../sdb (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  udev(async)|            UDEV   change /devices/.../sdb (block)
  gparted    |    set_device_one_partition()
  gparted    |      set_partition_label_and_uuid()
  gparted    |        read_label()
  gparted    |          fat16::read_label()
  gparted    |            Utils::execute_command("mlabel -s :: -i /dev/sdb")

This sequence of events only shows which close()s by libparted trigger
udev events.  It does not show that processing those events are
asynchronous and overlap with GParted executing mlabel, causing the
device busy error.

As libparted's ped_device_open() doesn't offer a way to specify that a
device will only be used for reading, it always opens it read-write
[1][2].  Hence this sequence in GParted_Core::flush_device():
    ped_device_open()
    ped_device_sync()
    ped_device_close()
always triggers udev change events on the whole disk device.  Fix by
waiting for those udev events to complete.

[1] libparted documentation, PedDevice, ped_device_open()
    https://www.gnu.org/software/parted/api/group__PedDevice.html#ga7c87bd06e76553c8ff3262b5e6ca256

[2] parted/libparted/arch/linux.c:linux_open()
    http://git.savannah.gnu.org/cgit/parted.git/tree/libparted/arch/linux.c?id=v3.2#n1628

Closes !46 - Whole device FAT32 file system reports device busy warning
             from mlabel
2019-07-18 15:27:43 +00:00
Mike Fleetwood 3f15a66291 Drop use of long ago removed udevinfo
The last distribution to include the separate 'udevinfo' command was
RHEL / CentOS 5 with udev 095.  All supported distributions have much
newer versions of udev and instead have the 'udevadm' command.
2019-07-04 10:51:50 -06:00
Mike Fleetwood 5a52b44071 Switch to faster minfo and mdir to read FAT16/32 usage (#569921)
A user reported that GParted was slow to refresh on FAT32 file systems:
"can take very long, up to several minutes; can be reproduced by running
dosfsck manually".  This is because the file system was large and almost
full, and GParted performs a file system check just to to report the
file system usage.

Created a 4 GiB FAT32 file system and almost filled it with 4 KiB files,
just over 970,000 files.

    # df -k /mnt/2
    Filesystem     1K-blocks     Used Available Used% Mounted on
    /dev/sdb2        4186108 39155384    270724   94% /mnt/2
    # df -i /mnt/2
    Filesystem     Inodes IUsed IFree IUse% Mounted on
    /dev/sdb2           0     0     0     - /mnt/2
    # find /mnt/2 -type f -print | wc -l
    971059
    # find /mnt/2 -type d -print | wc -l
    1949

Testing performance of the current fsck.fat:

    # time fsck.fat -n -v /dev/sdb2 | \
    > egrep 'bytes per logical sector|bytes per cluster|sectors total|clusters$'
           512 bytes per logical sector
          4096 bytes per cluster
       8388608 sectors total
    /dev/sdb2: 973008 files, 978846/1046527 clusters

    real    0m11.552s
    user    0m2.183s
    sys     0m7.547s

Free sectors in the file system according to fsck.fat:
    (1046527 - 978846) * 4096 / 512 = 541448 sectors

Repeating this test while also using 'blktrace /dev/sdb2' and Ctrl-C
around the test in a separate terminal, reports these numbers of I/Os
being performed:
    Read requests   Read bytes
           15,563      165 MiB

Prior to this commit [1] from 0.0.9, GParted used libparted's
ped_file_system_get_resize_constraint() to report the minimum size to
which a FAT* file system can be resized.  Use this test program [2] to
performance test this method:

    # time ./fscons /dev/sdb2
    dev=/dev/sdb2
    sector_size=512
    min_size=7909522
    max_size=8388608

    real    0m2.673s
    user    0m0.070s
    sys     0m1.834s

Free sectors in the file system according to libparted
ped_file_system_get_resize_constraint():
    8388608 - 7909522 = 479086 sectors

blktrace reports these numbers of I/Os being performed:
    Read requests   Read bytes
            7,821       71 MiB

So using libparted resize constraint is a bit faster but is still
reading too much data and is really too slow.  Also when testing GParted
using this libparted method against a corrupted FAT32 file system, on
every refresh, one popup dialog is displayed for each error libparted
detects with the file system, each of which needs acknowledgement.
Example popup:

                     Libparted Error
    \DIRNAME\FILENAME.EXT is 107724k, but is has 1920
    clusters (122880k).

                                 [ Cancel ][ Ignore ]

There could be a huge number of such errors in a corrupted file system.
Not really suitable for use by GParted.

Test the performance of mtools' minfo command to report the file system
figures:

    # time minfo -i /dev/sdb2 :: | \
    > egrep 'sector size:|cluster size:|small size:|big size:|free clusters='
    sector size: 512 bytes
    cluster size: 8 sectors
    small size: 0 sectors
    big size: 8388608 sectors
    free clusters=67681

    real    0m0.013s
    user    0m0.004s
    sys     0m0.019s

Free sectors in the file system according to minfo:
    67681 * 8 = 541448 sectors

blktrace reports these numbers of I/Os being performed by minfo:
    Read requests   Read bytes
                1       16 KiB

This matches with minfo just reading information from the BPB (BIOS
Parameter Block) [3] from sector 0 and the FS Information Sector [4]
usually in sector 1.  Note that the free cluster figure reported by
minfo comes from the FS Information Sector because it only reports it
for FAT32 file systems, not for FAT16 file systems.  Scanning the File
Allocation Table (FAT) [5] to count free clusters is exactly what mdir,
without the '-f' (fast) flag, does.  Test the performance of mdir:

    # export MTOOLS_SKIP_CHECK=1
    # time mdir -i /dev/sdb2 ::/ | fgrep 'bytes free'
                            277 221 376 bytes free

    real    0m0.023s
    user    0m0.011s
    sys     0m0.023s

Free sectors in the file system according to mdir:
    277221376 / 512 = 541448 sectors

blktrace reports these number of I/Os being performed by mdir:
    Read requests   Read bytes
                5      448 KiB

So minfo and mdir together provide the needed information and are 2 to 3
orders of magnitude faster because they only read the needed BPB and FAT
data from the drive.  Use these together to read the file system usage.

[1] 61cd0ce778
    lots of stuff and cleanups, including fixing getting used/unused
    space of hfs/hfs+/fat16/fat32

[2] fscons.c
/* FILE:     fscons.c
 * SYNOPSIS: Report libparted's FS resize limits.
 * BUILD:    gcc -o fscons fscons.c -lparted -lparted-fs-resize
 */

int main(int argc, const char *argv[])
{
    PedDevice* dev = NULL;
    PedDisk* tab = NULL;
    PedPartition* ptn = NULL;
    PedFileSystem* fs = NULL;
    PedConstraint* cons = NULL;

    if (argc != 2)
    {
        fprintf(stderr, "Usage: fscons BLOCKDEV\n");
        exit(1);
    }

    dev = ped_device_get(argv[1]);
    if (dev == NULL)
    {
        fprintf(stderr, "ped_device_get(\"%s\") failed\n", argv[1]);
        exit(1);
    }
    printf("dev=%s\n", dev->path);
    printf("sector_size=%ld\n", dev->sector_size);

    tab = ped_disk_new(dev);
    if (tab == NULL)
    {
        fprintf(stderr, "ped_disk_new(dev) failed\n");
        exit(1);
    }

    ptn = ped_disk_get_partition_by_sector(tab, 0);
    if (ptn == NULL)
    {
        fprintf(stderr, "ped_disk_get_partition(tab, 0) failed\n");
        exit(1);
    }

    fs = ped_file_system_open(&ptn->geom);
    if (fs == NULL)
    {
        fprintf(stderr, "ped_file_system_open(&ptn->geom) failed\n");
        exit(1);
    }

    cons = ped_file_system_get_resize_constraint(fs);
    if (cons == NULL)
    {
        fprintf(stderr, "ped_file_system_get_resize_constraint(fs) failed\n");
        exit(1);
    }
    printf("min_size=%ld\n", cons->min_size);
    printf("max_size=%ld\n", cons->max_size);

    ped_constraint_destroy(cons);
    ped_file_system_close(fs);
    ped_disk_destroy(tab);
    ped_device_destroy(dev);

    return 0;
}

[3] Design of the FAT file system, BIOS Parameter Block
    https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#BIOS_Parameter_Block

[4] Design of the FAT file system, FS Information Sector
    https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#FS_Information_Sector

[5] Design of the FAT file system, File Allocation Table
    https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#File_Allocation_Table

Bug 569921 - dosfsck -n delays device scan
2019-07-04 10:51:50 -06:00
Curtis Gedak 73f9a6f748 Add missing window title to Help Contents dialog (!45)
When GParted is configured with `--disable-doc` the help documentation
is neither built, nor installed.

With this configuration the Help -> Contents menu displays a message
dialog indicating the "Documentation is not available".

On GNOME 3 no title is shown; however, on some other graphic toolkits
/ window managers an unspecified title is shown.

For example on GParted Live with Fluxbox the following is displayed:

                         Unnamed
                         -------

               Documentation is not available

      This build of gparted is configured without documentation.
      Documentation is available at the project web site.
      https://gparted.org

                           OK

To address the unspecified title on non-GNOME 3 graphic toolkits, add
a title that works with and without GNOME 3.

Closes !45 - Add missing window title to Help Contents dialog
2019-06-12 15:26:54 +00:00
Mike Fleetwood 1d8cbd0125 Add missing Device.h include into GParted_Core and Win_GParted
The files GParted_Core.h, GParted_Core.cc and Win_GParted.cc all use the
Device type but don't include Device.h for it's definition.  Include it.
2019-06-11 15:55:02 +00:00
Mike Fleetwood 1ed8d909fc Replace partition boundary trimming with bug error messages (#48)
All the dialogs which compose new or modified partition boundaries
create those partitions within the boundaries of the device.  Therefore
trimming the partition boundaries to device boundaries never happens.
So replace this trimming with bug error reports.

Also add bug prefixes to the other error messages in
GParted_Core::valid_partition() too.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood e3b0558f62 White space layout update in snap_to_mebibyte/cylinder() (#48)
The previous commit moved the code from GParted_Core to
Dialog_Base_Partition class without making a single formatting change to
ensure the code was guaranteed to work the same within that larger
commit.  Now reformat the code to current layout standards.  Making it a
separate commit simplifies the effort for both changes and improves
bisectability.

Additionally to be sure there were no code changes,
Dialog_Base_Partition.cc was compiled to assembler code with and without
this change applied and the resultant assembler code compared.  There
were no differences in the generated assembler code.

Start with the make generated g++ command for compiling
Dialog_Base_Partition.o; (1) remove the '-g' flag as inserted debugging
directives do differ; and (2) replace '-c -o Dialog_Base_Partition.o'
with '-S -o Dialog_Base_Partition.s' to produce assembler output instead
of object code.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood 7c94b7d920 Snap partition boundaries before dialogs update FS usage (#48)
Move snap_to_*() method calls from the point when all operations are
added to the list, to earlier when Resize/Move, Paste (into new) and
Create New dialogs are composing the new partition objects.  In
particular for the Resize/Move operation, to just before updating the
file system usage.

This change finally resolves this bug.

Because of the dialog call chains into Dialog_Base_Partition,
snap_to_alignment() must be added into:
* Dialog_Base_Partition::prepare_new_partition() for the Resize/Move and
  Paste (into new) dialogs; and
* Dialog_Partition_New::Get_New_Partition() for the Create New dialog.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood 3222c8dd1a Pass the current device down to Dialog_Base_Partition class (#48)
The current device has to be passed to the dialog constructors and then
on to the Dialog_Base_Constructor.  Note that the Dialog_Partition_New
constructor is already passed the current device, however it still needs
to pass it on to Dialog_Base_Constructor.

This is ready so that snap_to_*() methods can access the current device
when they are called from within these dialogs.

* Pass Parameter to Base Class Constructor while creating Derived class
  Object
  https://stackoverflow.com/questions/16585856/pass-parameter-to-base-class-constructor-while-creating-derived-class-object

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood 4101b0961b Update file system usage last in prepare_new_partition() (#48)
Move setting of the new_partition object file system usage to after
everything else, specifically after free_space_before and strict_start.
This is because snap_to_*() use free_space_before and strict_start and
snap_to_alignment() is going to be called before the file system usage
is updated to avoid the error in this bug report.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood 14aa9276d4 Remove unused error reporting from snap_to_*() (#48)
None of the snap_to_*() methods report any errors so remove the unused
error parameter and return value.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Mike Fleetwood 406beaaed0 Separate partition alignment from validation (#48)
PATCHSET OVERVIEW

A user had 2 adjacent partitions which were aligned to multiples of
33553920 bytes (32 MiB - 512 bytes), not MiB or cylinders.  As far as
GParted is concerned this is not aligned.  The second partition
contained closed LUKS encrypted data.  Recreate this setup with:

    # truncate -s 200G /tmp/disk.img
    # losetup -f --show /tmp/disk.img
    /dev/loop0
    # sfdisk -u S /dev/loop0 << EOF
    65535 2162655 83
    2228190 78904140 83
    EOF
    # partprobe /dev/loop0
    # echo -n badpassword | cryptsetup luksFormat /dev/loop0p2 -

When trying to move the second LUKS encrypted partition to the right by
any amount, with the default MiB alignment, GParted displays this error
dialog and fails to even queue the operation:

    Could not add this operation to the list
    A partition with used sectors (78907392) greater than its
    length (78905344) is not valid
    [                       OK                               ]

Overview of the steps involved:

1. The Resize/Move dialog composed a new partition to start a whole
   multiple of MiB after the end of the previous non-aligned partition.
   The new partition also had it's size increased to a whole multiple of
   MiB, to 78907392 sectors (38529 MiB) which was 1.59 MiB larger than
   before.  Neither the start or end of the new partition are aligned at
   this point.

2. Win_GParted::activate_resize() applied the change back to the closed
   LUKS partition object, additionally making the used sectors equal to
   the partition size.
   (To match the fact that when opened the LUKS mapping it will
   automatically fill the new larger partition size).

3. GParted_Core::snap_to_mebibyte() then aligned the partition start and
   end to whole MiB boundaries, reducing the partition size in the
   process to 78905344 (38528 MiB).

4. GParted_Core::snap_to_alignment() reported the error saying that it
   couldn't add the operation to the list because it was invalid to have
   the file system used sectors larger than the partition size.

Fix this by having the snap to alignment adjustments applied before the
dialogs update any associated file system usage.  Specifically the
Resize/Move, Paste (into new) and Create New dialogs as these are the
only ones which either create or modify partition boundaries.
Validation done by snap_to_alignment() will continue to occur at the
current point when the operation is added to the list.

THIS COMMIT

snap_to_alignment() is doing two different jobs, it is (1) optionally
adjusting the new partition boundaries for MiB or Cylinder alignment;
and (2) checking that the partition boundaries and file system usage are
valid.

Split those into two different functions (1) snap_to_alignment() and
(2) valid_partition().  For now valid_partition() still calls
snap_to_alignment() so there is no functional change with this commit.

Closes #48 - Error when moving locked LUKS-encrypted partition
2019-06-11 15:55:02 +00:00
Curtis Gedak 6c9194869c Update copyright years 2019-05-29 09:46:10 -06:00