查找与文件名关联的目录的文件描述符

Find file descriptor of directory associated with filename

提问人:programme3219873 提问时间:7/14/2022 最后编辑:Jonathan Lefflerprogramme3219873 更新时间:7/14/2022 访问量:216

问:

我正在尝试编写一个函数,允许用户以纳秒级精度更改文件时间戳。经过一番研究,我找到了允许我这样做的函数 utimensat()。但是,问题在于该函数采用一个参数,该参数是文件所在目录的文件描述符。int dirfd

使用文件名是否可以以某种方式找到文件所在目录的文件描述符?

例如,如果一个文件位于 ,我如何获取该目录的文件描述符?又名目录。test.txt/home/user/files/test.txttest.txtfiles

C -IO 文件 描述符

评论

0赞 Jonathan Leffler 8/14/2022
如果您仍然需要有关现已删除的帮助,请与我联系(请参阅我的个人资料) 如何将字符串格式的 Linux 权限更改为 mode_t 格式? 问题。阅读本文后,请标记此评论“不再需要”。

答:

4赞 Jonathan Leffler 7/14/2022 #1

有多种方法可以达到预期的结果,最简单的方法根本不涉及打开目录。

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);
}

这使用幻数,这意味着函数的文件名参数被解释为“正常”。对于作为文件名的命令行参数,建议使用此选项。AT_FDCWD

如果您正在使用 opendir()、readdir()、closedir() 从 <dirent.h> 处理目录,则可以使用 dirfd() 获取与当前目录结构关联的目录文件描述符。 您可以使用它来设置文件修改时间。DIR *

如果你仍然想打开目录,那么你可以使用 dirname() 来查找给定文件名的目录名(你也可以使用 basename() 来查找目录中的文件名)。小心;根据 POSIX 的说法,实现有很多可能性。通常,在将重复的字符串传递给 之前,应创建文件名的副本。您可以在调用字符串之前调用字符串。然后,您可以使用 open() 打开仅供读取的目录——可能使用(未广泛使用)或用作打开模式。不要忘记在完成后关闭目录,也不要释放您使用的文件名的副本。dirname()basename()dirname()O_SEARCHO_RDONLY

下面是一个程序的源代码,它将文件上的时间戳设置为所选时间——默认为当前时间。ft97.cft97

#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;
}

您可以在 GitHub 上的 SOQ(堆栈溢出问题)存储库中找到非标准标头文件和相应的源代码,作为文件 、 、 和 src/libsoq 子目录。stderr.cstderr.hposixver.htimespec_io.ctimespec_io.h