1

I'd like to know in my C/Fortran code if the working directory is local to the host or actually remotely mounted e.g. NFS disks? Wanting to do this on both Windows and Linux.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
boreas
  • 29
  • 2
  • 1
    It is not a feature of the programming languages, but a feature of the operating system. You can run a shell command to do so, which depends on the OS. – gdlmx Jan 10 '20 at 18:48
  • @EugeneSh. [POSIX provides `[f]statvfs()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html). Comparing the file system ID of the initial directory to the file system ID of all mounted file systems should work. And the Linux-specific `statfs()` call directly provides a file system type, which may be sufficient. – Andrew Henle Jan 10 '20 at 20:50
  • @AndrewHenle You can mount NFS as well. Will this be detected? Upd: yeah, `statfs` seems to be good. – Eugene Sh. Jan 10 '20 at 20:52
  • For Linux, you can simply read `/proc/mounts` and scan for the beginning of the current directory to determine where the directory is mounted. Simply `cat /proc/mounts` in a terminal to examine the file. – David C. Rankin Jan 10 '20 at 20:54
  • @EugeneSh. [`statfs()`](http://man7.org/linux/man-pages/man2/statfs.2.html) provides `f_type`, of which `NFS_SUPER_MAGIC` might be sufficient. I haven't tested that thoroughly, though. – Andrew Henle Jan 10 '20 at 20:55
  • @DavidC.Rankin *For Linux, you can simply read `/proc/mounts` ...* Note that only works with the canonical path of the original directory. – Andrew Henle Jan 10 '20 at 20:57
  • Yes, correct, it's not a solution for everything, but if you are concerned about NFS or CIFS, FUSE, etc.. it is a quick hack. – David C. Rankin Jan 10 '20 at 21:10
  • will give a try. thanks for inputs. – boreas Jan 10 '20 at 23:38

2 Answers2

2

In C on Windows, you are probably looking for the Windows API function GetDriveType. To find the current working directory, you can use the function GetCurrentDirectory. However, according to the documentation, it is sufficient to simply call

GetDriveType( NULL );

if you want to inspect the drive of the current working directory.

I don't know what the equivalent functions for Fortran and Linux are.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
2

Linux solution

First, you need to find the file system the directory is mounted on.

Unless you want to go through path parsing, you can use the file system ID from the statfs() call:

SYNOPSIS

   #include <sys/vfs.h>    /* or <sys/statfs.h> */

   int statfs(const char *path, struct statfs *buf);
   int fstatfs(int fd, struct statfs *buf);

DESCRIPTION

The statfs() system call returns information about a mounted filesystem. path is the pathname of any file within the mounted filesystem. buf is a pointer to a statfs structure defined approximately as follows:

       struct statfs {
           __fsword_t f_type;    /* Type of filesystem (see below) */
           __fsword_t f_bsize;   /* Optimal transfer block size */
           fsblkcnt_t f_blocks;  /* Total data blocks in filesystem */
           fsblkcnt_t f_bfree;   /* Free blocks in filesystem */
           fsblkcnt_t f_bavail;  /* Free blocks available to
                                    unprivileged user */
           fsfilcnt_t f_files;   /* Total file nodes in filesystem */
           fsfilcnt_t f_ffree;   /* Free file nodes in filesystem */
           fsid_t     f_fsid;    /* Filesystem ID */
           __fsword_t f_namelen; /* Maximum length of filenames */
           __fsword_t f_frsize;  /* Fragment size (since Linux 2.6) */
           __fsword_t f_flags;   /* Mount flags of filesystem
                                    (since Linux 2.6.36) */
           __fsword_t f_spare[xxx];
                           /* Padding bytes reserved for future use */
       };

The following filesystem types may appear in f_type:

...

This may be sufficient for your purposes - the data in f_type might be sufficient to inform you that the file is on local or remote disks (As long as you're ignoring the potential to NFS-mount a local file system exported from the local machine, among other issues noted below.)

For a more portable solution, you can use the POSIX-standardized available-on-Linux statvfs() call for the file system ID given a path:

SYNOPSIS

   #include <sys/statvfs.h>

   int statvfs(const char *path, struct statvfs *buf);
   int fstatvfs(int fd, struct statvfs *buf);

DESCRIPTION

The function statvfs() returns information about a mounted filesystem. path is the pathname of any file within the mounted filesystem. buf is a pointer to a statvfs structure defined approximately as follows:

       struct statvfs {
           unsigned long  f_bsize;    /* Filesystem block size */
           unsigned long  f_frsize;   /* Fragment size */
           fsblkcnt_t     f_blocks;   /* Size of fs in f_frsize units */
           fsblkcnt_t     f_bfree;    /* Number of free blocks */
           fsblkcnt_t     f_bavail;   /* Number of free blocks for
                                         unprivileged users */
           fsfilcnt_t     f_files;    /* Number of inodes */
           fsfilcnt_t     f_ffree;    /* Number of free inodes */
           fsfilcnt_t     f_favail;   /* Number of free inodes for
                                         unprivileged users */
           unsigned long  f_fsid;     /* Filesystem ID */
           unsigned long  f_flag;     /* Mount flags */
           unsigned long  f_namemax;  /* Maximum filename length */
       };

Once you get the f_fsid value for your directory, you need to find the mount point for that filesystem.

Linux provides getmntent() et al for this purpose:

SYNOPSIS

   #include <stdio.h>
   #include <mntent.h>

   FILE *setmntent(const char *filename, const char *type);

   struct mntent *getmntent(FILE *stream);

   int addmntent(FILE *stream, const struct mntent *mnt);

   int endmntent(FILE *streamp);

   char *hasmntopt(const struct mntent *mnt, const char *opt);

   /* GNU extension */
   #include <mntent.h>

   struct mntent *getmntent_r(FILE *streamp, struct mntent *mntbuf,
                              char *buf, int buflen);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

   getmntent_r():
       Since glibc 2.19:
           _DEFAULT_SOURCE
       Glibc 2.19 and earlier:
           _BSD_SOURCE || _SVID_SOURCE

DESCRIPTION

These routines are used to access the filesystem description file /etc/fstab and the mounted filesystem description file /etc/mtab.

The setmntent() function opens the filesystem description file filename and returns a file pointer which can be used by getmntent(). The argument type is the type of access required and can take the same values as the mode argument of fopen(3). The returned stream should be closed using endmntent() rather than fclose(3).

The getmntent() function reads the next line of the filesystem description file from stream and returns a pointer to a structure containing the broken out fields from a line in the file. The pointer points to a static area of memory which is overwritten by subsequent calls to getmntent().

The addmntent() function adds the mntent structure mnt to the end of the open stream.

The endmntent() function closes the stream associated with the filesystem description file.

The hasmntopt() function scans the mnt_opts field (see below) of the mntent structure mnt for a substring that matches opt. See <mntent.h> and mount(8) for valid mount options.

The reentrant getmntent_r() function is similar to getmntent(), but stores the struct mount in the provided *mntbuf and stores the strings pointed to by the entries in that struct in the provided array buf of size buflen.

The mntent structure is defined in <mntent.h> as follows:

       struct mntent {
           char *mnt_fsname;   /* name of mounted filesystem */
           char *mnt_dir;      /* filesystem path prefix */
           char *mnt_type;     /* mount type (see mntent.h) */
           char *mnt_opts;     /* mount options (see mntent.h) */
           int   mnt_freq;     /* dump frequency in days */
           int   mnt_passno;   /* pass number on parallel fsck */
       };

See Linux function to get mount points for some code examples.

In your case, you would call statfs() or statvfs() on the path held in the mnt_dir field of the struct mntent and get the filesystem ID of that filesystem, and when it matched the filesystem ID from your directory, you'd have the mount point - and all the details for the filesystem.

You could also parse file paths and find the longest mnt_dir in the mounted filesystems returned from getmntent() that matches the canonical path of your directory. But in that case you need to handle softlinks crossing filesystem boundaries. And I'm not sure if hard links to directories are still strictly prohibited - if not, using path names can never be definitive.

Note regarding loopback devices

A loopback device is a way to mount a file as a filesystem. You'll have to decide if you treat a file or directory mounted on a loopback device as a local file, or if you trace it back to the actual file being used as the loopback file system and use that to determine if your original directory is local or remote.

Note about bind mounts

Linux provides the capability to do bind mounts, where a directory can be "mounted" at another location. This is another case where you have to decide if you need to trace back through the mounts to determine if your original directory is local or remote.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56