107. newfstatat - 相对于目录文件描述符获取文件状态 链接到标题

1. 函数介绍 链接到标题

newfstatat 是一个 Linux 系统调用,用于相对于指定目录文件描述符获取文件的状态信息。它是 fstatat 的别名,提供了比传统 stat 函数更灵活的文件状态查询方式。

2. 函数原型 链接到标题

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int newfstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);

注意:在某些系统中,这个函数可能被称为 fstatat,功能相同。

3. 功能 链接到标题

相对于目录文件描述符 dirfd 获取文件的状态信息。如果 pathname 是相对路径,则相对于 dirfd 指定的目录解析;如果是绝对路径,则忽略 dirfd

4. 参数 链接到标题

  • int dirfd: 目录文件描述符
    • AT_FDCWD: 使用当前工作目录
    • 有效的目录文件描述符:相对于该目录进行操作
  • const char *pathname: 文件路径名
    • 绝对路径:从根目录开始
    • 相对路径:相对于 dirfd 指定的目录
  • struct stat *statbuf: 指向 stat 结构体的指针,用于存储文件状态信息
  • int flags: 控制标志
    • 0: 基本行为
    • AT_SYMLINK_NOFOLLOW: 不跟随符号链接(获取符号链接本身的信息)
    • AT_NO_AUTOMOUNT: 不自动挂载(如果文件在未挂载的文件系统上)
    • AT_EMPTY_PATH: 允许空路径名(对 dirfd 本身进行操作)

5. stat 结构体定义 链接到标题

struct stat {
    dev_t     st_dev;         /* 设备 ID(包含文件的设备)*/
    ino_t     st_ino;         /* inode 号码 */
    mode_t    st_mode;        /* 文件类型和权限 */
    nlink_t   st_nlink;       /* 硬链接数 */
    uid_t     st_uid;         /* 所有者用户 ID */
    gid_t     st_gid;         /* 所有者组 ID */
    dev_t     st_rdev;        /* 设备 ID(如果是特殊文件)*/
    off_t     st_size;        /* 文件总大小(字节)*/
    blksize_t st_blksize;     /* 文件系统 I/O 块大小 */
    blkcnt_t  st_blocks;      /* 分配的 512B 块数 */
    time_t    st_atime;       /* 最后访问时间 */
    time_t    st_mtime;       /* 最后修改时间 */
    time_t    st_ctime;       /* 最后状态改变时间 */
};

6. 返回值 链接到标题

  • 成功时返回 0
  • 失败时返回 -1,并设置 errno

7. 常见 errno 错误码 链接到标题

  • EBADF: dirfd 不是有效的文件描述符且不等于 AT_FDCWD
  • ENOTDIR: dirfd 不是目录文件描述符
  • ENOENT: 指定的文件或路径不存在
  • EACCES: 权限不足
  • ELOOP: 符号链接层级过深
  • ENAMETOOLONG: 路径名过长
  • EFAULT: 指针参数指向无效内存地址
  • ENOMEM: 内存不足
  • EOVERFLOW: 文件大小、inode 号或链接数超出范围

8. 相似函数,或关联函数 链接到标题

  • stat(): 通过路径获取文件状态
  • lstat(): 获取符号链接本身的状态(不跟随链接)
  • fstat(): 通过文件描述符获取文件状态
  • openat(): 相对于目录文件描述符打开文件
  • readlinkat(): 相对于目录文件描述符读取符号链接
  • unlinkat(): 相对于目录文件描述符删除文件
  • mkdirat(): 相对于目录文件描述符创建目录

9. 示例代码 链接到标题

示例1:基本使用 - 获取文件状态信息 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <string.h>

void print_file_type(mode_t mode) {
    if (S_ISREG(mode)) {
        printf("文件类型: 普通文件\n");
    } else if (S_ISDIR(mode)) {
        printf("文件类型: 目录\n");
    } else if (S_ISLNK(mode)) {
        printf("文件类型: 符号链接\n");
    } else if (S_ISCHR(mode)) {
        printf("文件类型: 字符设备\n");
    } else if (S_ISBLK(mode)) {
        printf("文件类型: 块设备\n");
    } else if (S_ISFIFO(mode)) {
        printf("文件类型: FIFO/管道\n");
    } else if (S_ISSOCK(mode)) {
        printf("文件类型: 套接字\n");
    } else {
        printf("文件类型: 未知\n");
    }
}

void print_file_permissions(mode_t mode) {
    printf("权限: ");
    printf((S_ISDIR(mode)) ? "d" : "-");
    printf((mode & S_IRUSR) ? "r" : "-");
    printf((mode & S_IWUSR) ? "w" : "-");
    printf((mode & S_IXUSR) ? "x" : "-");
    printf((mode & S_IRGRP) ? "r" : "-");
    printf((mode & S_IWGRP) ? "w" : "-");
    printf((mode & S_IXGRP) ? "x" : "-");
    printf((mode & S_IROTH) ? "r" : "-");
    printf((mode & S_IWOTH) ? "w" : "-");
    printf((mode & S_IXOTH) ? "x" : "-");
    printf("\n");
}

void print_stat_info(const char *label, const struct stat *st) {
    printf("\n=== %s ===\n", label);
    printf("设备 ID: %ld\n", (long)st->st_dev);
    printf("inode 号: %ld\n", (long)st->st_ino);
    
    print_file_type(st->st_mode);
    print_file_permissions(st->st_mode);
    
    printf("硬链接数: %ld\n", (long)st->st_nlink);
    printf("所有者 UID: %d\n", st->st_uid);
    printf("所属组 GID: %d\n", st->st_gid);
    printf("文件大小: %ld 字节\n", (long)st->st_size);
    printf("I/O 块大小: %ld\n", (long)st->st_blksize);
    printf("分配的块数: %ld (512字节块)\n", (long)st->st_blocks);
    
    // 打印时间信息
    printf("最后访问时间: %s", ctime(&st->st_atime));
    printf("最后修改时间: %s", ctime(&st->st_mtime));
    printf("状态改变时间: %s", ctime(&st->st_ctime));
}

int main() {
    printf("=== newfstatat 基本使用演示 ===\n");
    
    // 创建测试目录结构
    if (mkdir("test_dir", 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
        exit(EXIT_FAILURE);
    }
    
    // 在测试目录中创建文件
    int fd = open("test_dir/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        rmdir("test_dir");
        exit(EXIT_FAILURE);
    }
    
    const char *content = "This is a test file for newfstatat demonstration.\n";
    write(fd, content, strlen(content));
    close(fd);
    
    printf("创建测试文件: test_dir/test_file.txt\n");
    
    // 创建符号链接
    if (symlink("test_file.txt", "test_dir/test_symlink") == -1) {
        if (errno != EEXIST) {
            perror("创建符号链接失败");
            unlink("test_dir/test_file.txt");
            rmdir("test_dir");
            exit(EXIT_FAILURE);
        }
    }
    printf("创建符号链接: test_dir/test_symlink -> test_file.txt\n");
    
    // 方法1: 使用 AT_FDCWD(当前工作目录)获取文件状态
    struct stat stat_info1;
    if (newfstatat(AT_FDCWD, "test_dir/test_file.txt", &stat_info1, 0) == 0) {
        print_stat_info("AT_FDCWD 方式获取的文件状态", &stat_info1);
        printf("✓ 使用 AT_FDCWD 成功获取文件状态\n");
    } else {
        perror("使用 AT_FDCWD 获取文件状态失败");
    }
    
    // 方法2: 打开目录获取文件描述符,然后相对获取文件状态
    int dirfd = open("test_dir", O_RDONLY);
    if (dirfd == -1) {
        perror("打开目录失败");
        unlink("test_dir/test_symlink");
        unlink("test_dir/test_file.txt");
        rmdir("test_dir");
        exit(EXIT_FAILURE);
    }
    
    printf("\n✓ 成功打开目录: test_dir (dirfd: %d)\n", dirfd);
    
    // 相对于目录文件描述符获取文件状态
    struct stat stat_info2;
    if (newfstatat(dirfd, "test_file.txt", &stat_info2, 0) == 0) {
        print_stat_info("相对目录获取的文件状态", &stat_info2);
        printf("✓ 相对目录成功获取文件状态\n");
    } else {
        perror("相对目录获取文件状态失败");
    }
    
    // 获取符号链接状态(不跟随链接)
    struct stat stat_info3;
    if (newfstatat(dirfd, "test_symlink", &stat_info3, AT_SYMLINK_NOFOLLOW) == 0) {
        print_stat_info("符号链接本身的状态", &stat_info3);
        if (S_ISLNK(stat_info3.st_mode)) {
            printf("✓ 成功获取符号链接本身的状态\n");
        }
    } else {
        perror("获取符号链接状态失败");
    }
    
    // 获取符号链接目标状态(跟随链接)
    struct stat stat_info4;
    if (newfstatat(dirfd, "test_symlink", &stat_info4, 0) == 0) {
        print_stat_info("符号链接目标的状态", &stat_info4);
        printf("✓ 成功获取符号链接目标的状态\n");
    } else {
        perror("获取符号链接目标状态失败");
    }
    
    // 验证 inode 号码
    printf("\n=== inode 号码对比 ===\n");
    printf("文件 inode: %ld\n", (long)stat_info1.st_ino);
    printf("符号链接 inode: %ld\n", (long)stat_info3.st_ino);
    printf("符号链接目标 inode: %ld\n", (long)stat_info4.st_ino);
    
    if (stat_info3.st_ino != stat_info4.st_ino) {
        printf("✓ 符号链接和目标文件有不同的 inode(正常)\n");
    }
    
    // 清理资源
    close(dirfd);
    unlink("test_dir/test_symlink");
    unlink("test_dir/test_file.txt");
    rmdir("test_dir");
    
    printf("\n✓ 完成 newfstatat 基本使用演示\n");
    
    return 0;
}

示例2:错误处理和特殊情况 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

void test_newfstatat_errors(int dirfd, const char *pathname, int flags, const char *description) {
    printf("\n测试 %s:\n", description);
    printf("  dirfd: %d\n", dirfd);
    printf("  pathname: %s\n", pathname);
    printf("  flags: 0x%x\n", flags);
    
    struct stat st;
    int result = newfstatat(dirfd, pathname, &st, flags);
    
    if (result == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        switch (errno) {
            case EBADF:
                printf("    原因: 无效的 dirfd\n");
                break;
            case ENOTDIR:
                printf("    原因: dirfd 不是目录\n");
                break;
            case ENOENT:
                printf("    原因: 文件或路径不存在\n");
                break;
            case EACCES:
                printf("    原因: 权限不足\n");
                break;
            case ELOOP:
                printf("    原因: 符号链接层级过深\n");
                break;
            case ENAMETOOLONG:
                printf("    原因: 路径名过长\n");
                break;
            case EFAULT:
                printf("    原因: 无效的指针参数\n");
                break;
            default:
                printf("    原因: 其他错误\n");
                break;
        }
    } else {
        printf("  结果: 成功\n");
        printf("  文件类型: ");
        if (S_ISREG(st.st_mode)) printf("普通文件\n");
        else if (S_ISDIR(st.st_mode)) printf("目录\n");
        else if (S_ISLNK(st.st_mode)) printf("符号链接\n");
        else printf("其他\n");
        printf("  inode: %ld\n", (long)st.st_ino);
        printf("  大小: %ld 字节\n", (long)st.st_size);
    }
}

int main() {
    printf("=== newfstatat 错误处理测试 ===\n");
    
    // 创建测试环境
    if (mkdir("error_test_dir", 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
        exit(EXIT_FAILURE);
    }
    
    // 创建测试文件
    int fd = open("error_test_dir/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "test content", 12);
        close(fd);
        printf("创建测试文件: error_test_dir/test_file.txt\n");
    }
    
    // 创建符号链接
    if (symlink("test_file.txt", "error_test_dir/test_symlink") == 0) {
        printf("创建符号链接: error_test_dir/test_symlink -> test_file.txt\n");
    }
    
    // 打开目录获取文件描述符
    int dirfd = open("error_test_dir", O_RDONLY);
    if (dirfd == -1) {
        perror("打开测试目录失败");
        unlink("error_test_dir/test_symlink");
        unlink("error_test_dir/test_file.txt");
        rmdir("error_test_dir");
        exit(EXIT_FAILURE);
    }
    
    printf("打开测试目录: error_test_dir (dirfd: %d)\n", dirfd);
    
    // 测试正常情况
    test_newfstatat_errors(dirfd, "test_file.txt", 0, "正常相对路径获取状态");
    test_newfstatat_errors(AT_FDCWD, "error_test_dir/test_file.txt", 0, "AT_FDCWD 绝对路径获取状态");
    
    // 测试各种错误情况
    test_newfstatat_errors(dirfd, "nonexistent.txt", 0, "不存在的文件");
    test_newfstatat_errors(-2, "test_file.txt", 0, "无效的 dirfd");
    test_newfstatat_errors(dirfd, "", 0, "空路径名");
    test_newfstatat_errors(dirfd, "test_file.txt", 999, "无效的标志位");
    
    // 测试符号链接相关标志
    test_newfstatat_errors(dirfd, "test_symlink", AT_SYMLINK_NOFOLLOW, "不跟随符号链接");
    test_newfstatat_errors(dirfd, "test_symlink", 0, "跟随符号链接");
    
    // 测试过长路径名
    char long_path[1024];
    memset(long_path, 'a', sizeof(long_path) - 1);
    long_path[sizeof(long_path) - 1] = '\0';
    test_newfstatat_errors(dirfd, long_path, 0, "过长路径名");
    
    // 测试 NULL 指针
    printf("\n测试 NULL 指针:\n");
    int result = newfstatat(dirfd, "test_file.txt", NULL, 0);
    if (result == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        if (errno == EFAULT) {
            printf("    原因: 无效的 statbuf 指针\n");
        }
    }
    
    // 清理测试环境
    close(dirfd);
    unlink("error_test_dir/test_symlink");
    unlink("error_test_dir/test_file.txt");
    rmdir("error_test_dir");
    
    return 0;
}

示例3:符号链接和特殊文件处理 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

void analyze_symlink_behavior() {
    printf("=== 符号链接行为分析 ===\n");
    
    // 创建测试环境
    if (mkdir("symlink_test", 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
        return;
    }
    
    // 创建目标文件
    int fd = open("symlink_test/target.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "This is the target file content.", 32);
        close(fd);
        printf("创建目标文件: symlink_test/target.txt\n");
    }
    
    // 创建符号链接
    if (symlink("target.txt", "symlink_test/symlink_to_target") == 0) {
        printf("创建符号链接: symlink_test/symlink_to_target -> target.txt\n");
    }
    
    // 创建悬空符号链接
    if (symlink("nonexistent.txt", "symlink_test/dangling_symlink") == 0) {
        printf("创建悬空符号链接: symlink_test/dangling_symlink -> nonexistent.txt\n");
    }
    
    // 打开目录
    int dirfd = open("symlink_test", O_RDONLY);
    if (dirfd == -1) {
        perror("打开测试目录失败");
        unlink("symlink_test/symlink_to_target");
        unlink("symlink_test/dangling_symlink");
        unlink("symlink_test/target.txt");
        rmdir("symlink_test");
        return;
    }
    
    printf("\n分析符号链接行为:\n");
    
    // 获取目标文件状态
    struct stat target_stat;
    if (newfstatat(dirfd, "target.txt", &target_stat, 0) == 0) {
        printf("1. 目标文件状态:\n");
        printf("   inode: %ld\n", (long)target_stat.st_ino);
        printf("   大小: %ld 字节\n", (long)target_stat.st_size);
        printf("   类型: ");
        if (S_ISREG(target_stat.st_mode)) printf("普通文件\n");
        else printf("其他\n");
    }
    
    // 获取符号链接状态(不跟随)
    struct stat symlink_stat;
    if (newfstatat(dirfd, "symlink_to_target", &symlink_stat, AT_SYMLINK_NOFOLLOW) == 0) {
        printf("2. 符号链接本身状态 (AT_SYMLINK_NOFOLLOW):\n");
        printf("   inode: %ld\n", (long)symlink_stat.st_ino);
        printf("   大小: %ld 字节\n", (long)symlink_stat.st_size);
        printf("   类型: ");
        if (S_ISLNK(symlink_stat.st_mode)) printf("符号链接\n");
        else printf("其他\n");
    }
    
    // 获取符号链接目标状态(跟随)
    struct stat symlink_target_stat;
    if (newfstatat(dirfd, "symlink_to_target", &symlink_target_stat, 0) == 0) {
        printf("3. 符号链接目标状态 (默认行为):\n");
        printf("   inode: %ld\n", (long)symlink_target_stat.st_ino);
        printf("   大小: %ld 字节\n", (long)symlink_target_stat.st_size);
        printf("   类型: ");
        if (S_ISREG(symlink_target_stat.st_mode)) printf("普通文件\n");
        else printf("其他\n");
    }
    
    // 比较 inode 号码
    printf("\ninode 号码对比:\n");
    printf("   目标文件 inode: %ld\n", (long)target_stat.st_ino);
    printf("   符号链接 inode: %ld\n", (long)symlink_stat.st_ino);
    printf("   符号链接目标 inode: %ld\n", (long)symlink_target_stat.st_ino);
    
    if (target_stat.st_ino == symlink_target_stat.st_ino) {
        printf("✓ 符号链接正确指向目标文件\n");
    }
    
    if (symlink_stat.st_ino != target_stat.st_ino) {
        printf("✓ 符号链接和目标文件有不同的 inode\n");
    }
    
    // 测试悬空符号链接
    printf("\n悬空符号链接测试:\n");
    
    // 悬空符号链接(不跟随)
    struct stat dangling_stat;
    if (newfstatat(dirfd, "dangling_symlink", &dangling_stat, AT_SYMLINK_NOFOLLOW) == 0) {
        printf("悬空符号链接状态 (AT_SYMLINK_NOFOLLOW):\n");
        printf("   类型: ");
        if (S_ISLNK(dangling_stat.st_mode)) printf("符号链接\n");
        else printf("其他\n");
        printf("✓ 可以获取悬空符号链接本身的状态\n");
    } else {
        printf("获取悬空符号链接状态失败: %s\n", strerror(errno));
    }
    
    // 悬空符号链接(跟随)
    if (newfstatat(dirfd, "dangling_symlink", &dangling_stat, 0) == -1) {
        if (errno == ENOENT) {
            printf("悬空符号链接目标 (默认行为): ENOENT (文件不存在)\n");
            printf("✓ 正确处理悬空符号链接\n");
        } else {
            printf("悬空符号链接目标: %s\n", strerror(errno));
        }
    }
    
    // 清理资源
    close(dirfd);
    unlink("symlink_test/symlink_to_target");
    unlink("symlink_test/dangling_symlink");
    unlink("symlink_test/target.txt");
    rmdir("symlink_test");
    
    printf("\n✓ 完成符号链接行为分析\n");
}

void demonstrate_special_files() {
    printf("\n=== 特殊文件类型演示 ===\n");
    
    // 创建测试目录
    if (mkdir("special_test", 0755) == -1 && errno != EEXIST) {
        perror("创建测试目录失败");
        return;
    }
    
    // 打开目录
    int dirfd = open("special_test", O_RDONLY);
    if (dirfd == -1) {
        perror("打开测试目录失败");
        rmdir("special_test");
        return;
    }
    
    printf("分析当前目录中的特殊文件:\n");
    
    // 获取当前目录状态
    struct stat dir_stat;
    if (newfstatat(dirfd, ".", &dir_stat, 0) == 0) {
        printf("当前目录 (.) 状态:\n");
        printf("  inode: %ld\n", (long)dir_stat.st_ino);
        printf("  类型: ");
        if (S_ISDIR(dir_stat.st_mode)) printf("目录\n");
        else printf("其他\n");
        printf("  权限: %o\n", dir_stat.st_mode & 0777);
        printf("  硬链接数: %ld\n", (long)dir_stat.st_nlink);
    }
    
    // 获取父目录状态
    if (newfstatat(dirfd, "..", &dir_stat, 0) == 0) {
        printf("父目录 (..) 状态:\n");
        printf("  inode: %ld\n", (long)dir_stat.st_ino);
        printf("  类型: ");
        if (S_ISDIR(dir_stat.st_mode)) printf("目录\n");
        else printf("其他\n");
    }
    
    // 获取当前工作目录状态
    if (newfstatat(AT_FDCWD, ".", &dir_stat, 0) == 0) {
        printf("当前工作目录状态:\n");
        printf("  inode: %ld\n", (long)dir_stat.st_ino);
        printf("  类型: ");
        if (S_ISDIR(dir_stat.st_mode)) printf("目录\n");
        else printf("其他\n");
    }
    
    // 清理资源
    close(dirfd);
    rmdir("special_test");
    
    printf("✓ 完成特殊文件类型演示\n");
}

int main() {
    printf("=== newfstatat 符号链接和特殊文件处理 ===\n");
    
    analyze_symlink_behavior();
    demonstrate_special_files();
    
    return 0;
}

示例4:高级文件状态分析工具 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

typedef struct {
    char path[256];
    struct stat stat_info;
    int is_symlink;
    char target_path[256];
} file_info_t;

void print_detailed_stat_info(const file_info_t *file_info) {
    const struct stat *st = &file_info->stat_info;
    
    printf("=== 文件详细信息: %s ===\n", file_info->path);
    
    // 基本信息
    printf("设备 ID: %ld (0x%lx)\n", (long)st->st_dev, (long)st->st_dev);
    printf("inode 号: %ld\n", (long)st->st_ino);
    
    // 文件类型和权限
    printf("文件类型和权限: ");
    if (S_ISREG(st->st_mode)) printf("普通文件");
    else if (S_ISDIR(st->st_mode)) printf("目录");
    else if (S_ISLNK(st->st_mode)) printf("符号链接");
    else if (S_ISCHR(st->st_mode)) printf("字符设备");
    else if (S_ISBLK(st->st_mode)) printf("块设备");
    else if (S_ISFIFO(st->st_mode)) printf("FIFO");
    else if (S_ISSOCK(st->st_mode)) printf("套接字");
    else printf("未知");
    
    printf(" (0%o)\n", st->st_mode & 0777);
    
    // 所有者信息
    printf("所有者: %d", st->st_uid);
    struct passwd *pwd = getpwuid(st->st_uid);
    if (pwd) {
        printf(" (%s)", pwd->pw_name);
    }
    printf("\n");
    
    printf("所属组: %d", st->st_gid);
    struct group *grp = getgrgid(st->st_gid);
    if (grp) {
        printf(" (%s)", grp->gr_name);
    }
    printf("\n");
    
    // 大小和存储信息
    printf("文件大小: %ld 字节", (long)st->st_size);
    if (st->st_size >= 1024) {
        printf(" (%.2f KB)", (double)st->st_size / 1024);
    }
    if (st->st_size >= 1024 * 1024) {
        printf(" (%.2f MB)", (double)st->st_size / (1024 * 1024));
    }
    printf("\n");
    
    printf("I/O 块大小: %ld 字节\n", (long)st->st_blksize);
    printf("分配的块数: %ld (512字节块)\n", (long)st->st_blocks);
    printf("实际占用空间: %ld 字节\n", (long)st->st_blocks * 512);
    
    // 硬链接信息
    printf("硬链接数: %ld\n", (long)st->st_nlink);
    
    // 时间信息
    printf("最后访问时间: %s", ctime(&st->st_atime));
    printf("最后修改时间: %s", ctime(&st->st_mtime));
    printf("状态改变时间: %s", ctime(&st->st_ctime));
    
    // 符号链接信息
    if (file_info->is_symlink) {
        printf("符号链接目标: %s\n", file_info->target_path);
    }
    
    // 特殊文件信息
    if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
        printf("设备号: %ld (主设备号: %ld, 次设备号: %ld)\n",
               (long)st->st_rdev,
               (long)major(st->st_rdev),
               (long)minor(st->st_rdev));
    }
}

int get_file_info(int dirfd, const char *pathname, file_info_t *file_info) {
    // 初始化结构体
    memset(file_info, 0, sizeof(file_info_t));
    strncpy(file_info->path, pathname, sizeof(file_info->path) - 1);
    
    // 首先检查是否为符号链接(不跟随)
    if (newfstatat(dirfd, pathname, &file_info->stat_info, AT_SYMLINK_NOFOLLOW) == 0) {
        if (S_ISLNK(file_info->stat_info.st_mode)) {
            file_info->is_symlink = 1;
            
            // 获取符号链接目标路径
            ssize_t len = readlinkat(dirfd, pathname, file_info->target_path, 
                                   sizeof(file_info->target_path) - 1);
            if (len != -1) {
                file_info->target_path[len] = '\0';
            }
            
            // 获取符号链接目标的 stat 信息
            struct stat target_stat;
            if (newfstatat(dirfd, pathname, &target_stat, 0) == 0) {
                file_info->stat_info = target_stat;
            }
        }
        return 0;
    }
    
    return -1;
}

void interactive_stat_tool() {
    int choice;
    char path[256];
    int dirfd = AT_FDCWD;
    int current_dirfd = -1;
    
    while (1) {
        printf("\n=== newfstatat 文件状态分析工具 ===\n");
        printf("当前目录文件描述符: %d\n", dirfd);
        printf("1. 设置目录文件描述符\n");
        printf("2. 获取文件状态信息\n");
        printf("3. 批量分析目录内容\n");
        printf("4. 比较两个文件\n");
        printf("5. 显示系统信息\n");
        printf("6. 高级选项\n");
        printf("0. 退出\n");
        printf("请选择操作: ");
        
        if (scanf("%d", &choice) != 1) {
            printf("输入无效\n");
            while (getchar() != '\n');  // 清空输入缓冲区
            continue;
        }
        
        switch (choice) {
            case 1:
                printf("设置目录文件描述符:\n");
                printf("1. 使用 AT_FDCWD (当前工作目录)\n");
                printf("2. 打开指定目录\n");
                printf("请选择: ");
                
                int dir_choice;
                if (scanf("%d", &dir_choice) == 1) {
                    if (dir_choice == 1) {
                        dirfd = AT_FDCWD;
                        if (current_dirfd != -1) {
                            close(current_dirfd);
                            current_dirfd = -1;
                        }
                        printf("✓ 设置为当前工作目录\n");
                    } else if (dir_choice == 2) {
                        printf("输入目录路径: ");
                        scanf("%255s", path);
                        
                        int new_dirfd = open(path, O_RDONLY);
                        if (new_dirfd != -1) {
                            if (current_dirfd != -1) {
                                close(current_dirfd);
                            }
                            dirfd = current_dirfd = new_dirfd;
                            printf("✓ 成功打开目录 (dirfd: %d)\n", dirfd);
                        } else {
                            printf("✗ 打开目录失败: %s\n", strerror(errno));
                        }
                    }
                }
                break;
                
            case 2: {
                printf("获取文件状态信息:\n");
                printf("输入文件路径: ");
                scanf("%255s", path);
                
                file_info_t file_info;
                if (get_file_info(dirfd, path, &file_info) == 0) {
                    print_detailed_stat_info(&file_info);
                } else {
                    printf("✗ 获取文件状态失败: %s\n", strerror(errno));
                }
                break;
            }
            
            case 3: {
                printf("批量分析目录内容:\n");
                printf("输入目录路径: ");
                scanf("%255s", path);
                
                int batch_dirfd = (dirfd == AT_FDCWD) ? open(path, O_RDONLY) : 
                                 openat(dirfd, path, O_RDONLY);
                
                if (batch_dirfd != -1) {
                    DIR *dir = fdopendir(batch_dirfd);
                    if (dir) {
                        struct dirent *entry;
                        int count = 0;
                        
                        printf("目录 %s 的内容:\n", path);
                        printf("%-20s %-10s %-8s %s\n", "名称", "类型", "大小", "权限");
                        printf("%-20s %-10s %-8s %s\n", "----", "----", "----", "----");
                        
                        while ((entry = readdir(dir)) != NULL && count < 50) {
                            if (strcmp(entry->d_name, ".") == 0 || 
                                strcmp(entry->d_name, "..") == 0) {
                                continue;
                            }
                            
                            struct stat st;
                            if (newfstatat(batch_dirfd, entry->d_name, &st, 
                                         AT_SYMLINK_NOFOLLOW) == 0) {
                                const char *type;
                                if (S_ISDIR(st.st_mode)) type = "目录";
                                else if (S_ISLNK(st.st_mode)) type = "链接";
                                else if (S_ISREG(st.st_mode)) type = "文件";
                                else type = "其他";
                                
                                printf("%-20s %-10s %-8ld %o\n",
                                       entry->d_name, type, (long)st.st_size,
                                       st.st_mode & 0777);
                                count++;
                            }
                        }
                        
                        if (count >= 50) {
                            printf("... (显示前50个项目)\n");
                        }
                        
                        closedir(dir);
                    } else {
                        close(batch_dirfd);
                        printf("✗ 打开目录流失败\n");
                    }
                } else {
                    printf("✗ 打开目录失败: %s\n", strerror(errno));
                }
                break;
            }
            
            case 4: {
                printf("比较两个文件:\n");
                char path1[256], path2[256];
                printf("输入第一个文件路径: ");
                scanf("%255s", path1);
                printf("输入第二个文件路径: ");
                scanf("%255s", path2);
                
                file_info_t file1, file2;
                if (get_file_info(dirfd, path1, &file1) == 0 &&
                    get_file_info(dirfd, path2, &file2) == 0) {
                    
                    printf("文件比较结果:\n");
                    printf("  inode 相同: %s\n", 
                           (file1.stat_info.st_ino == file2.stat_info.st_ino) ? "是" : "否");
                    printf("  大小相同: %s\n",
                           (file1.stat_info.st_size == file2.stat_info.st_size) ? "是" : "否");
                    printf("  修改时间相同: %s\n",
                           (file1.stat_info.st_mtime == file2.stat_info.st_mtime) ? "是" : "否");
                    printf("  权限相同: %s\n",
                           ((file1.stat_info.st_mode & 0777) == (file2.stat_info.st_mode & 0777)) ? "是" : "否");
                } else {
                    printf("✗ 获取文件信息失败\n");
                }
                break;
            }
            
            case 5:
                printf("系统信息:\n");
                printf("  当前 PID: %d\n", getpid());
                printf("  用户 ID: %d\n", getuid());
                printf("  组 ID: %d\n", getgid());
                printf("  页面大小: %ld 字节\n", (long)getpagesize());
                system("uname -a");
                break;
                
            case 6: {
                printf("高级选项:\n");
                printf("1. 使用 AT_SYMLINK_NOFOLLOW 标志\n");
                printf("2. 显示原始 stat 结构体\n");
                printf("3. 性能测试\n");
                printf("请选择: ");
                
                int advanced_choice;
                if (scanf("%d", &advanced_choice) == 1) {
                    switch (advanced_choice) {
                        case 1: {
                            printf("输入符号链接路径: ");
                            scanf("%255s", path);
                            
                            struct stat st;
                            if (newfstatat(dirfd, path, &st, AT_SYMLINK_NOFOLLOW) == 0) {
                                printf("符号链接本身的状态:\n");
                                printf("  inode: %ld\n", (long)st.st_ino);
                                printf("  类型: %s\n", 
                                       S_ISLNK(st.st_mode) ? "符号链接" : "其他");
                                printf("  大小: %ld 字节\n", (long)st.st_size);
                            } else {
                                printf("获取状态失败: %s\n", strerror(errno));
                            }
                            break;
                        }
                        
                        case 2: {
                            printf("输入文件路径: ");
                            scanf("%255s", path);
                            
                            struct stat st;
                            if (newfstatat(dirfd, path, &st, 0) == 0) {
                                printf("原始 stat 结构体:\n");
                                printf("  st_dev: %ld\n", (long)st.st_dev);
                                printf("  st_ino: %ld\n", (long)st.st_ino);
                                printf("  st_mode: 0%o\n", st.st_mode);
                                printf("  st_nlink: %ld\n", (long)st.st_nlink);
                                printf("  st_uid: %d\n", st.st_uid);
                                printf("  st_gid: %d\n", st.st_gid);
                                printf("  st_rdev: %ld\n", (long)st.st_rdev);
                                printf("  st_size: %ld\n", (long)st.st_size);
                                printf("  st_blksize: %ld\n", (long)st.st_blksize);
                                printf("  st_blocks: %ld\n", (long)st.st_blocks);
                                printf("  st_atime: %ld\n", (long)st.st_atime);
                                printf("  st_mtime: %ld\n", (long)st.st_mtime);
                                printf("  st_ctime: %ld\n", (long)st.st_ctime);
                            }
                            break;
                        }
                        
                        case 3: {
                            printf("性能测试:\n");
                            printf("输入测试文件路径: ");
                            scanf("%255s", path);
                            
                            clock_t start = clock();
                            const int iterations = 10000;
                            
                            for (int i = 0; i < iterations; i++) {
                                struct stat st;
                                newfstatat(dirfd, path, &st, 0);
                            }
                            
                            clock_t end = clock();
                            double time_spent = ((double)(end - start)) / CLOCKS_PER_SEC;
                            
                            printf("执行 %d 次 newfstatat 调用\n", iterations);
                            printf("总时间: %.6f 秒\n", time_spent);
                            printf("平均每次调用: %.6f 微秒\n", 
                                   (time_spent * 1000000) / iterations);
                            break;
                        }
                    }
                }
                break;
            }
            
            case 0:
                printf("退出文件状态分析工具\n");
                if (current_dirfd != -1) {
                    close(current_dirfd);
                }
                return;
                
            default:
                printf("无效选择\n");
                break;
        }
    }
}

void demonstrate_newfstatat_features() {
    printf("=== newfstatat 特性演示 ===\n");
    
    printf("newfstatat 主要特性:\n");
    printf("1. 相对路径支持: 相对于目录文件描述符获取文件状态\n");
    printf("2. 符号链接控制: 可选择是否跟随符号链接\n");
    printf("3. 安全性提升: 避免路径解析竞态条件\n");
    printf("4. 灵活性: 支持 AT_FDCWD 和绝对路径\n");
    printf("5. 容器友好: 在受限环境中更安全\n");
    
    printf("\n标志位说明:\n");
    printf("  0: 基本行为,跟随符号链接\n");
    printf("  AT_SYMLINK_NOFOLLOW: 不跟随符号链接\n");
    printf("  AT_NO_AUTOMOUNT: 不自动挂载\n");
    printf("  AT_EMPTY_PATH: 允许空路径名\n");
    
    printf("\n使用场景:\n");
    printf("• 安全的文件状态查询\n");
    printf("• 容器和沙箱环境\n");
    printf("• 符号链接处理\n");
    printf("• 系统管理和监控工具\n");
}

int main() {
    printf("=== newfstatat 高级文件状态分析工具 ===\n");
    
    // 演示特性
    demonstrate_newfstatat_features();
    
    // 启动交互式工具
    char choice;
    printf("\n是否启动交互式文件状态分析工具? (y/N): ");
    if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
        interactive_stat_tool();
    }
    
    return 0;
}

10. newfstatat 与相关函数的对比 链接到标题

// 文件状态获取函数对比:

// 传统 stat 函数:
struct stat st1;
int result1 = stat("/path/to/file.txt", &st1);
// • 只能使用绝对或相对路径
// • 可能存在竞态条件
// • 对符号链接默认跟随

// 现代 newfstatat 函数:
int dirfd = open("/path/to", O_RDONLY);
struct stat st2;
int result2 = newfstatat(dirfd, "file.txt", &st2, AT_SYMLINK_NOFOLLOW);
// • 支持相对路径操作
// • 可控制符号链接行为
// • 基于已打开的目录文件描述符
// • 更安全的文件状态查询

// fstat 函数:
int fd = open("/path/to/file.txt", O_RDONLY);
struct stat st3;
int result3 = fstat(fd, &st3);
// • 通过文件描述符获取状态
// • 无需路径解析
// • 最安全但需要先打开文件

// 特殊值 AT_FDCWD:
int result4 = newfstatat(AT_FDCWD, "relative/path.txt", &st2, 0);
// • 等同于传统的相对路径 stat
// • 提供统一的接口

11. 实际应用场景 链接到标题

场景1:安全的文件状态查询 链接到标题

int secure_stat_check(const char *base_dir, const char *filename) {
    // 打开基础目录
    int dirfd = open(base_dir, O_RDONLY);
    if (dirfd == -1) {
        return -1;
    }
    
    // 相对获取文件状态(防止目录遍历攻击)
    struct stat st;
    int result = newfstatat(dirfd, filename, &st, 0);
    
    close(dirfd);
    return result;
}

场景2:符号链接安全处理 链接到标题

int safe_symlink_check(const char *path) {
    int dirfd = AT_FDCWD;
    struct stat st_link, st_target;
    
    // 获取符号链接本身的状态
    if (newfstatat(dirfd, path, &st_link, AT_SYMLINK_NOFOLLOW) == 0) {
        if (S_ISLNK(st_link.st_mode)) {
            // 是符号链接,获取目标状态
            if (newfstatat(dirfd, path, &st_target, 0) == 0) {
                // 检查目标是否安全
                return check_target_safety(&st_target);
            } else {
                // 目标不存在(悬空链接)
                return -1;
            }
        }
    }
    
    return -1;
}

场景3:容器环境文件检查 链接到标题

int container_file_check(int root_dirfd, const char *path) {
    // 在容器根目录中安全检查文件
    // 防止跳出容器文件系统
    struct stat st;
    return newfstatat(root_dirfd, path, &st, 0);
}

12. 注意事项 链接到标题

使用 newfstatat 时需要注意:

  1. 目录文件描述符: dirfd 必须是有效的目录文件描述符
  2. 路径安全: 防止目录遍历攻击(如 ../
  3. 符号链接处理: 正确使用 AT_SYMLINK_NOFOLLOW 标志
  4. 错误处理: 仔细处理各种可能的错误情况
  5. 权限检查: 确保有足够的权限访问目标文件
  6. 竞态条件: 在多线程环境中注意同步

13. 系统配置检查 链接到标题

# 查看系统支持的文件操作特性
grep -i fstatat /proc/self/maps

# 查看文件系统信息
df -T

# 查看进程打开的文件描述符
ls -la /proc/self/fd/

# 检查系统调用表
ausyscall newfstatat

# 查看系统限制
ulimit -n

总结 链接到标题

newfstatat 是现代 Linux 系统中推荐使用的文件状态获取函数:

关键特性:

  1. 相对路径支持: 相对于目录文件描述符获取文件状态
  2. 符号链接控制: 可选择是否跟随符号链接
  3. 安全性提升: 避免路径解析竞态条件
  4. 灵活性增强: 支持多种操作模式和标志

主要应用:

  1. 安全的文件状态查询程序
  2. 容器和虚拟化环境
  3. 符号链接处理工具
  4. 系统管理和监控应用

使用要点:

  1. 理解 dirfd 参数的含义和使用
  2. 正确处理相对路径和绝对路径
  3. 合理使用符号链接控制标志
  4. 注意资源管理和错误处理

newfstatat 为现代 Linux 应用程序提供了更安全、更灵活的文件状态查询方式,是系统编程的重要工具。