Home > Back-end >  Find file descriptor of directory associated with filename
Find file descriptor of directory associated with filename

Time:07-14

I am trying to write a function that allows users to change file timestamps with nanosecond precision. After some research, I found the function utimensat() which allows me to do this. However the problem is that the function takes an argument int dirfd which is a file descriptor of the directory the file resides in.

Using a filename would it be possible to somehow find the file descriptor of the directory a file is in?

For example if a file test.txt is located in /home/user/files/test.txt, how could I get the file descriptor of the directory test.txt is in? aka the files directory.

CodePudding user response:

There are multiple ways to achieve the desired result, and the simplest one doesn't involve opening a directory at all.

static void set_time(const char *file, struct timespec *tvals)
{
    if (utimensat(AT_FDCWD, file, tvals, 0) != 0)
        err_sysrem("failed to set time on %s: ", file);
}

This uses the magic number AT_FDCWD which means that the file name argument to the function is interpreted 'as normal'. This is the recommended option for command line arguments that are file names.

If you are processing a directory with opendir(), readdir(), closedir() from <dirent.h>, then you can use dirfd() to get the directory file descriptor associated with the DIR * structure for the current directory. You can use that to set the file modification times.

If you still want to open the directory, then you can use dirname() to find the name of the directory for a given file name (and you may also use basename() to find the name of the file within the directory). Be careful; there are a lot of possibilities for what the implementation does according to POSIX. You should usually create a duplicate of the file name before passing the duplicate string to dirname(). You can call basename() on a string before calling dirname() on the string. You can then use open() to open the directory for reading only — possibly using O_SEARCH (not widely available) or using O_RDONLY as the open mode. Don't forget to close the directory after you're finished with it, nor to free the copy of the filename that you used.

Here's the source code ft97.c for a program ft97 which will set the time stamps on a file to the chosen time – defaulting to the current time.

#include "posixver.h"
#include <fcntl.h>           /* AT_FDCWD on macOS Monterey 12.4 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "stderr.h"
#include "timespec_io.h"

static char optstr[] = "a:hm:t:V";
static char usestr[] = "[-hV] [-a atime] [-m mtime] [-t time] file ...";
static char hlpstr[] =
    "  -a atime  Use this time for the access time (default: now)\n"
    "  -h        Print this help message and exit\n"
    "  -m mtime  Use this time for the modification time (default: now)\n"
    "  -t time   Use this time value for both access and modification time\n"
    "  -V        Print version information and exit\n"
    ;

static void set_time(const char *file, struct timespec *tvals)
{
    if (utimensat(AT_FDCWD, file, tvals, 0) != 0)
        err_sysrem("failed to set time on %s: ", file);
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);

    struct timespec tvals[2];
    clock_gettime(CLOCK_REALTIME, &tvals[0]);
    tvals[1] = tvals[0];
    int opt;

    while ((opt = getopt(argc, argv, optstr)) != -1)
    {
        switch (opt)
        {
        case 'a':
            if (scn_timespec(optarg, &tvals[0]) != 0)
                err_error("failed to parse '%s' as a time\n", optarg);
            break;
        case 'h':
            err_help(usestr, hlpstr);
        /*NOTREACHED*/
        case 'm':
            if (scn_timespec(optarg, &tvals[1]) != 0)
                err_error("failed to parse '%s' as a time\n", optarg);
            break;
        case 'V':
            err_version("FT97", &"@(#)$Revision$ ($Date$)"[4]);
        /*NOTREACHED*/
        case 't':
            if (scn_timespec(optarg, &tvals[0]) != 0)
                err_error("failed to parse '%s' as a time\n", optarg);
            tvals[1] = tvals[0];
            break;
        default:
            err_usage(usestr);
            /*NOTREACHED*/
        }
    }

    if (argc == optind)
        err_usage(usestr);

    for (int i = optind; i < argc; i  )
        set_time(argv[i], tvals);

    return 0;
}

You can find the non-standard headers and corresponding source code in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c, stderr.h, posixver.h, timespec_io.c and timespec_io.h in the src/libsoq sub-directory.

CodePudding user response:

To get a file descriptor to a directory in c, simply open the "." file of the directory and get the file descriptor from that, as you would a normal file.

Here's an example

#include <stdio.h>

int main(int argc, char** argv) {   
    FILE* file = fopen("/path/to/directory/of/file/.", "r");
    int fd = fileno(file);
    
    //Do what you want with the file descriptor

    return 0;       
}

Note that the function fileno only works in POSIX compliant systems. Thankfully, it appears that you are considering the forward slashes in your file path.

Hope this helps!

  • Related