Linux文件属性解析与ls -l命令实现
Linux文件属性解析与ls -l命令实现
1. 项目概述
1.1 系统架构
本项目通过分析Linux系统中ls -l命令的实现原理,深入探讨文件属性信息的获取与解析方法。系统主要分为三个功能模块:
- 目录读取模块:负责遍历目录内容
- 属性获取模块:通过系统调用获取文件元数据
- 信息转换模块:将数字形式的属性转换为可读字符串
1.2 开发环境
- 操作系统:Ubuntu 18.04 LTS
- 编译器:gcc 7.5.0
- 开发语言:C语言
2. 文件属性系统调用
2.1 stat()函数解析
获取文件属性的核心系统调用是stat(),其函数原型如下:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *pathname, struct stat *statbuf);参数说明:
pathname:目标文件路径statbuf:用于存储文件属性的结构体指针
返回值:
- 成功返回0
- 失败返回-1并设置errno
2.2 stat结构体详解
struct 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块数 */ struct timespec st_atim; /* 最后访问时间 */ struct timespec st_mtim; /* 最后修改时间 */ struct timespec st_ctim; /* 最后状态变更时间 */ };3. 文件类型与权限解析
3.1 文件类型编码
st_mode字段的前4位表示文件类型,常用类型掩码定义如下:
#define S_IFMT 0170000 /* 文件类型掩码 */ #define S_IFSOCK 0140000 /* 套接字 */ #define S_IFLNK 0120000 /* 符号链接 */ #define S_IFREG 0100000 /* 普通文件 */ #define S_IFBLK 0060000 /* 块设备 */ #define S_IFDIR 0040000 /* 目录 */ #define S_IFCHR 0020000 /* 字符设备 */ #define S_IFIFO 0010000 /* 管道/FIFO */3.2 权限位解析
st_mode的低9位表示文件权限,分为三组:
- 所有者权限(位8-6)
- 组权限(位5-3)
- 其他用户权限(位2-0)
每组包含读(r)、写(w)、执行(x)三种权限,对应的掩码:
#define S_IRWXU 00700 /* 所有者权限掩码 */ #define S_IRUSR 00400 /* 所有者读权限 */ #define S_IWUSR 00200 /* 所有者写权限 */ #define S_IXUSR 00100 /* 所有者执行权限 */ #define S_IRWXG 00070 /* 组权限掩码 */ #define S_IRGRP 00040 /* 组读权限 */ #define S_IWGRP 00020 /* 组写权限 */ #define S_IXGRP 00010 /* 组执行权限 */ #define S_IRWXO 00007 /* 其他用户权限掩码 */ #define S_IROTH 00004 /* 其他用户读权限 */ #define S_IWOTH 00002 /* 其他用户写权限 */ #define S_IXOTH 00001 /* 其他用户执行权限 */3.3 权限转换实现
将st_mode转换为ls -l风格的字符串表示:
void mode_to_letters(int mode, char str[]) { strcpy(str, "----------"); /* 文件类型 */ if (S_ISDIR(mode)) str[0] = 'd'; if (S_ISCHR(mode)) str[0] = 'c'; if (S_ISBLK(mode)) str[0] = 'b'; if (S_ISLNK(mode)) str[0] = 'l'; /* 所有者权限 */ if (mode & S_IRUSR) str[1] = 'r'; if (mode & S_IWUSR) str[2] = 'w'; if (mode & S_IXUSR) str[3] = 'x'; /* 组权限 */ if (mode & S_IRGRP) str[4] = 'r'; if (mode & S_IWGRP) str[5] = 'w'; if (mode & S_IXGRP) str[6] = 'x'; /* 其他用户权限 */ if (mode & S_IROTH) str[7] = 'r'; if (mode & S_IWOTH) str[8] = 'w'; if (mode & S_IXOTH) str[9] = 'x'; }4. 用户与组信息获取
4.1 用户信息查询
通过getpwuid()获取用户信息:
#include <pwd.h> struct passwd *getpwuid(uid_t uid); struct passwd { char *pw_name; /* 用户名 */ char *pw_passwd; /* 用户密码 */ uid_t pw_uid; /* 用户ID */ gid_t pw_gid; /* 组ID */ char *pw_gecos; /* 用户信息 */ char *pw_dir; /* 主目录 */ char *pw_shell; /* shell程序 */ };4.2 组信息查询
通过getgrgid()获取组信息:
#include <grp.h> struct group *getgrgid(gid_t gid); struct group { char *gr_name; /* 组名 */ char *gr_passwd; /* 组密码 */ gid_t gr_gid; /* 组ID */ char **gr_mem; /* 组成员列表 */ };4.3 ID到名称转换实现
将用户ID和组ID转换为名称字符串:
char *uid_to_name(uid_t uid) { struct passwd *pw_ptr; static char numstr[10]; if ((pw_ptr = getpwuid(uid)) == NULL) { sprintf(numstr, "%d", uid); return numstr; } else { return pw_ptr->pw_name; } } char *gid_to_name(gid_t gid) { struct group *grp_ptr; static char numstr[10]; if ((grp_ptr = getgrgid(gid)) == NULL) { sprintf(numstr, "%d", gid); return numstr; } else { return grp_ptr->gr_name; } }5. 完整实现代码
5.1 主程序框架
#include <stdio.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <pwd.h> #include <grp.h> #include <time.h> #include <string.h> void do_ls(char dirname[]); void do_stat(char *dirname, char *filename); void show_file_info(char *filename, struct stat *info_p); void mode_to_letters(int mode, char str[]); char *uid_to_name(uid_t uid); char *gid_to_name(gid_t gid); int main(int argc, char *argv[]) { if (argc == 1) { do_ls("."); } else { while (--argc) { printf("%s:\n", *++argv); do_ls(*argv); } } return 0; }5.2 目录遍历实现
void do_ls(char dirname[]) { DIR *dir_ptr = NULL; struct dirent *direntp = NULL; if ((dir_ptr = opendir(dirname)) == NULL) { fprintf(stderr, "ls2: cannot open %s\n", dirname); } else { while ((direntp = readdir(dir_ptr)) != NULL) { do_stat(dirname, direntp->d_name); } closedir(dir_ptr); } }5.3 文件属性处理
void do_stat(char *dirname, char *filename) { struct stat info; char pathname[256]; sprintf(pathname, "%s/%s", dirname, filename); if (stat(pathname, &info) == -1) { perror(filename); } else { show_file_info(filename, &info); } } void show_file_info(char *filename, struct stat *info_p) { char modestr[11]; mode_to_letters(info_p->st_mode, modestr); printf("%-12s", modestr); printf("%-8d", (int)info_p->st_nlink); printf("%-8s", uid_to_name(info_p->st_uid)); printf("%-8s", gid_to_name(info_p->st_gid)); printf("%-16ld", (long)info_p->st_size); printf("%-16.12s", 4 + ctime(&info_p->st_mtime)); printf("%s\n", filename); }6. 编译与测试
6.1 编译命令
gcc ls2.c -o ls26.2 测试输出示例
$ ./ls2 / drwxrwxr-x 2 root root 4096 Apr 18 15:05 cdrom/ drwxr-xr-x 18 root root 4180 Nov 19 08:58 dev/ drwxr-xr-x 2 root root 12288 Nov 19 08:59 sbin/ drwxr-xr-x 25 root root 4096 Nov 9 10:21 ./ drwxrwxrwx 13 root root 4096 Nov 19 13:56 tmp/ dr-xr-xr-x 13 root root 0 Nov 19 08:57 sys/ dr-xr-xr-x 242 root root 0 Nov 19 08:57 proc/ drwxr-xr-x 22 root root 4096 Apr 18 23:24 lib/ -rw------- 1 root root 8453792 Oct 18 18:37 vmlinuz/