dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.37k stars 4.75k forks source link

DriveInfo.DriveFormat property reports ext3 format on ext4 formatted filesystems (Unix) #95099

Open alphons opened 12 months ago

alphons commented 12 months ago

Description

using System;
namespace GetFileSystemEntries
{
    class Class1
    {
        static void Main(string[] args)
        {
            foreach (var drive in DriveInfo.GetDrives())
                Console.WriteLine(drive.Name + "\t\t" + drive.DriveFormat);
        }
    }
}
vmt@kvm:~/a$ dotnet run
/mnt/nvme1n1p1     ext3
/mnt/nvme0n1p1     ext3
/mnt/sda2          ext3
/mnt/sdb1          ext3
/mnt/sdc1          ext3
/mnt/loop/joe-4.6  squashfs

Using unix df tool for comparison (same system)

vmt@kvm:~/a$ df -T
/dev/nvme1n1p1     ext4
/dev/nvme0n1p1     ext4
/dev/sda2          ext4
/dev/sdb1          ext4
/dev/sdc1          ext4
/dev/loop0         squashfs

Configuration

vmt@kvm:~/a$ dotnet --info .NET SDK: Version: 8.0.100 Commit: 57efcf1350 Workload version: 8.0.100-manifests.6c33ef20

Runtime Environment: OS Name: vmtux OS Version: 2.0 OS Platform: Linux RID: linux-x64 Base Path: /mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/sdk/8.0.100/

.NET workloads installed: Workload version: 8.0.100-manifests.6c33ef20 There are no installed workloads to display.

Host: Version: 8.0.0 Architecture: x64 Commit: 5535e31a71

.NET SDKs installed: 8.0.100 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 8.0.0 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 8.0.0 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

rzikm commented 12 months ago

@dotnet/area-system-io Can you please take a look?

ghost commented 12 months ago

Tagging subscribers to this area: @dotnet/area-system-io See info in area-owners.md if you want to be subscribed.

Issue Details
### Description ```C# using System; namespace GetFileSystemEntries { class Class1 { static void Main(string[] args) { foreach (var drive in DriveInfo.GetDrives()) Console.WriteLine(drive.Name + "\t\t" + drive.DriveFormat); } } } ``` ``` vmt@kvm:~/a$ dotnet run /mnt/nvme1n1p1 ext3 /mnt/nvme0n1p1 ext3 /mnt/sda2 ext3 /mnt/sdb1 ext3 /mnt/sdc1 ext3 /mnt/loop/joe-4.6 squashfs ``` Using unix df tool for comparison (same system) ``` vmt@kvm:~/a$ df -T /dev/nvme1n1p1 ext4 /dev/nvme0n1p1 ext4 /dev/sda2 ext4 /dev/sdb1 ext4 /dev/sdc1 ext4 /dev/loop0 squashfs ``` ### Configuration vmt@kvm:~/a$ dotnet --info .NET SDK: Version: 8.0.100 Commit: 57efcf1350 Workload version: 8.0.100-manifests.6c33ef20 Runtime Environment: OS Name: vmtux OS Version: 2.0 OS Platform: Linux RID: linux-x64 Base Path: /mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/sdk/8.0.100/ .NET workloads installed: Workload version: 8.0.100-manifests.6c33ef20 There are no installed workloads to display. Host: Version: 8.0.0 Architecture: x64 Commit: 5535e31a71 .NET SDKs installed: 8.0.100 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/sdk] .NET runtimes installed: Microsoft.AspNetCore.App 8.0.0 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 8.0.0 [/mnt/loop/dotnet-sdk-8.0.100/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
Author: alphons
Assignees: -
Labels: `area-System.IO`, `untriaged`, `needs-area-label`
Milestone: -
jozkee commented 12 months ago

DriveFormat uses statfs and it returns the same value for ext2, ext3 and ext4. https://github.com/dotnet/runtime/blob/52b4c824710b6bcf2377ebd986fdd01195b8cb1f/src/libraries/Common/src/Interop/Unix/System.Native/Interop.UnixFileSystemTypes.cs#L57-L59.

adamsitnik commented 12 months ago

The magic numbers come from the official Linux man page for statfs: https://man7.org/linux/man-pages/man2/statfs.2.html

image

But there are other ways of getting the file system name, we currently use them as a fallback:

https://github.com/dotnet/runtime/blob/e440ebc55895c80fced3410b8018dee79eb2ce4a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MountPoints.FormatInfo.cs#L60-L63

We could experiment with what we have in https://github.com/dotnet/runtime/blob/e440ebc55895c80fced3410b8018dee79eb2ce4a/src/native/libs/System.Native/pal_mount.c#L162

and take some lessons from https://github.com/dotnet/runtime/blob/e440ebc55895c80fced3410b8018dee79eb2ce4a/src/native/libs/System.Native/pal_io.c#L1703

and try to use the stats.f_fstypename for enums with duplicate values.

BTW .NET is not the only tool with the same issue: https://unix.stackexchange.com/a/274030

I am going to re-open it and apply help-wanted label.

adamsitnik commented 12 months ago

cc @am11 @tmds who helped us with similar issues in the past

am11 commented 12 months ago

Per https://stackoverflow.com/a/17503494, filesystem magics are unreliable. To identify the exact filesystem version of conflicting magic numbers, we would need to read something called "super block" and follow what blkid(8) does in its probing: https://github.com/util-linux/util-linux/blob/61c40f484563881420e1606a27663aee930cbe53/libblkid/src/superblocks/ext.c#L264.

tmds commented 7 months ago

/proc/self/mountinfo contains a field for the filesystem type of the mounts. We can use that for DriveFormat.

We can also change GetDrives so uses this file to get the drives, and immediately stores the format in the DriveInfo instance.

MojtabaTajik commented 7 months ago

@adamsitnik @tmds Can I pick this ticket up?

tmds commented 7 months ago

@MojtabaTajik sure. For parsing the file from proc, you can probably use the StringParser struct to minimize allocations.

tmds commented 7 months ago

I proposed to use /proc/self/mountinfo in an earlier comment since it will include all mounts for the current process.

This is different from /etc/mtab which is a file that has the mounted filesystems made through the mount command.

tmds commented 7 months ago

I proposed to use /proc/self/mountinfo in an earlier comment since it will include all mounts for the current process.

The paths in this file also take into account the process's root directory.

alphons commented 2 months ago

I proposed to use /proc/self/mountinfo in an earlier comment since it will include all mounts for the current process.

The paths in this file also take into account the process's root directory.

Ow wow, you are absolutely right. Did not know the (real) difference between /proc/self/mounts and /proc/self/mountinfo This effect can easily be seen on chrooted environments.

(chroot) vmt@kvmbig:/proc/self$ cat mounts

rootfs /dev rootfs rw,size=29526768k,nr_inodes=10881052 0 0
devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
tmpfs /dev/shm tmpfs rw,nosuid,nodev,relatime 0 0
proc /proc proc rw,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0
tmpfs /run tmpfs rw,relatime 0 0
/dev/nvme0n1p1 /home/vmt/vmtux ext4 rw,relatime 0 0

(chroot) vmt@kvmbig:/proc/self$ cat mountinfo

118 22 0:2 /dev /dev rw - rootfs rootfs rw,size=29526768k,nr_inodes=10881052
119 118 0:19 / /dev/pts rw,relatime - devpts devpts rw,mode=600,ptmxmode=000
120 118 0:21 / /dev/shm rw,nosuid,nodev,relatime - tmpfs tmpfs rw
121 22 0:22 / /proc rw,relatime - proc proc rw
122 22 0:18 / /sys rw,relatime - sysfs sysfs rw
123 22 0:23 / /run rw,relatime - tmpfs tmpfs rw
124 22 259:2 /git/vmtux/src /home/vmt/vmtux rw,relatime - ext4 /dev/nvme0n1p1 rw

The latter (mountinfo) is the right one to use.