自制ls -l工具触发段错误,请求问题排查
自定义ls -l段错误排查与修复
核心问题与修复点
未初始化的stat结构体引发非法访问
函数开头声明的struct stat stat完全未初始化,直接用它的st_uid调用getpwuid,得到的pw和grp大概率是NULL,后续访问grp->gr_name直接触发段错误。而且每个文件的属性需要单独通过stat系统调用获取,不能复用垃圾值结构体。未校验opendir返回值
如果当前目录打开失败(比如权限不足),opendir会返回NULL,后续readdir(NULL)直接崩溃,必须先检查dir是否有效。缺少每个文件的stat调用
循环里直接用未初始化的stat数据,根本没获取当前文件的实际属性,所有属性值都是内存垃圾,访问这些值必然导致未定义行为。必须在循环内对每个entry->d_name调用stat获取属性。printf格式符类型不匹配
stat.st_nlink和stat.st_size是整数,却用%s输出——这会把整数当作字符串指针解析,访问非法内存地址触发段错误,要用%d/%ld输出整数。错误访问dirent的无效成员
entry->d_name_name是不存在的字段,你应该是想输出文件所有者的用户名,也就是pw->pw_name。用户/组信息调用时机错误
getpwuid和getgrgid要在获取到当前文件的有效stat数据之后调用,不能一开始就用垃圾值调用。
修复后的完整代码
#include <stdio.h> #include <dirent.h> #include <sys/stat.h> #include <pwd.h> #include <grp.h> #include <time.h> #include <string.h> // 假设你已经实现了这两个自定义函数 void mini_printf(const char *fmt, ...); void my_putchar(char c); void get_time(char *str) { if (!str) return; // ctime返回格式为"Wed Jun 30 21:49:08 1993\n",截取到第16位截断 if (strlen(str) >= 16) { str[16] = '\0'; str += 4; mini_printf("%s", str); } } void get_permission(const struct stat *stat_buf) { if (!stat_buf) return; my_putchar((S_ISDIR(stat_buf->st_mode)) ? 'd' : (S_ISBLK(stat_buf->st_mode) ? 'b' : (S_ISCHR(stat_buf->st_mode) ? 'c' : '-'))); my_putchar((stat_buf->st_mode & S_IRUSR) ? 'r' : '-'); my_putchar((stat_buf->st_mode & S_IWUSR) ? 'w' : '-'); my_putchar((stat_buf->st_mode & S_IXUSR) ? 'x' : '-'); my_putchar((stat_buf->st_mode & S_IRGRP) ? 'r' : '-'); my_putchar((stat_buf->st_mode & S_IWGRP) ? 'w' : '-'); my_putchar((stat_buf->st_mode & S_IXGRP) ? 'x' : '-'); my_putchar((stat_buf->st_mode & S_IROTH) ? 'r' : '-'); my_putchar((stat_buf->st_mode & S_IWOTH) ? 'w' : '-'); my_putchar((stat_buf->st_mode & S_IXOTH) ? 'x' : (stat_buf->st_mode & S_ISVTX) ? 'T' : '-'); } int my_ls_l(void) { struct dirent *entry; DIR *dir; struct stat stat_buf; struct passwd *pw; struct group *grp; dir = opendir("./"); if (!dir) { mini_printf("opendir failed\n"); return -1; } while ((entry = readdir(dir)) != NULL) { // 跳过隐藏文件 if (entry->d_name[0] == '.') { continue; } // 获取当前文件的属性信息 if (stat(entry->d_name, &stat_buf) == -1) { mini_printf("stat failed for %s\n", entry->d_name); continue; } // 获取用户和组信息 pw = getpwuid(stat_buf.st_uid); grp = getgrgid(stat_buf.st_gid); // 输出权限位 get_permission(&stat_buf); mini_printf(" "); // 输出链接数 mini_printf("%d ", stat_buf.st_nlink); // 输出用户名(如果获取失败则输出UID) if (pw) { mini_printf("%s ", pw->pw_name); } else { mini_printf("%d ", stat_buf.st_uid); } // 输出组名(如果获取失败则输出GID) if (grp) { mini_printf("%s ", grp->gr_name); } else { mini_printf("%d ", stat_buf.st_gid); } // 输出文件大小 mini_printf("%ld ", (long)stat_buf.st_size); // 输出修改时间 get_time(ctime(&stat_buf.st_mtime)); mini_printf(" "); // 输出文件名 mini_printf("%s\n", entry->d_name); } closedir(dir); return 0; } int main(void) { my_ls_l(); return 0; }
内容的提问来源于stack exchange,提问作者owen.H
相关产品推荐
相关产品推荐

