如何根据文件名获取目标文件所在目录的文件描述符
解答
完全可以通过标准POSIX接口拿到目标文件所在目录的文件描述符,不需要特殊内核接口,具体实现逻辑和注意点如下:
核心实现步骤
针对你举的/home/user/files/test.txt例子,要拿到files目录的fd,流程很直接:
- 先把完整路径拆成两部分:父目录路径
/home/user/files、目标文件名test.txt - 调用
open()打开父目录路径,打开标志必须带O_RDONLY | O_DIRECTORY,其中O_DIRECTORY用来保证打开的目标一定是目录,避免路径被篡改指向普通文件带来安全问题,返回的文件描述符就是utimensat()需要的dirfd参数 - 调用
utimensat()时,dirfd传刚才拿到的目录fd,pathname传拆分出的目标文件名即可 - 操作完成后调用
close()释放目录fd,避免文件描述符泄漏
路径拆分不要自己写字符串切割逻辑,容易被多斜杠、末尾斜杠、相对路径等边界情况搞出bug,直接用POSIX标准的
dirname()和basename()函数即可,注意这两个函数会修改传入的字符串缓冲区,使用前一定要提前拷贝原路径字符串。
更简洁的替代方案
如果你只是单次修改文件时间戳,完全不需要手动拆分路径、打开目录:直接给utimensat()的dirfd参数传特殊值AT_FDCWD,pathname参数传完整的文件路径(不管是绝对路径还是相对路径都可以)即可,纳秒级时间戳的精度不会受任何影响,这个写法是POSIX标准规定的,全平台兼容,代码量少还不容易出错。
只有当你需要反复操作同一个目录下的大量文件时,才需要提前打开目录拿fd复用,减少重复路径解析的开销。
参考示例代码
#include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> #include <libgen.h> #include <string.h> #include <limits.h> // 手动获取目录fd的实现 int set_file_ts_ns(const char *full_path, struct timespec ts[2]) { char dir_buf[PATH_MAX] = {0}; char base_buf[PATH_MAX] = {0}; strncpy(dir_buf, full_path, PATH_MAX - 1); strncpy(base_buf, full_path, PATH_MAX - 1); char *dir_path = dirname(dir_buf); char *file_name = basename(base_buf); int dirfd = open(dir_path, O_RDONLY | O_DIRECTORY); if (dirfd < 0) { perror("open parent dir failed"); return -1; } int ret = utimensat(dirfd, file_name, ts, 0); if (ret < 0) { perror("utimensat call failed"); close(dirfd); return -1; } close(dirfd); return 0; } // 简洁写法,无需手动处理目录fd int set_file_ts_ns_simple(const char *full_path, struct timespec ts[2]) { int ret = utimensat(AT_FDCWD, full_path, ts, 0); if (ret < 0) { perror("utimensat call failed"); return -1; } return 0; }
注意事项
- 打开父目录时需要当前进程对该目录有读权限,否则
open()会返回权限错误 - 如果需要修改符号链接本身的时间戳而非其指向的文件,记得给
utimensat()的flags参数加AT_SYMLINK_NOFOLLOW - 如果传入的文件路径是相对路径,
AT_FDCWD的写法会自动以进程当前工作目录为基准解析路径,不需要额外处理
内容的提问来源于stack exchange,提问作者programme3219873
相关产品推荐
相关产品推荐

