Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

statfs/statvfs calls in native impl. for SystemNative_GetFormatInfoForMountPoint (via [System.IO.DriveInfo]::GetDrives()) hangs when run against a disconnected network mountpoint #113098

Open
helpimnotdrowning opened this issue Mar 3, 2025 · 2 comments
Labels
area-System.IO untriaged New issue has not been triaged by the area owner

Comments

@helpimnotdrowning
Copy link

helpimnotdrowning commented Mar 3, 2025

Description

NOTE: I consistently say that call will hang "forever": I found that if you bring back the connection, the programs/PowerShell will (eventually) resolve or exit/die. This can take a while.

Running [System.IO.DriveInfo]::GetDrives() will hang on trying to determine a mount type (Fixed, Network, ...) some property of the mount (apart from the name) when, on Linux, a network mount has been disconnected (but is still mounted). This is because it eventually calls down to statfs(2)/statvfs(3), which will hang the caller (I haven't tested for how long).

The following is specifically for the DriveType property & follows along with the C# example below, but probably applies to the others as well:
In asking for drive.DriveType, the getter calls to Interop.Sys.GetFormatInfoForMountPoint

int result = Interop.Sys.GetFormatInfoForMountPoint(Name, out type);

internal static int GetFormatInfoForMountPoint(string name, out DriveType type)
{
return GetFormatInfoForMountPoint(name, out _, out type);
}

, who redirects to

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetFormatInfoForMountPoint", SetLastError = true)]
internal static unsafe partial int GetFormatInfoForMountPoint(
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
byte* formatNameBuffer,
int bufferLength,
long* formatType);

, which calls out to a native C method SystemNative_GetFormatInfoForMountPoint

int32_t
SystemNative_GetFormatInfoForMountPoint(const char* name, char* formatNameBuffer, int32_t bufferLength, int64_t* formatType)
{

This calls into either statfs(2)

int result = statfs(name, &stats);
or statvfs(3)
int result = statvfs(name, &stats);
, of which both will hang.

Reproduction Steps

Repro steps are included for PowerShell, C (at the end), and C#.

To reproduce the hanging within the runtime:

This can be reproduced with PowerShell with minimal effort, through C (for the base error), or through C#, though you need to make a project for this.

  1. Setup
  • If you want to reproduce with PowerShell, start the shell now.

If you are actively using your terminal, spawn a new instance. Once the problem is created, PowerShell cannot be killed (no Ctrl+C or kill -9!) and under certain terminals (I use KDE's Konsole) it may eventually freeze the entire window. The hung PowerShell instance can only be killed by closing the terminal window.

  • For C#, create a project and save this to a source file:
DriveInfo[] logicalDrives = DriveInfo.GetDrives();

foreach (DriveInfo drive in logicalDrives) {
    Console.WriteLine("Type for " + drive.Name + ":");
    Console.WriteLine("\t" + drive.DriveType); // can also be done with other properties!!
    Console.WriteLine("");
}

Build the executable.

When running, if you are actively using your terminal, spawn a new instance. Once the problem is created, the program cannot be killed (no Ctrl+C or kill -9!), but can be stopped by closing your terminal/terminal tab (at least on KDE's Konsole).

  1. Mount a filesystem over the network. I used sshfs:
sshfs <server>:/ <mountpoint>

For PowerShell, run the following code:

[io.driveinfo]::getdrives()

Everything will list normally. For me, my mounted drive is always at the end, so it looks like this:

...
Name               : /run/user/1000
IsReady            : True
RootDirectory      : /run/user/1000
VolumeLabel        : /run/user/1000
DriveType          : Ram
DriveFormat        : udev
AvailableFreeSpace : 1604616192
TotalFreeSpace     : 1604616192
TotalSize          : 1604755456

Name               : /run/user/1000/doc
IsReady            : True
RootDirectory      : /run/user/1000/doc
VolumeLabel        : /run/user/1000/doc
DriveType          : Unknown
DriveFormat        : 
AvailableFreeSpace : 
TotalFreeSpace     : 
TotalSize          : 

Name               : /mnt/URUHA
IsReady            : True
RootDirectory      : /mnt/URUHA
VolumeLabel        : /mnt/URUHA
DriveType          : Network
DriveFormat        : fuse
AvailableFreeSpace : 363964641280
TotalFreeSpace     : 413913505792
TotalSize          : 981813280768

For C#, run the executable. Everything will list normally. For me, my mounted drive is always at the end, so it looks like this:

...
Type for /proc/sys/fs/binfmt_misc:
        Ram

Type for /run/user/1000:
        Ram

Type for /run/user/1000/doc:
        Unknown

Type for /mnt/URUHA:
        Network
  1. Turn off your network connection (airplane mode, rfkill, physical unplug, etc...).

  2. Run the code again. Both will freeze while writing the details of the mountpoint.

For PowerShell,

...
Name               : /run/user/1000
IsReady            : True
RootDirectory      : /run/user/1000
VolumeLabel        : /run/user/1000
DriveType          : Ram
DriveFormat        : udev
AvailableFreeSpace : 1604632576
TotalFreeSpace     : 1604632576
TotalSize          : 1604755456

Name               : /run/user/1000/doc
IsReady            : True
RootDirectory      : /run/user/1000/doc
VolumeLabel        : /run/user/1000/doc
DriveType          : Unknown
DriveFormat        : 
AvailableFreeSpace : 
TotalFreeSpace     : 
TotalSize          : 

, and C#,

...
Type for /run/user/1000:
        Ram

Type for /run/user/1000/doc:
        Unknown

Type for /mnt/URUHA:

To reproduce the pure hanging in C

When running the program, if you are actively using your terminal, spawn a new instance. Once the problem is created, the program cannot be killed (no Ctrl+C or kill -9!), but can be stopped by closing your terminal/terminal tab (at least on KDE's Konsole).

  1. Save the following code somewhere
#include <stdio.h>
#include <string.h>

#include <sys/statfs.h>
#include <sys/statvfs.h>

int main(int argc, char* argv[]) {
	if (argc != 3) {
		printf("usage: %s <fs|vfs> <mountpoint>\n", argv[0]);
		return 1;
	}
	
	int result;

	if ( strcmp(argv[1], "fs") == 0 ) {
		struct statfs stats;
		result = statfs(argv[2], &stats);
	} else if ( strcmp(argv[1], "vfs") == 0 ) {
		struct statvfs stats;
		result = statvfs(argv[2], &stats);	
	} else {
		printf("invalid argv[1]: %s", argv[1]);
		return 1;
	}

	printf("Result (%s): %d\n", argv[1], result);
}
  1. Compile: gcc <filename>.c
  2. Mount a filesystem over the network. I used sshfs:
sshfs <server>:/ <mountpoint>
  1. Run the program with a.out <fs or vfs, run with each> <mountpoint>
    It will output something like
Result (fs): 0

or

Result (vfs): 0

where the result is 0 for success and -1 for an error (this will not indicate the hanging)
5. Turn off your network connection (airplane mode, rfkill, physical unplug, etc...).
6. Run the program again. It will freeze and not output anything.

Or

You can also just run stat <mountpoint> in a shell while it's disconnected.

Expected behavior

[System.IO.DriveInfo]::GetDrives() should not hang forever when the mountpoint it is scanning is disconnected. Maybe it could time out after some time?

Actual behavior

[System.IO.DriveInfo]::GetDrives() hangs forever.

Regression?

No idea

Known Workarounds

No idea

Configuration

  • .NET
    • C# example: 9.0.200 (I believe)
    • PowerShell example: ???
  • OS: Linux/Debian 12
  • Arch: amd64
  • Likely Linux-specific (maybe on macOS as well?)

Other information

(mentioned in description)

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Mar 3, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 3, 2025
@helpimnotdrowning helpimnotdrowning changed the title statvfs call in src/natives/libs/System.Native/pal_mount.c (from [System.IO.DriveInfo]::GetDrives()) hangs when run against a disconnected network mountpoint statfs/statvfs calls in native impl. for [System.IO.DriveInfo]::GetDrives() hangs when run against a disconnected network mountpoint Mar 3, 2025
@jkotas jkotas added area-System.IO and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Mar 3, 2025
Copy link
Contributor

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

@helpimnotdrowning helpimnotdrowning changed the title statfs/statvfs calls in native impl. for [System.IO.DriveInfo]::GetDrives() hangs when run against a disconnected network mountpoint statfs/statvfs calls in native impl. for SystemNative_GetFormatInfoForMountPoint (via [System.IO.DriveInfo]::GetDrives()) hangs when run against a disconnected network mountpoint Mar 4, 2025
@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Mar 4, 2025

For sshfs, there is some advice at SSHFS hangs after the connection was interrupted.

For NFS, see "soft / hard" and "intr / nointr" in nfs(5).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.IO untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

3 participants