99. lstat - 获取文件状态信息(不跟随符号链接) 見出しへのリンク
1. 函数介绍 見出しへのリンク
lstat
是一个 Linux 系统调用,用于获取文件的状态信息,但与 stat
不同的是,当遇到符号链接时,lstat
返回符号链接本身的信息,而不是跟随符号链接获取目标文件的信息。
2. 函数原型 見出しへのリンク
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int lstat(const char *pathname, struct stat *statbuf);
3. 功能 見出しへのリンク
获取指定路径名的文件状态信息。如果路径指向符号链接,lstat
返回符号链接本身的信息,而不是目标文件的信息。
4. 参数 見出しへのリンク
const char *pathname
: 文件路径名struct stat *statbuf
: 指向stat
结构体的指针,用于存储文件状态信息
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 错误码 見出しへのリンク
ENOENT
: 指定的文件或路径不存在EACCES
: 权限不足ELOOP
: 符号链接层级过深ENAMETOOLONG
: 路径名过长EFAULT
: 路径名指针指向无效地址ENOMEM
: 内存不足ENOTDIR
: 路径中的某个组件不是目录EOVERFLOW
: 文件大小、inode 号或链接数超出范围
8. 相似函数,或关联函数 見出しへのリンク
stat()
: 获取文件状态信息(跟随符号链接)fstat()
: 通过文件描述符获取文件状态信息fstatat()
: 相对于目录文件描述符获取文件状态readlink()
,readlinkat()
: 读取符号链接内容symlink()
,symlinkat()
: 创建符号链接/proc/[pid]/fd/
: 查看进程打开的文件描述符
9. 示例代码 見出しへのリンク
示例1:基本使用 - 符号链接状态对比 見出しへのリンク
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
void print_file_type(mode_t mode) {
if (S_ISREG(mode)) {
printf("普通文件");
} else if (S_ISDIR(mode)) {
printf("目录");
} else if (S_ISLNK(mode)) {
printf("符号链接");
} else if (S_ISCHR(mode)) {
printf("字符设备");
} else if (S_ISBLK(mode)) {
printf("块设备");
} else if (S_ISFIFO(mode)) {
printf("FIFO/管道");
} else if (S_ISSOCK(mode)) {
printf("套接字");
} else {
printf("未知类型");
}
}
void print_file_permissions(mode_t mode) {
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" : "-");
}
void print_stat_info(const char *filename, const struct stat *st, const char *label) {
printf("\n=== %s: %s ===\n", label, filename);
printf("设备 ID: %ld\n", (long)st->st_dev);
printf("inode 号: %ld\n", (long)st->st_ino);
printf("文件类型: ");
print_file_type(st->st_mode);
printf("\n");
printf("权限: ");
print_file_permissions(st->st_mode);
printf("\n");
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() {
const char *target_file = "target.txt";
const char *symlink_name = "symlink_to_target";
printf("=== lstat vs stat 符号链接对比演示 ===\n");
// 创建目标文件
int fd = open(target_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建目标文件失败");
exit(EXIT_FAILURE);
}
const char *content = "This is the target file content.\nIt has multiple lines.\nFor testing lstat vs stat.";
write(fd, content, strlen(content));
close(fd);
printf("创建目标文件: %s (大小: %ld 字节)\n", target_file, (long)strlen(content));
// 创建符号链接
if (symlink(target_file, symlink_name) == -1) {
perror("创建符号链接失败");
unlink(target_file);
exit(EXIT_FAILURE);
}
printf("创建符号链接: %s -> %s\n", symlink_name, target_file);
// 使用 stat 获取目标文件信息(跟随符号链接)
struct stat stat_info;
if (stat(symlink_name, &stat_info) == 0) {
print_stat_info(symlink_name, &stat_info, "stat 结果(跟随符号链接)");
printf("注意: 这显示的是目标文件的信息\n");
} else {
printf("stat 调用失败: %s\n", strerror(errno));
}
// 使用 lstat 获取符号链接信息(不跟随符号链接)
struct stat lstat_info;
if (lstat(symlink_name, &lstat_info) == 0) {
print_stat_info(symlink_name, &lstat_info, "lstat 结果(不跟随符号链接)");
printf("注意: 这显示的是符号链接本身的信息\n");
} else {
printf("lstat 调用失败: %s\n", strerror(errno));
}
// 直接获取目标文件信息进行对比
if (stat(target_file, &stat_info) == 0) {
print_stat_info(target_file, &stat_info, "目标文件直接 stat 结果");
}
// 验证 inode 号码
printf("\n=== inode 号码对比 ===\n");
printf("目标文件 inode: %ld\n", (long)stat_info.st_ino);
printf("符号链接 inode (stat): %ld\n", (long)stat_info.st_ino); // 应该相同
printf("符号链接 inode (lstat): %ld\n", (long)lstat_info.st_ino); // 应该不同
if (stat_info.st_ino == lstat_info.st_ino) {
printf("⚠ 警告: inode 相同,可能文件系统不支持符号链接\n");
} else {
printf("✓ 正常: 符号链接和目标文件有不同的 inode\n");
}
// 清理测试文件
unlink(symlink_name);
unlink(target_file);
return 0;
}
示例2:符号链接深度和循环检测 見出しへのリンク
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
void test_symlink_loops() {
printf("=== 符号链接循环检测 ===\n");
// 创建循环符号链接
if (symlink("loop1", "loop2") == 0 && symlink("loop2", "loop1") == 0) {
printf("创建循环符号链接: loop1 -> loop2 -> loop1\n");
// 测试 stat(会失败)
struct stat st;
printf("测试 stat (loop1):\n");
if (stat("loop1", &st) == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
if (errno == ELOOP) {
printf(" 原因: 检测到符号链接循环\n");
}
}
// 测试 lstat(应该成功)
printf("测试 lstat (loop1):\n");
if (lstat("loop1", &st) == 0) {
printf(" 结果: 成功\n");
printf(" 文件类型: ");
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);
} else {
printf(" 结果: 失败 - %s\n", strerror(errno));
}
// 清理
unlink("loop1");
unlink("loop2");
}
}
void test_symlink_chains() {
printf("\n=== 符号链接链测试 ===\n");
// 创建符号链接链
const char *files[] = {"file1", "file2", "file3", "file4"};
const char *links[] = {"link1", "link2", "link3"};
// 创建目标文件
int fd = open("file1", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "Original file content", 21);
close(fd);
printf("创建目标文件: file1\n");
}
// 创建符号链接链
for (int i = 0; i < 3; i++) {
if (symlink(files[i], links[i]) == 0) {
printf("创建符号链接: %s -> %s\n", links[i], files[i]);
}
}
// 测试不同深度的符号链接
printf("\n测试不同深度的符号链接:\n");
for (int i = 0; i <= 3; i++) {
struct stat st_stat, st_lstat;
char filename[16];
if (i == 0) {
strcpy(filename, "file1");
} else {
snprintf(filename, sizeof(filename), "link%d", i);
}
printf("深度 %d (%s):\n", i, filename);
// stat 测试
if (stat(filename, &st_stat) == 0) {
printf(" stat: inode=%ld, size=%ld\n",
(long)st_stat.st_ino, (long)st_stat.st_size);
} else {
printf(" stat: 失败 - %s\n", strerror(errno));
}
// lstat 测试
if (lstat(filename, &st_lstat) == 0) {
printf(" lstat: inode=%ld, size=%ld, type=",
(long)st_lstat.st_ino, (long)st_lstat.st_size);
if (S_ISLNK(st_lstat.st_mode)) {
printf("符号链接\n");
} else {
printf("普通文件\n");
}
} else {
printf(" lstat: 失败 - %s\n", strerror(errno));
}
}
// 清理测试文件
unlink("file1");
unlink("link1");
unlink("link2");
unlink("link3");
}
int main() {
printf("=== 符号链接深度和循环检测演示 ===\n");
test_symlink_loops();
test_symlink_chains();
return 0;
}
示例3:错误处理和边界情况 見出しへのリンク
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
void test_lstat_errors(const char *pathname, const char *description) {
printf("\n测试 %s:\n", description);
printf(" 路径: %s\n", pathname);
struct stat st;
int result = lstat(pathname, &st);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
switch (errno) {
case ENOENT:
printf(" 原因: 文件或目录不存在\n");
break;
case EACCES:
printf(" 原因: 权限不足\n");
break;
case ENAMETOOLONG:
printf(" 原因: 路径名过长\n");
break;
case ELOOP:
printf(" 原因: 符号链接层级过深\n");
break;
case ENOTDIR:
printf(" 原因: 路径中的某个组件不是目录\n");
break;
case EFAULT:
printf(" 原因: 路径指针无效\n");
break;
case EIO:
printf(" 原因: I/O 错误\n");
break;
default:
printf(" 原因: 其他错误\n");
break;
}
} else {
printf(" 结果: 成功\n");
printf(" inode: %ld\n", (long)st.st_ino);
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");
}
}
int main() {
printf("=== lstat 错误处理测试 ===\n");
// 创建测试文件
const char *test_file = "error_test.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);
}
// 创建测试符号链接
const char *test_symlink = "error_test_symlink";
if (symlink(test_file, test_symlink) == 0) {
printf("创建测试符号链接: %s -> %s\n", test_symlink, test_file);
}
// 测试正常情况
test_lstat_errors(test_file, "正常文件");
test_lstat_errors(test_symlink, "正常符号链接");
// 测试各种错误情况
test_lstat_errors("nonexistent_file.txt", "不存在的文件");
test_lstat_errors("", "空路径");
// 测试过长路径名
char long_path[PATH_MAX + 10];
memset(long_path, 'a', sizeof(long_path) - 1);
long_path[sizeof(long_path) - 1] = '\0';
test_lstat_errors(long_path, "过长路径名");
// 测试无效路径组件
test_lstat_errors("/nonexistent_dir/file.txt", "无效路径组件");
// 测试 NULL 指针
printf("\n测试 NULL 指针:\n");
int result = lstat(test_file, NULL);
if (result == -1) {
printf(" 结果: 失败 - %s\n", strerror(errno));
if (errno == EFAULT) {
printf(" 原因: statbuf 指针无效\n");
}
}
// 测试特殊文件类型
printf("\n=== 特殊文件类型测试 ===\n");
printf("当前目录:\n");
struct stat st;
if (lstat(".", &st) == 0) {
printf(" 类型: ");
if (S_ISDIR(st.st_mode)) printf("目录\n");
printf(" inode: %ld\n", (long)st.st_ino);
}
printf("父目录:\n");
if (lstat("..", &st) == 0) {
printf(" 类型: ");
if (S_ISDIR(st.st_mode)) printf("目录\n");
printf(" inode: %ld\n", (long)st.st_ino);
}
// 清理测试文件
unlink(test_symlink);
unlink(test_file);
return 0;
}
示例4:符号链接信息分析工具 見出しへのリンク
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <time.h>
typedef struct {
char path[256];
ino_t inode;
off_t size;
time_t mtime;
int is_symlink;
char target[256];
} file_info_t;
void analyze_symlink(const char *filename) {
printf("=== 符号链接分析: %s ===\n", filename);
// 获取符号链接本身的信息
struct stat lstat_info;
if (lstat(filename, &lstat_info) == -1) {
printf("获取符号链接信息失败: %s\n", strerror(errno));
return;
}
printf("符号链接信息:\n");
printf(" inode: %ld\n", (long)lstat_info.st_ino);
printf(" 大小: %ld 字节\n", (long)lstat_info.st_size);
printf(" 权限: %o\n", lstat_info.st_mode & 0777);
printf(" 创建时间: %s", ctime(&lstat_info.st_ctime));
printf(" 修改时间: %s", ctime(&lstat_info.st_mtime));
// 读取符号链接目标
char target[256];
ssize_t len = readlink(filename, target, sizeof(target) - 1);
if (len != -1) {
target[len] = '\0';
printf(" 目标: %s\n", target);
// 检查目标是否存在
struct stat target_stat;
if (stat(filename, &target_stat) == 0) {
printf(" 目标状态: 存在\n");
printf(" 目标 inode: %ld\n", (long)target_stat.st_ino);
printf(" 目标大小: %ld 字节\n", (long)target_stat.st_size);
// 检查是否指向自身(循环)
if (target_stat.st_ino == lstat_info.st_ino) {
printf(" ⚠ 警告: 符号链接可能指向自身\n");
}
} else {
printf(" 目标状态: 不存在 (%s)\n", strerror(errno));
if (errno == ENOENT) {
printf(" ⚠ 警告: 悬空符号链接\n");
}
}
} else {
printf(" 读取目标失败: %s\n", strerror(errno));
}
// 比较 stat 和 lstat 结果
struct stat stat_info;
if (stat(filename, &stat_info) == 0) {
printf("\n对比信息:\n");
printf(" lstat inode: %ld\n", (long)lstat_info.st_ino);
printf(" stat inode: %ld\n", (long)stat_info.st_ino);
printf(" inode 相同: %s\n",
(lstat_info.st_ino == stat_info.st_ino) ? "是" : "否");
}
}
void scan_directory_for_symlinks(const char *directory) {
printf("=== 扫描目录中的符号链接: %s ===\n", directory);
DIR *dir = opendir(directory);
if (!dir) {
printf("无法打开目录: %s\n", strerror(errno));
return;
}
struct dirent *entry;
char filepath[512];
int symlink_count = 0;
int dangling_count = 0;
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);
// 检查是否为符号链接
struct stat st;
if (lstat(filepath, &st) == 0 && S_ISLNK(st.st_mode)) {
symlink_count++;
printf("符号链接: %s\n", entry->d_name);
// 检查目标是否存在
if (stat(filepath, &st) == -1) {
if (errno == ENOENT) {
printf(" ⚠ 悬空符号链接\n");
dangling_count++;
} else {
printf(" 无法访问目标: %s\n", strerror(errno));
}
}
// 显示符号链接目标
char target[256];
ssize_t len = readlink(filepath, target, sizeof(target) - 1);
if (len != -1) {
target[len] = '\0';
printf(" 目标: %s\n", target);
}
printf("\n");
}
}
closedir(dir);
printf("扫描结果:\n");
printf(" 符号链接总数: %d\n", symlink_count);
printf(" 悬空符号链接: %d\n", dangling_count);
}
void interactive_symlink_analyzer() {
char filename[256];
int choice;
while (1) {
printf("\n=== 符号链接分析工具 ===\n");
printf("1. 分析单个符号链接\n");
printf("2. 扫描目录中的符号链接\n");
printf("3. 创建测试符号链接\n");
printf("4. 检查符号链接循环\n");
printf("0. 退出\n");
printf("请选择操作: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效\n");
while (getchar() != '\n'); // 清空输入缓冲区
continue;
}
switch (choice) {
case 1:
printf("输入符号链接路径: ");
scanf("%255s", filename);
analyze_symlink(filename);
break;
case 2:
printf("输入目录路径: ");
scanf("%255s", filename);
scan_directory_for_symlinks(filename);
break;
case 3: {
char target[256], linkname[256];
printf("输入目标文件名: ");
scanf("%255s", target);
printf("输入符号链接名: ");
scanf("%255s", linkname);
if (symlink(target, linkname) == 0) {
printf("✓ 成功创建符号链接: %s -> %s\n", linkname, target);
} else {
printf("✗ 创建符号链接失败: %s\n", strerror(errno));
}
break;
}
case 4: {
char link1[256], link2[256];
printf("输入第一个符号链接名: ");
scanf("%255s", link1);
printf("输入第二个符号链接名: ");
scanf("%255s", link2);
// 创建循环符号链接
if (symlink(link2, link1) == 0 && symlink(link1, link2) == 0) {
printf("✓ 创建循环符号链接: %s <-> %s\n", link1, link2);
printf("测试循环检测:\n");
struct stat st;
if (stat(link1, &st) == -1 && errno == ELOOP) {
printf("✓ 成功检测到循环\n");
}
} else {
printf("✗ 创建循环符号链接失败: %s\n", strerror(errno));
}
break;
}
case 0:
printf("退出符号链接分析工具\n");
return;
default:
printf("无效选择\n");
break;
}
}
}
int main() {
printf("=== 符号链接信息分析工具 ===\n");
// 显示系统信息
printf("系统信息:\n");
system("uname -a");
// 启动交互式分析器
char choice;
printf("\n是否启动交互式符号链接分析器? (y/N): ");
if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
interactive_symlink_analyzer();
}
return 0;
}
10. stat 函数族对比 見出しへのリンク
// stat 函数族的区别:
// stat(path, buf)
// • 跟随符号链接
// • 返回目标文件信息
// • 如果符号链接悬空则失败
// lstat(path, buf)
// • 不跟随符号链接
// • 返回符号链接本身信息
// • 即使符号链接悬空也成功
// fstat(fd, buf)
// • 通过文件描述符操作
// • 不涉及路径解析
// • 最安全的方式
// fstatat(dirfd, path, buf, flags)
// • 相对于目录文件描述符
// • flags 参数控制是否跟随符号链接
11. 实际应用场景 見出しへのリンク
场景1:备份工具符号链接处理 見出しへのリンク
void backup_symlink_handling(const char *filepath) {
struct stat st;
// 使用 lstat 检查是否为符号链接
if (lstat(filepath, &st) == 0) {
if (S_ISLNK(st.st_mode)) {
// 处理符号链接
char target[256];
ssize_t len = readlink(filepath, target, sizeof(target) - 1);
if (len != -1) {
target[len] = '\0';
// 备份符号链接本身,而不是目标文件
printf("备份符号链接: %s -> %s\n", filepath, target);
}
} else {
// 处理普通文件
printf("备份普通文件: %s\n", filepath);
}
}
}
场景2:文件系统清理工具 見出しへのリンク
void cleanup_dangling_symlinks(const char *directory) {
DIR *dir = opendir(directory);
if (!dir) return;
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);
struct stat lst;
if (lstat(filepath, &lst) == 0 && S_ISLNK(lst.st_mode)) {
// 检查符号链接是否悬空
struct stat st;
if (stat(filepath, &st) == -1 && errno == ENOENT) {
printf("删除悬空符号链接: %s\n", filepath);
unlink(filepath);
}
}
}
closedir(dir);
}
场景3:安全审计工具 見出しへのリンク
int audit_symlink_security(const char *filepath) {
struct stat lst;
if (lstat(filepath, &lst) == 0) {
if (S_ISLNK(lst.st_mode)) {
// 符号链接可能的安全风险
printf("发现符号链接: %s\n", filepath);
// 检查权限
if ((lst.st_mode & 0777) != 0777) {
printf(" 警告: 符号链接权限异常\n");
return 1;
}
// 检查目标
char target[256];
ssize_t len = readlink(filepath, target, sizeof(target) - 1);
if (len != -1) {
target[len] = '\0';
// 检查目标是否在安全路径内
if (strstr(target, "..") != NULL) {
printf(" 警告: 符号链接目标包含 .. 路径遍历\n");
return 1;
}
}
}
}
return 0;
}
12. 注意事项 見出しへのリンク
使用 lstat
时需要注意:
- 符号链接处理: 明确理解不跟随符号链接的行为
- 悬空符号链接:
lstat
对悬空符号链接仍然成功 - 权限检查: 需要对路径中的所有目录有执行权限
- 循环检测: 系统会自动检测符号链接循环
- 文件类型判断: 正确使用
S_ISLNK()
宏判断符号链接 - 错误处理: 仔细处理各种可能的错误情况
13. 系统配置检查 見出しへのリンク
# 查看文件系统类型
df -T
# 查找系统中的符号链接
find /path -type l
# 查看符号链接详细信息
ls -la filename
# 统计目录中的符号链接数量
find /path -type l | wc -l
# 查找悬空符号链接
find /path -type l -exec test ! -e {} \; -print
总结 見出しへのリンク
lstat
是处理符号链接的重要系统调用:
关键特性:
- 符号链接专用: 不跟随符号链接,返回链接本身信息
- 悬空链接处理: 即使目标不存在也能成功获取信息
- 安全相关: 在安全审计和权限检查中很有用
- 系统管理: 文件系统管理和清理工具的基础
主要应用:
- 备份工具符号链接处理
- 文件系统清理和维护
- 安全审计和权限检查
- 符号链接分析和管理工具
使用要点:
- 理解与
stat
的区别(符号链接处理方式) - 正确判断文件类型(使用
S_ISLNK()
宏) - 处理悬空符号链接的情况
- 配合
readlink()
获取符号链接目标
正确使用 lstat
可以帮助程序准确处理符号链接,避免因路径解析错误导致的问题。