元数据
文件元数据记录了文件所有的状态信息
获取元数据
- stat(1)命令获取文件元数据
stat + 文件名
Desktop linraffe$:stat giraffe
File: giraffe
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 801h/2049d Inode: 396525 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/linraffe) Gid: ( 1000/linraffe)
Access: 2020-03-20 11:59:46.058797586 +0800
Modify: 2020-03-20 11:59:46.058797586 +0800
Change: 2020-03-20 11:59:46.058797586 +0800
Birth: -
// 这里的Links指硬链接数
- stat(2)系统调用获取文件元数据
stat - 获取文件状态信息(元数据)
所需头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
函数原型
int stat(const char *pathname, struct stat *statbuf);
参数
pathname - 需要查询元数据的文件路径
statbuf - 函数将文件元数据拷贝到statbuf指向的线性地址
返回值
获取成功,返回0
获取失败,返回-1,errno被设置
存储元数据结构体
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
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) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
};
stat(2)系统调用举例:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char *argv[]){
struct stat statbuf = {};
if(stat(argv[1],&statbuf)){
perror("stat");
return -1;
}
else{
printf("dev:%ld\n",statbuf.st_dev);
printf("ino:%ld\n",statbuf.st_ino);
printf("mod:%d\n",statbuf.st_mode);
printf("lnk:%ld\n",statbuf.st_nlink);
printf("uid:%d\n",statbuf.st_uid);
printf("gid:%d\n",statbuf.st_gid);
}
return 0;
}
执行:
Desktop linraffe$:a.out giraffe
dev:2049
ino:396525
mod:33188
lnk:1
uid:1000
gid:1000
可见系统调用stat(2)和命令stat(1)的输出结果一致
元数据相关
1)inode - 系统对文件的编码;每个文件必须有一个inode与之对应,但是每个inode不一定只对应一个文件
2)硬链接文件 - 有相同inode的文件互为硬链接文件;本质就是同一个文件,只是文件名不同,当修改一个硬链接文件,与其有硬链接关系同其他文件也会被修改
创建硬链接方法:
ln 原文件名 要创建的硬链接文件名
3)软链接文件 - 软链接文件的内容是另一个文件的路径,当访问一个软链接文件时,系统会自动将访问者引导向另一个文件
与硬链接文件不同,软链接文件与它指向的文件的inode不同;如果软链接文件所指向的文件被删除,那么软链接文件存储的文件路径就会失效,访问会报错
创建软链接接方法:
ln -s 原文件名 要创建的软链接文件名
inode详细介绍,参见inode详解
stat命令实现
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
void GetFileType(mode_t mode){
printf("File Type :");
switch(mode & S_IFMT){
case S_IFBLK: printf("block device\n"); break;
case S_IFCHR: printf("character device\n"); break;
case S_IFDIR: printf("directory\n"); break;
case S_IFIFO: printf("FIFO/pipe\n"); break;
case S_IFLNK: printf("symlink\n"); break;
case S_IFREG: printf("regular file\n"); break;
case S_IFSOCK: printf("socket\n"); break;
default: printf("unknown?\n"); break;
}
return;
}
void GetFileAccess(mode_t mode){
printf("Access: (%o/",mode);
if((mode & S_IRUSR) != 0)
printf("r");
else
printf("-");
if((mode & S_IWUSR) != 0)
printf("w");
else
printf("-");
if((mode & S_IXUSR) != 0)
printf("x");
else
printf("-");
if((mode & S_IRGRP) != 0)
printf("r");
else
printf("-");
if((mode & S_IWGRP) != 0)
printf("w");
else
printf("-");
if((mode & S_IXGRP) != 0)
printf("x");
else
printf("-");
if((mode & S_IROTH) != 0)
printf("r");
else
printf("-");
if((mode & S_IWOTH) != 0)
printf("w");
else
printf("-");
if((mode & S_IXOTH) != 0)
printf("x) \t");
else
printf("-) \t");
return;
}
void GetUid(uid_t uid){
struct passwd *pwd = getpwuid(uid);
printf("Uid: (%d/%s) \t",uid,pwd -> pw_name);
return;
}
void GetGid(gid_t gid){
struct group * grp = getgrgid(gid);
printf("Gid: (%d/%s) \n",gid,grp -> gr_name);
return;
}
void GetTime(struct stat * stat_p){
printf("Access: %s",ctime(&(stat_p -> st_atim.tv_sec)));
printf("Modify: %s",ctime(&(stat_p -> st_mtim.tv_sec)));
printf("Change: %s",ctime(&(stat_p -> st_ctim.tv_sec)));
}
int main(int argc, char *argv[]){
struct stat statbuf = {};
if(stat(argv[1],&statbuf)){
perror("stat");
return -1;
}
else{
printf("File: %s\n",argv[1]);
printf("Size: %ld \tBlocks: %ld \tIO Block: %ld\t\t",statbuf.st_size,
statbuf.st_blocks,statbuf.st_blksize);
GetFileType(statbuf.st_mode);
printf("Device: %ld/%ld \tInode: %ld \tLinks: %ld \n",statbuf.st_rdev,
statbuf.st_dev,statbuf.st_ino,statbuf.st_nlink);
GetFileAccess(statbuf.st_mode);
GetUid(statbuf.st_uid);
GetGid(statbuf.st_gid);
GetTime(&statbuf);
}
return 0;
}
编译运行结果:
Desktop linraffe$:gcc MyStat.c -o MyStat
Desktop linraffe$:MyStat giraffe
File: giraffe
Size: 0 Blocks: 0 IO Block: 4096 File Type :regular file
Device: 0/2049 Inode: 396525 Links: 1
Access: (100644/rw-r--r--) Uid: (1000/linraffe) Gid: (1000/linraffe)
Access: Fri Mar 20 13:09:59 2020
Modify: Fri Mar 20 11:59:46 2020
Change: Fri Mar 20 11:59:46 2020
Desktop linraffe$:stat giraffe
File: giraffe
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 801h/2049d Inode: 396525 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/linraffe) Gid: ( 1000/linraffe)
Access: 2020-03-20 13:09:59.703822557 +0800
Modify: 2020-03-20 11:59:46.058797586 +0800
Change: 2020-03-20 11:59:46.058797586 +0800
Birth: -
实现中的要点
1)获取用typedef定义的数据类型的实际内置数据类型的技巧
当不清楚一个自定义数据类型所代表的实际数据内置类型时,可以通过gcc -E 获得与编译后的源文件,查找头文件中对自定义数据类型的定义:
打开.i文件后,在vim的普通模式下,输入‘/’+要找的自定义数据类型
2)获取文件类型,并以字符串输出
在stat的说明文档中给出获取文件类型的方式:通过stat结构体中的st_mode成员变量按位与S_IFMT可以的到文件类型
The file type
POSIX refers to the stat.st_mode bits corresponding to the mask S_IFMT (see below) as the file type, the 12 bits corresponding to the mask 07777 as the file mode bits and the least significant 9 bits (0777) as the file permission bits.
The following mask values are defined for the file type:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
Thus, to test for a regular file (for example), one could write:
stat(pathname, &sb);
if ((sb.st_mode & S_IFMT) == S_IFREG) {
/* Handle regular file */
}
3)通过uid获取对应用户名
getpwuid - get password file entry
所需头文件 #include <sys/types.h>
#include <pwd.h>
函数原型
struct passwd *getpwuid(uid_t uid);
参数
uid - 用户的uid
返回值:
获取成功,返回0,并将passwd指针指向用户信息
passwd - 存储用户信息的结构体,成员如下:
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
获取失败,返回错误码,并将passwd指向NULL
补充:
/etc/passwd 记录了系统中所有用户的信息,这些信息就是从passwd结构体中获取的
4)通过gid获取对应组名
getgrgid - get group file entry
所需头文件 #include <sys/types.h>
#include <grp.h>
函数原型
struct group *getgrgid(gid_t gid);
参数
gid - 要获取信息的组id
返回值
获取成功,返回0,并将group指针指向组信息
group - 存储组信息的结构体,成员如下:
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* NULL-terminated array of pointers
to names of group members */
};
获取失败,返回错误码,group指针置空(指向NULL)
补充:
/etc/group 记录了系统中所有组的信息,这些信息就是从group结构体中获取的
5)以rwx的形式显示文件权限
从stat结构体的st_mode成员变量中可以获取八进制形式的文件权限,将权限转化为字符需要逐个判断每个分组中的读、写、可执行权限
The following mask values are defined for the file mode component of the st_mode field:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and
execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
6)将长整型表示的时间转换为字符串时间
ctime函数将长整型的时间转化为字符串形式的时间
ctime - transform date and time to brokendown time or ASCII
所需头文件 <time.h>
函数原型
char *ctime(const time_t *timep);
timep - 以长整型记录的时间值变量的地址
返回值
成功,返回一个表示时间的字符串
失败,返回空指针,errno被设置