linux系统编程之文件与io(三)

时间:2023-03-09 10:02:43
linux系统编程之文件与io(三)

上次我们利用文件的read和write来实现了简易的cp命令,其中将源文件拷贝到目标文件时,我们给目标文件的权限是写死的,而非根据源文件的权限生成的,如下:

linux系统编程之文件与io(三)

今天就来解决这个问题,来学习获取文件权限相关的函数,言归正传,正入正题。

stat:功能:读取文件元数据
linux系统编程之文件与io(三)
linux系统编程之文件与io(三)
关于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() #define MAJOR(a) (int)((unsigned short)a >> 8)//获取高八位数据,也就是主设备号
#define MINOR(a) (int)((unsigned short)a & 0xFF)//获取低八位数据,也就是次设备号 int main(int argc, char *argv[])
{
if (argc != )
{
fprintf(stderr, "Usage %s file\n", argv[]);
exit(EXIT_FAILURE);
}
struct stat sbuf;
printf("Filename:%s\n", argv[]);//打印文件名
if (stat(argv[], &sbuf) == -)
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;
}

编译:

linux系统编程之文件与io(三)

linux系统编程之文件与io(三)

这时再编译运行:

linux系统编程之文件与io(三)

实际上上面的数据都可由系统命令来进行查看,如下:

linux系统编程之文件与io(三)

linux系统编程之文件与io(三)

总结:主设备号用来区分不同的设备,它决定了用什么样的驱动程序来访问设备;次设备号用来区分同设备中的不同分区。

下面我们继续来查看文件信息,先来打印下文件类型,关于这个数据,它是存放到stat结构体中的mode_t字段中:

先来查看下man帮助:

linux系统编程之文件与io(三)

下面具体代码如下:

#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() #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 != )
{
fprintf(stderr, "Usage %s file\n", argv[]);
exit(EXIT_FAILURE);
}
struct stat sbuf;
printf("Filename:%s\n", argv[]);
if (lstat(argv[], &sbuf) == -)
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 ;
} 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;
}

编译运行:

linux系统编程之文件与io(三)

下面来打印文件的权限信息:

linux系统编程之文件与io(三)

下面看具体代码:

#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() #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 != )
{
fprintf(stderr, "Usage %s file\n", argv[]);
exit(EXIT_FAILURE);
} struct stat sbuf;
printf("Filename:%s\n", argv[]);
if (stat(argv[], &sbuf) == -)
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 ;
} int filetype(struct stat *buf)
{
int flag = ;
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 = ;
break;
case S_IFDIR:
printf("directory\n");
break;
case S_IFCHR:
printf("character device\n");
flag = ;
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';
}

编译运行:

linux系统编程之文件与io(三)

利用上面所学的东西,我们就可以实现自己的ls -l的功能了,在实现这个功能之时,还有几个知识点需介绍一下:

先看一下ls -l会显示什么信息:

linux系统编程之文件与io(三)

文件权限,这个上面已经实现过了,所以不成问题

连接数,对应的是stat->nlink_t,所以不成问题

用户名,我们知道stat->uid_t,可以根据用户ID获得用户名,通过如下函数:

linux系统编程之文件与io(三)

linux系统编程之文件与io(三)

④组名,我们知道stat->gid_t,可以根据组ID获得组名,通过如下函数:

linux系统编程之文件与io(三)

linux系统编程之文件与io(三)

文件大小,可以通过stat->st_size来获得

文件修改时间,可以通过stat->st_mtime来获得

文件名,这个肯定可以获取。

但是,对于符号链接文件,还需要做一个特珠的处理,如下:
linux系统编程之文件与io(三)
那对于符号链接文件,怎么才能获取它所指向的链接文件呢?可以通过如下函数获取:
linux系统编程之文件与io(三)
所以说,我们完全有能力实现跟ls -l系统命令一样的功能:
注意:
对于符号链接文件,我们查看其文件信息时,应该是lstat函数,而不是stat
对于目前我们实现查看的程序,是用的stat函数,那查看我们新创建的a链接文件的信息是什么呢?
linux系统编程之文件与io(三)
这时,我们将state改为lstate,再次查看下:
linux系统编程之文件与io(三)
编译运行:
linux系统编程之文件与io(三)
查看一下lsate函数的说明:
linux系统编程之文件与io(三)
好了,今天文件信息的查看学到这,下次下见。