1. 函数介绍 链接到标题

link 是一个 Linux 系统调用,用于创建文件的硬链接。硬链接是文件系统中指向同一 inode(索引节点)的多个目录条目,所有硬链接共享相同的文件数据和元数据。

2. 函数原型 链接到标题

#include <unistd.h>

int link(const char *oldpath, const char *newpath);

95. linkat - 相对于目录文件描述符创建硬链接 链接到标题

1. 函数介绍 链接到标题

linkatlink 的扩展版本,允许相对于指定目录文件描述符创建硬链接。它提供了更灵活的链接创建方式,支持相对路径和额外的控制标志。

2. 函数原型 链接到标题

#include <fcntl.h>
#include <unistd.h>

int linkat(int olddirfd, const char *oldpath,
           int newdirfd, const char *newpath, int flags);

3. 功能对比 链接到标题

函数 功能 路径处理 控制标志
link(oldpath, newpath) 创建硬链接 绝对/相对路径
linkat(olddirfd, oldpath, newdirfd, newpath, flags) 创建硬链接(扩展版) 相对于文件描述符 支持 AT_SYMLINK_FOLLOW

4. 参数说明 链接到标题

link 参数:

  • const char *oldpath: 现有文件的路径
  • const char *newpath: 新硬链接的路径

linkat 参数:

  • int olddirfd: 源文件所在目录的文件描述符
    • AT_FDCWD: 使用当前工作目录
    • 有效的目录文件描述符:相对于该目录
  • const char *oldpath: 源文件路径(相对或绝对)
  • int newdirfd: 目标目录的文件描述符
    • AT_FDCWD: 使用当前工作目录
    • 有效的目录文件描述符:相对于该目录
  • const char *newpath: 目标硬链接路径(相对或绝对)
  • int flags: 控制标志
    • 0: 基本行为
    • AT_SYMLINK_FOLLOW: 跟随符号链接(在某些实现中)

5. 返回值 链接到标题

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

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

  • EACCES: 权限不足
  • EDQUOT: 磁盘配额超限
  • EEXIST: 目标文件已存在
  • EFAULT: 路径指针指向无效内存地址
  • EIO: I/O 错误
  • ELOOP: 符号链接层级过深
  • EMLINK: 源文件的硬链接数已达上限
  • ENAMETOOLONG: 路径名过长
  • ENOENT: 源文件不存在
  • ENOMEM: 内存不足
  • ENOSPC: 磁盘空间不足
  • ENOTDIR: 指定的文件描述符不是目录
  • EPERM: 操作被拒绝
  • EROFS: 文件系统为只读
  • EXDEV: 不允许跨文件系统创建硬链接

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

  • symlink(), symlinkat(): 创建符号链接
  • unlink(): 删除目录条目(删除硬链接)
  • rename(), renameat(): 重命名文件
  • stat(), lstat(), fstat(): 获取文件状态
  • openat(): 相对于目录文件描述符打开文件
  • readlink(), readlinkat(): 读取符号链接内容

8. 示例代码 链接到标题

示例1:基本使用 - 硬链接创建 链接到标题

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

void print_file_info(const char *filename) {
    struct stat st;
    if (stat(filename, &st) == 0) {
        printf("文件: %s\n", filename);
        printf("  inode: %ld\n", (long)st.st_ino);
        printf("  硬链接数: %ld\n", (long)st.st_nlink);
        printf("  大小: %ld 字节\n", (long)st.st_size);
        printf("  权限: %o\n", st.st_mode & 0777);
        printf("\n");
    } else {
        printf("无法获取文件 %s 信息: %s\n", filename, strerror(errno));
    }
}

int main() {
    const char *original_file = "original.txt";
    const char *hard_link1 = "hardlink1.txt";
    const char *hard_link2 = "hardlink2.txt";
    
    printf("=== 硬链接基本使用演示 ===\n");
    
    // 创建原始文件
    int fd = open(original_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建原始文件失败");
        exit(EXIT_FAILURE);
    }
    
    const char *content = "这是硬链接测试文件的内容。\n包含多行文本。\n用于演示硬链接功能。";
    write(fd, content, strlen(content));
    close(fd);
    
    printf("✓ 创建原始文件: %s\n", original_file);
    
    // 显示原始文件信息
    print_file_info(original_file);
    
    // 使用 link 创建硬链接
    printf("使用 link() 创建硬链接...\n");
    if (link(original_file, hard_link1) == 0) {
        printf("✓ 成功创建硬链接: %s\n", hard_link1);
    } else {
        printf("✗ 创建硬链接失败: %s\n", strerror(errno));
        unlink(original_file);
        exit(EXIT_FAILURE);
    }
    
    // 显示链接后文件信息
    printf("创建硬链接后的文件信息:\n");
    print_file_info(original_file);
    print_file_info(hard_link1);
    
    // 验证硬链接是否工作正常
    printf("验证硬链接内容:\n");
    FILE *fp = fopen(hard_link1, "r");
    if (fp) {
        char buffer[256];
        if (fgets(buffer, sizeof(buffer), fp)) {
            printf("从硬链接读取内容: %s", buffer);
        }
        fclose(fp);
    }
    
    // 使用 linkat 创建第二个硬链接
    printf("\n使用 linkat() 创建硬链接...\n");
    if (linkat(AT_FDCWD, original_file, AT_FDCWD, hard_link2, 0) == 0) {
        printf("✓ 成功创建硬链接: %s\n", hard_link2);
    } else {
        printf("✗ 创建硬链接失败: %s\n", strerror(errno));
    }
    
    // 显示最终文件信息
    printf("最终文件信息:\n");
    print_file_info(original_file);
    print_file_info(hard_link1);
    print_file_info(hard_link2);
    
    // 验证所有文件共享相同 inode
    struct stat st_orig, st_link1, st_link2;
    if (stat(original_file, &st_orig) == 0 &&
        stat(hard_link1, &st_link1) == 0 &&
        stat(hard_link2, &st_link2) == 0) {
        
        if (st_orig.st_ino == st_link1.st_ino && st_orig.st_ino == st_link2.st_ino) {
            printf("✓ 验证成功:所有文件具有相同的 inode\n");
        } else {
            printf("✗ 验证失败:文件具有不同的 inode\n");
        }
    }
    
    // 清理测试文件
    unlink(original_file);
    unlink(hard_link1);
    unlink(hard_link2);
    
    return 0;
}

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

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

void test_link_errors(const char *oldpath, const char *newpath, const char *description) {
    printf("\n测试 %s:\n", description);
    printf("  源文件: %s\n", oldpath);
    printf("  目标文件: %s\n", newpath);
    
    int result = link(oldpath, newpath);
    if (result == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        switch (errno) {
            case EACCES:
                printf("    原因: 权限不足\n");
                break;
            case EEXIST:
                printf("    原因: 目标文件已存在\n");
                break;
            case ENOENT:
                printf("    原因: 源文件不存在\n");
                break;
            case EXDEV:
                printf("    原因: 跨文件系统操作\n");
                break;
            case EMLINK:
                printf("    原因: 硬链接数已达上限\n");
                break;
            case EROFS:
                printf("    原因: 文件系统为只读\n");
                break;
            case ELOOP:
                printf("    原因: 符号链接层级过深\n");
                break;
            default:
                printf("    原因: 其他错误\n");
                break;
        }
    } else {
        printf("  结果: 成功\n");
        // 清理创建的链接
        unlink(newpath);
    }
}

void test_linkat_errors(int olddirfd, const char *oldpath, 
                       int newdirfd, const char *newpath, 
                       int flags, const char *description) {
    printf("\n测试 %s:\n", description);
    printf("  源目录 fd: %d, 路径: %s\n", olddirfd, oldpath);
    printf("  目标目录 fd: %d, 路径: %s\n", newdirfd, newpath);
    printf("  标志: 0x%x\n", flags);
    
    int result = linkat(olddirfd, oldpath, newdirfd, newpath, flags);
    if (result == -1) {
        printf("  结果: 失败 - %s\n", strerror(errno));
        switch (errno) {
            case ENOTDIR:
                printf("    原因: 指定的文件描述符不是目录\n");
                break;
            default:
                // 复用 link 的错误处理
                break;
        }
    } else {
        printf("  结果: 成功\n");
        // 清理创建的链接
        if (newdirfd == AT_FDCWD) {
            unlink(newpath);
        }
    }
}

int main() {
    printf("=== link 和 linkat 错误处理测试 ===\n");
    
    // 创建测试文件
    const char *test_file = "test_file.txt";
    int fd = open(test_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        write(fd, "test content", 12);
        close(fd);
        printf("创建测试文件: %s\n", test_file);
    } else {
        printf("创建测试文件失败\n");
        return 1;
    }
    
    // 测试正常情况
    test_link_errors(test_file, "normal_link.txt", "正常硬链接创建");
    
    // 测试各种错误情况
    test_link_errors("nonexistent.txt", "link_to_nonexistent.txt", "源文件不存在");
    test_link_errors(test_file, test_file, "源文件和目标文件相同");
    
    // 创建已存在的目标文件
    fd = open("existing_target.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        close(fd);
        test_link_errors(test_file, "existing_target.txt", "目标文件已存在");
        unlink("existing_target.txt");
    }
    
    // 测试 linkat
    test_linkat_errors(AT_FDCWD, test_file, AT_FDCWD, "linkat_normal.txt", 0, "正常 linkat 调用");
    test_linkat_errors(-2, test_file, AT_FDCWD, "linkat_invalid_fd.txt", 0, "无效的目录文件描述符");
    
    // 创建子目录进行测试
    if (mkdir("test_dir", 0755) == 0) {
        int dirfd = open("test_dir", O_RDONLY);
        if (dirfd != -1) {
            test_linkat_errors(AT_FDCWD, test_file, dirfd, "subdir_link.txt", 0, "相对目录创建链接");
            close(dirfd);
        }
        rmdir("test_dir");
    }
    
    // 创建子进程测试(验证跨进程链接)
    pid_t child_pid = fork();
    if (child_pid == 0) {
        // 子进程
        printf("\n=== 子进程测试 ===\n");
        if (link(test_file, "child_process_link.txt") == 0) {
            printf("子进程成功创建硬链接\n");
            unlink("child_process_link.txt");
        } else {
            printf("子进程创建硬链接失败: %s\n", strerror(errno));
        }
        exit(0);
    } else if (child_pid > 0) {
        // 父进程等待子进程结束
        int status;
        waitpid(child_pid, &status, 0);
        printf("子进程测试完成\n");
    }
    
    // 清理测试文件
    unlink(test_file);
    
    return 0;
}

示例3:跨目录硬链接管理 链接到标题

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

typedef struct {
    char path[256];
    ino_t inode;
    nlink_t link_count;
} file_info_t;

int create_directory_structure() {
    // 创建测试目录结构
    const char *dirs[] = {"source_dir", "target_dir1", "target_dir2", "subdir"};
    
    for (int i = 0; i < 4; i++) {
        if (mkdir(dirs[i], 0755) == -1 && errno != EEXIST) {
            printf("创建目录 %s 失败: %s\n", dirs[i], strerror(errno));
            return -1;
        }
    }
    
    // 在子目录中也创建目标目录
    if (mkdir("source_dir/subdir", 0755) == -1 && errno != EEXIST) {
        printf("创建子目录失败: %s\n", strerror(errno));
        return -1;
    }
    
    if (mkdir("source_dir/subdir/target_subdir", 0755) == -1 && errno != EEXIST) {
        printf("创建目标子目录失败: %s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

int create_test_files() {
    // 在源目录创建测试文件
    const char *files[] = {
        "source_dir/file1.txt",
        "source_dir/file2.txt",
        "source_dir/subdir/subfile.txt"
    };
    
    const char *contents[] = {
        "Content of file 1",
        "Content of file 2", 
        "Content of subfile"
    };
    
    for (int i = 0; i < 3; i++) {
        int fd = open(files[i], O_CREAT | O_WRONLY | O_TRUNC, 0644);
        if (fd != -1) {
            write(fd, contents[i], strlen(contents[i]));
            close(fd);
            printf("创建测试文件: %s\n", files[i]);
        } else {
            printf("创建文件 %s 失败: %s\n", files[i], strerror(errno));
            return -1;
        }
    }
    
    return 0;
}

void print_file_details(const char *filepath) {
    struct stat st;
    if (stat(filepath, &st) == 0) {
        printf("  %s: inode=%ld, links=%ld, size=%ld\n",
               filepath, (long)st.st_ino, (long)st.st_nlink, (long)st.st_size);
    } else {
        printf("  %s: 无法获取信息 (%s)\n", filepath, strerror(errno));
    }
}

int demonstrate_cross_directory_links() {
    printf("=== 跨目录硬链接演示 ===\n");
    
    // 创建目录结构
    if (create_directory_structure() == -1) {
        return -1;
    }
    
    // 创建测试文件
    if (create_test_files() == -1) {
        return -1;
    }
    
    printf("\n原始文件状态:\n");
    print_file_details("source_dir/file1.txt");
    print_file_details("source_dir/file2.txt");
    print_file_details("source_dir/subdir/subfile.txt");
    
    // 使用 link 创建跨目录硬链接
    printf("\n使用 link() 创建跨目录硬链接:\n");
    
    if (link("source_dir/file1.txt", "target_dir1/file1_link.txt") == 0) {
        printf("✓ 成功创建: source_dir/file1.txt -> target_dir1/file1_link.txt\n");
    } else {
        printf("✗ 创建失败: %s\n", strerror(errno));
    }
    
    if (link("source_dir/file2.txt", "target_dir2/file2_link.txt") == 0) {
        printf("✓ 成功创建: source_dir/file2.txt -> target_dir2/file2_link.txt\n");
    } else {
        printf("✗ 创建失败: %s\n", strerror(errno));
    }
    
    // 使用 linkat 创建硬链接
    printf("\n使用 linkat() 创建硬链接:\n");
    
    // 打开目录文件描述符
    int source_fd = open("source_dir", O_RDONLY);
    int target1_fd = open("target_dir1", O_RDONLY);
    int target2_fd = open("target_dir2", O_RDONLY);
    
    if (source_fd != -1 && target1_fd != -1 && target2_fd != -1) {
        if (linkat(source_fd, "subdir/subfile.txt", target2_fd, "subfile_link.txt", 0) == 0) {
            printf("✓ 成功创建: source_dir/subdir/subfile.txt -> target_dir2/subfile_link.txt\n");
        } else {
            printf("✗ linkat 创建失败: %s\n", strerror(errno));
        }
    }
    
    // 显示链接后文件状态
    printf("\n链接后文件状态:\n");
    print_file_details("source_dir/file1.txt");
    print_file_details("target_dir1/file1_link.txt");
    print_file_details("source_dir/file2.txt");
    print_file_details("target_dir2/file2_link.txt");
    print_file_details("source_dir/subdir/subfile.txt");
    print_file_details("target_dir2/subfile_link.txt");
    
    // 验证 inode 是否相同
    struct stat st1_orig, st1_link, st2_orig, st2_link, st3_orig, st3_link;
    if (stat("source_dir/file1.txt", &st1_orig) == 0 &&
        stat("target_dir1/file1_link.txt", &st1_link) == 0) {
        if (st1_orig.st_ino == st1_link.st_ino) {
            printf("✓ file1.txt 和其链接具有相同 inode\n");
        }
    }
    
    // 验证内容是否相同
    printf("\n验证文件内容:\n");
    FILE *fp1 = fopen("source_dir/file1.txt", "r");
    FILE *fp2 = fopen("target_dir1/file1_link.txt", "r");
    if (fp1 && fp2) {
        char buf1[100], buf2[100];
        if (fgets(buf1, sizeof(buf1), fp1) && fgets(buf2, sizeof(buf2), fp2)) {
            if (strcmp(buf1, buf2) == 0) {
                printf("✓ 源文件和链接文件内容相同\n");
            } else {
                printf("✗ 文件内容不匹配\n");
            }
        }
        fclose(fp1);
        fclose(fp2);
    }
    
    // 清理资源
    if (source_fd != -1) close(source_fd);
    if (target1_fd != -1) close(target1_fd);
    if (target2_fd != -1) close(target2_fd);
    
    return 0;
}

void cleanup_test_files() {
    printf("\n清理测试文件...\n");
    
    // 删除硬链接
    unlink("target_dir1/file1_link.txt");
    unlink("target_dir2/file2_link.txt");
    unlink("target_dir2/subfile_link.txt");
    
    // 删除原始文件
    unlink("source_dir/file1.txt");
    unlink("source_dir/file2.txt");
    unlink("source_dir/subdir/subfile.txt");
    
    // 删除目录
    rmdir("source_dir/subdir/target_subdir");
    rmdir("source_dir/subdir");
    rmdir("source_dir");
    rmdir("target_dir1");
    rmdir("target_dir2");
    rmdir("subdir");
}

int main() {
    printf("=== 跨目录硬链接管理演示 ===\n");
    
    if (demonstrate_cross_directory_links() == 0) {
        printf("\n✓ 跨目录硬链接演示完成\n");
    } else {
        printf("\n✗ 演示过程中出现错误\n");
    }
    
    cleanup_test_files();
    return 0;
}

示例4:硬链接安全和监控工具 链接到标题

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

typedef struct {
    ino_t inode;
    char paths[10][256];  // 最多存储10个路径
    int path_count;
    off_t file_size;
    time_t last_modified;
} inode_info_t;

int find_hard_links(const char *directory, inode_info_t *inodes, int *inode_count) {
    DIR *dir = opendir(directory);
    if (!dir) {
        printf("无法打开目录 %s: %s\n", directory, strerror(errno));
        return -1;
    }
    
    struct dirent *entry;
    char filepath[512];
    
    while ((entry = readdir(dir)) != NULL) {
        // 跳过 . 和 ..
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        snprintf(filepath, sizeof(filepath), "%s/%s", directory, entry->d_name);
        
        // 如果是目录,递归处理
        if (entry->d_type == DT_DIR) {
            find_hard_links(filepath, inodes, inode_count);
            continue;
        }
        
        // 获取文件信息
        struct stat st;
        if (stat(filepath, &st) == 0) {
            // 查找是否已存在相同的 inode
            int found = 0;
            for (int i = 0; i < *inode_count; i++) {
                if (inodes[i].inode == st.st_ino) {
                    // 找到相同的 inode,添加路径
                    if (inodes[i].path_count < 10) {
                        strncpy(inodes[i].paths[inodes[i].path_count], filepath, 255);
                        inodes[i].paths[inodes[i].path_count][255] = '\0';
                        inodes[i].path_count++;
                    }
                    found = 1;
                    break;
                }
            }
            
            // 如果是新 inode,添加到列表
            if (!found && *inode_count < 1000) {
                inodes[*inode_count].inode = st.st_ino;
                strncpy(inodes[*inode_count].paths[0], filepath, 255);
                inodes[*inode_count].paths[0][255] = '\0';
                inodes[*inode_count].path_count = 1;
                inodes[*inode_count].file_size = st.st_size;
                inodes[*inode_count].last_modified = st.st_mtime;
                (*inode_count)++;
            }
        }
    }
    
    closedir(dir);
    return 0;
}

void show_hard_link_statistics(const char *directory) {
    printf("=== 硬链接统计分析 ===\n");
    printf("分析目录: %s\n", directory);
    
    inode_info_t inodes[1000];
    int inode_count = 0;
    
    if (find_hard_links(directory, inodes, &inode_count) == -1) {
        return;
    }
    
    printf("扫描完成,共发现 %d 个不同的 inode\n", inode_count);
    
    // 统计硬链接情况
    int files_with_links = 0;
    int total_hard_links = 0;
    int max_links = 1;
    
    printf("\n硬链接详情:\n");
    printf("%-15s %-10s %-8s %s\n", "inode", "链接数", "大小", "路径");
    printf("%-15s %-10s %-8s %s\n", "-----", "----", "----", "----");
    
    for (int i = 0; i < inode_count; i++) {
        if (inodes[i].path_count > 1) {
            files_with_links++;
            total_hard_links += inodes[i].path_count;
            
            if (inodes[i].path_count > max_links) {
                max_links = inodes[i].path_count;
            }
            
            printf("%-15ld %-10d %-8ld %s\n", 
                   (long)inodes[i].inode, 
                   inodes[i].path_count,
                   (long)inodes[i].file_size,
                   inodes[i].paths[0]);
            
            // 显示其他链接路径
            for (int j = 1; j < inodes[i].path_count; j++) {
                printf("%-15s %-10s %-8s %s\n", "", "", "", inodes[i].paths[j]);
            }
            printf("\n");
        }
    }
    
    printf("统计摘要:\n");
    printf("  具有硬链接的文件数: %d\n", files_with_links);
    printf("  硬链接总数: %d\n", total_hard_links);
    printf("  最大链接数: %d\n", max_links);
    printf("  平均链接数: %.2f\n", 
           files_with_links > 0 ? (double)total_hard_links / files_with_links : 0.0);
}

void demonstrate_link_security() {
    printf("\n=== 硬链接安全考虑 ===\n");
    
    printf("硬链接安全特性:\n");
    printf("  1. 所有链接共享相同的数据和权限\n");
    printf("  2. 删除任何一个链接不会影响其他链接\n");
    printf("  3. 只有当所有链接都被删除时,文件数据才被释放\n");
    printf("  4. 不能跨文件系统创建硬链接\n");
    printf("  5. 不能为目录创建硬链接(防止循环)\n");
    
    printf("\n安全建议:\n");
    printf("  • 监控异常的硬链接创建\n");
    printf("  • 限制用户创建硬链接的权限\n");
    printf("  • 定期检查硬链接使用情况\n");
    printf("  • 注意备份系统对硬链接的处理\n");
    printf("  • 防止通过硬链接绕过访问控制\n");
}

void interactive_link_manager() {
    char source[256], target[256];
    int choice;
    
    while (1) {
        printf("\n=== 硬链接管理工具 ===\n");
        printf("1. 创建硬链接 (link)\n");
        printf("2. 创建硬链接 (linkat)\n");
        printf("3. 删除硬链接\n");
        printf("4. 显示文件硬链接信息\n");
        printf("5. 分析目录硬链接统计\n");
        printf("0. 退出\n");
        printf("请选择操作: ");
        
        if (scanf("%d", &choice) != 1) {
            printf("输入无效\n");
            while (getchar() != '\n');  // 清空输入缓冲区
            continue;
        }
        
        switch (choice) {
            case 1:
                printf("输入源文件路径: ");
                scanf("%255s", source);
                printf("输入目标链接路径: ");
                scanf("%255s", target);
                
                if (link(source, target) == 0) {
                    printf("✓ 成功创建硬链接\n");
                } else {
                    printf("✗ 创建硬链接失败: %s\n", strerror(errno));
                }
                break;
                
            case 2:
                printf("输入源文件路径: ");
                scanf("%255s", source);
                printf("输入目标链接路径: ");
                scanf("%255s", target);
                
                if (linkat(AT_FDCWD, source, AT_FDCWD, target, 0) == 0) {
                    printf("✓ 成功创建硬链接 (linkat)\n");
                } else {
                    printf("✗ 创建硬链接失败: %s\n", strerror(errno));
                }
                break;
                
            case 3:
                printf("输入要删除的链接路径: ");
                scanf("%255s", target);
                
                if (unlink(target) == 0) {
                    printf("✓ 成功删除链接\n");
                } else {
                    printf("✗ 删除链接失败: %s\n", strerror(errno));
                }
                break;
                
            case 4: {
                printf("输入文件路径: ");
                scanf("%255s", source);
                
                struct stat st;
                if (stat(source, &st) == 0) {
                    printf("文件信息:\n");
                    printf("  inode: %ld\n", (long)st.st_ino);
                    printf("  硬链接数: %ld\n", (long)st.st_nlink);
                    printf("  大小: %ld 字节\n", (long)st.st_size);
                    printf("  最后修改: %s", ctime(&st.st_mtime));
                } else {
                    printf("获取文件信息失败: %s\n", strerror(errno));
                }
                break;
            }
            
            case 5:
                printf("输入要分析的目录路径: ");
                scanf("%255s", source);
                show_hard_link_statistics(source);
                break;
                
            case 0:
                printf("退出硬链接管理工具\n");
                return;
                
            default:
                printf("无效选择\n");
                break;
        }
    }
}

int main() {
    printf("=== 硬链接安全和监控工具 ===\n");
    
    // 演示安全特性
    demonstrate_link_security();
    
    // 显示当前目录的硬链接统计
    show_hard_link_statistics(".");
    
    // 启动交互式管理器
    char choice;
    printf("\n是否启动交互式硬链接管理器? (y/N): ");
    if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
        interactive_link_manager();
    }
    
    return 0;
}

9. 硬链接特性说明 链接到标题

// 硬链接的重要特性:

// 1. 相同 inode
// 所有硬链接指向相同的 inode,共享文件数据

// 2. 相同元数据
// 权限、所有者、时间戳等元数据完全相同

// 3. 独立存在
// 删除任何一个链接不影响其他链接

// 4. 计数机制
// st_nlink 字段记录硬链接数量

// 5. 限制条件
// - 不能跨文件系统
// - 不能链接到目录
// - 需要写权限

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

场景1:文件备份和版本管理 链接到标题

void backup_with_hardlinks() {
    // 使用硬链接创建快速备份
    link("important_file.txt", "backup/important_file.txt");
    // 节省空间,只有修改时才占用额外空间
}

场景2:快照功能 链接到标题

void filesystem_snapshot() {
    // 创建目录快照
    // linkat 可以用于创建大量硬链接的快照
}

场景3:文件去重 链接到标题

void deduplicate_files() {
    // 通过比较 inode 来识别和删除重复文件
    // 保留一个链接,删除其他链接
}

11. 注意事项 链接到标题

使用硬链接时需要注意:

  1. 文件系统限制: 不能跨文件系统创建硬链接
  2. 目录限制: 不能为目录创建硬链接
  3. 权限要求: 需要对目标目录有写权限
  4. 链接数限制: 每个文件的硬链接数有上限
  5. 安全考虑: 硬链接可能被用于绕过访问控制
  6. 备份影响: 备份工具需要正确处理硬链接

12. 与符号链接的区别 链接到标题

// 硬链接 vs 符号链接:

// 硬链接 (link/linkat):
// • 指向相同的 inode
// • 文件系统级别链接
// • 不能跨文件系统
// • 不能链接到目录
// • 删除源文件不影响链接

// 符号链接 (symlink/symlinkat):
// • 包含路径名的特殊文件
// • 可以跨文件系统
// • 可以链接到目录
// • 可能出现悬空链接

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

# 检查文件系统对硬链接的支持
df -T

# 查看文件的硬链接数
ls -l filename

# 查看文件的 inode 信息
stat filename

# 查找具有多个硬链接的文件
find /path -links +1

总结 链接到标题

linklinkat 是 Linux 系统中创建硬链接的重要系统调用:

关键特性:

  1. 数据共享: 所有硬链接共享相同的数据和元数据
  2. 独立存在: 删除任何一个链接不影响其他链接
  3. 空间效率: 不占用额外的磁盘空间
  4. 灵活接口: linkat 提供相对路径和文件描述符支持

主要应用:

  1. 文件备份和快照
  2. 空间优化和去重
  3. 版本管理和原子操作
  4. 文件系统管理工具

使用要点:

  1. 理解硬链接的基本概念和限制
  2. 正确处理各种错误情况
  3. 注意安全和权限问题
  4. 合理使用 linkat 的扩展功能

正确使用硬链接可以显著提高文件管理的效率和灵活性,是 Linux 系统编程中的重要技术。