Question

I am writing a function that recursively deletes all files and sub-directories in a directory tree, and this function will be used in multithread environment, so I would prefer opendir/readdir_r than nftw (the code is for Linux/Mac OSX/Solaris, while nftw is not thread-safe on some platform).

Since the function is deleting files, security is a great concern. If there's a link pointing to a sensitive location (e.g., the /usr/lib system directory), I don't want my function to try to delete the files under that directory. For symbolic/hard link files, lstat then S_ISLNK will do the job. However, if there's a mount point, S_ISDIR just returns true on it.

Maybe setmntent/getmntent would help, but my experiment on Linux found it can't handle following situation:

  1. mount //192.168.0.1/share at ~/work/share (need root privilege)
  2. mv ~/work /tmp/work (does not need root privilege)
  3. now getmntent still reports ~/work/share as the mount point

What I want is like the FTW_MOUNT flag to nftw:

man nftw:
...

    FTW_MOUNT
                  If set, stay within the same file system.

I am not sure if the st_dev field from struct stat is good for this, I don't know if the dev numbers are always different beyond a mount point.

with readdir_r is there a way to figure out mounted points?

Thank you!

Was it helpful?

Solution

From the Single Unix Specification - Issue 7:

3.228 Mount Point

Either the system root directory or a directory for which the st_dev field of structure stat differs from that of its parent directory.

Note: The stat structure is defined in detail in <sys/stat.h>.

In other words, yes, you can rely upon the device ID to determine whether you're at a mount point or not. The key to understand what a mount point is involves understanding that if something like /usr resides on the same file system as /, you will never type mount device /usr.

A simple example where /home, /tmp, /usr, and /usr/src are all on different devices:

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include <sys/stat.h>

int main(int argc, char *argv[])
{
    struct stat stbuf;

    if (stat(".", &stbuf) == -1) {
        perror("couldn't get working directory");
        return 1;
    }

    printf("Device ID for directory .: %lX\n", stbuf.st_dev);

    /* Loop through the command line arguments. */
    while (*++argv) {
        if (stat(*argv, &stbuf) == -1) {
            fprintf(stderr, "error: couldn't get device ID for directory '%s': %s\n", *argv, strerror(error));
            continue;
        }

        printf("Device ID for directory %s: %lX\n", *argv, stbuf.st_dev);
    }

    return 0;
}

Sample run:

sh$ ./a.out /usr ~/misc\ files /nonexistent/path /usr/src /tmp
Device ID for directory .: 807
Device ID for directory /usr: 803
Device ID for directory /home/kit/misc files: 807
error: couldn't get device ID for directory '/nonexistent/path': No such file or directory
Device ID for directory /usr/src: 805
Device ID for directory /tmp: 802
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top