linux系统编程之文件与io(三)
上次我们利用文件的read和write来实现了简易的cp命令,其中将源文件拷贝到目标文件时,我们给目标文件的权限是写死的,而非根据源文件的权限生成的,如下:
今天就来解决这个问题,来学习获取文件权限相关的函数,言归正传,正入正题。
stat:功能:读取文件元数据
关于stat结构体的结构如下:
struct stat { dev_t st_dev; /* ID of device containing file文件保存在磁盘上的设备号,包含主设备号和次设备号,它是16位的整数,高八位为主设备号,低八位为次设备号 */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection文件的权限信息 */ nlink_t st_nlink; /* number of hard links 文件的硬连接数*/ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) 如果文件是设备文件,所对应的设备ID*/ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O 系统当中每个块的大小 */ blkcnt_t st_blocks; /* number of 512B blocks allocated 块数目 */ time_t st_atime; /* time of last access 文件总共访问的次数*/ time_t st_mtime; /* time of last modification 最后的修改时间*/ time_t st_ctime; /* time of last status change 最后状态改变的时间,比如说改变了文件权限,并未改变文件的内容*/ };
下面就以程序例子来对其进行说明:
#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define MAJOR(a) (int)((unsigned short)a >> 8)//获取高八位数据,也就是主设备号 #define MINOR(a) (int)((unsigned short)a & 0xFF)//获取低八位数据,也就是次设备号 int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage %s file\n", argv[0]); exit(EXIT_FAILURE); } struct stat sbuf; printf("Filename:%s\n", argv[1]);//打印文件名 if (stat(argv[1], &sbuf) == -1) ERR_EXIT("stat error"); printf("File number:major %d,minor %d inode %d\n", MAJOR(sbuf.st_dev)/**文件主设备号**/, MINOR(sbuf.st_dev)/**文件次设备号**/, sbuf.st_ino/**inode数**/);
return 0; }
编译:
这时再编译运行:
实际上上面的数据都可由系统命令来进行查看,如下:
总结:主设备号用来区分不同的设备,它决定了用什么样的驱动程序来访问设备;次设备号用来区分同设备中的不同分区。
下面我们继续来查看文件信息,先来打印下文件类型,关于这个数据,它是存放到stat结构体中的mode_t字段中:
先来查看下man帮助:
下面具体代码如下:
#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define MAJOR(a) (int)((unsigned short)a >> 8) #define MINOR(a) (int)((unsigned short)a & 0xFF) int filetype(struct stat *buf);//获取文件类型函数定义 int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage %s file\n", argv[0]); exit(EXIT_FAILURE); } struct stat sbuf; printf("Filename:%s\n", argv[1]); if (lstat(argv[1], &sbuf) == -1) ERR_EXIT("stat error"); printf("File number:major %d,minor %d inode %d\n", MAJOR(sbuf.st_dev), MINOR(sbuf.st_dev), (int)sbuf.st_ino); if (filetype(&sbuf)) {
//如果文件类型是设备文件,还可以获取设备更详细的信息:主设备号、次设备号 printf("Device number:major %d,minor %d\n", MAJOR(sbuf.st_rdev), MINOR(sbuf.st_rdev)); } return 0; } int filetype(struct stat *buf)//如果是设备文件,则返回1,否则返回0 { int flag = 0; printf("Filetype:"); mode_t mode; mode = buf->st_mode; switch (mode & S_IFMT) { case S_IFSOCK: printf("socket\n"); break; case S_IFLNK: printf("symbolic link\n"); break; case S_IFREG: printf("regular file\n"); break; case S_IFBLK: printf("block device\n"); flag = 1; break; case S_IFDIR: printf("directory\n"); break; case S_IFCHR: printf("character device\n"); flag = 1; break; case S_IFIFO: printf("FIFO\n"); break; default: printf("unknown file type\n"); break; } return flag; }
编译运行:
下面来打印文件的权限信息:
下面看具体代码:
#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define MAJOR(a) (int)((unsigned short)a >> 8) #define MINOR(a) (int)((unsigned short)a & 0xFF) int filetype(struct stat *buf); void fileperm(struct stat *buf, char *perm);//查看文件权限,以rwx进行格式化 int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage %s file\n", argv[0]); exit(EXIT_FAILURE); } struct stat sbuf; printf("Filename:%s\n", argv[1]); if (stat(argv[1], &sbuf) == -1) ERR_EXIT("stat error"); printf("File number:major %d,minor %d inode %d\n", MAJOR(sbuf.st_dev), MINOR(sbuf.st_dev), (int)sbuf.st_ino); if (filetype(&sbuf)) { printf("Device number:major %d,minor %d\n", MAJOR(sbuf.st_rdev), MINOR(sbuf.st_rdev)); }
//打印文件的权限信息 char perm[11] = {0}; fileperm(&sbuf, perm); printf("File permission bits=%o %s\n", sbuf.st_mode & 07777, perm); return 0; } int filetype(struct stat *buf) { int flag = 0; printf("Filetype:"); mode_t mode; mode = buf->st_mode; switch (mode & S_IFMT) { case S_IFSOCK: printf("socket\n"); break; case S_IFLNK: printf("symbolic link\n"); break; case S_IFREG: printf("regular file\n"); break; case S_IFBLK: printf("block device\n"); flag = 1; break; case S_IFDIR: printf("directory\n"); break; case S_IFCHR: printf("character device\n"); flag = 1; break; case S_IFIFO: printf("FIFO\n"); break; default: printf("unknown file type\n"); break; } return flag; } void fileperm(struct stat *buf, char *perm) { strcpy(perm, "----------"); perm[0] = '?'; mode_t mode; mode = buf->st_mode; switch (mode & S_IFMT)//确定第一位文件类型 { case S_IFSOCK: perm[0] = 's'; break; case S_IFLNK: perm[0] = 'l'; break; case S_IFREG: perm[0] = '-'; break; case S_IFBLK: perm[0] = 'b'; break; case S_IFDIR: perm[0] = 'd'; break; case S_IFCHR: perm[0] = 'c'; break; case S_IFIFO: perm[0] = 'p'; break; } if (mode & S_IRUSR)//与用户读权限进行与操作之后,如果为真,则代表有这个权限 perm[1] = 'r'; if (mode & S_IWUSR) perm[2] = 'w'; if (mode & S_IXUSR) perm[3] = 'x'; if (mode & S_IRGRP) perm[4] = 'r'; if (mode & S_IWGRP) perm[5] = 'w'; if (mode & S_IXGRP) perm[6] = 'x'; if (mode & S_IROTH) perm[7] = 'r'; if (mode & S_IWOTH) perm[8] = 'w'; if (mode & S_IXOTH) perm[9] = 'x'; perm[10] = '\0'; }
编译运行:
利用上面所学的东西,我们就可以实现自己的ls -l的功能了,在实现这个功能之时,还有几个知识点需介绍一下:
先看一下ls -l会显示什么信息:
①文件权限,这个上面已经实现过了,所以不成问题
②连接数,对应的是stat->nlink_t,所以不成问题
③用户名,我们知道stat->uid_t,可以根据用户ID获得用户名,通过如下函数:
④组名,我们知道stat->gid_t,可以根据组ID获得组名,通过如下函数:
⑤文件大小,可以通过stat->st_size来获得
⑥文件修改时间,可以通过stat->st_mtime来获得
⑦文件名,这个肯定可以获取。
但是,对于符号链接文件,还需要做一个特珠的处理,如下:
那对于符号链接文件,怎么才能获取它所指向的链接文件呢?可以通过如下函数获取:
所以说,我们完全有能力实现跟ls -l系统命令一样的功能:
注意:
对于符号链接文件,我们查看其文件信息时,应该是lstat函数,而不是stat
对于目前我们实现查看的程序,是用的stat函数,那查看我们新创建的a链接文件的信息是什么呢?
这时,我们将state改为lstate,再次查看下:
编译运行:
查看一下lsate函数的说明:
好了,今天文件信息的查看学到这,下次下见。