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:
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!
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