UNIXC001 文件操作 之 文件的元数据

文件的元数据

1. 概念

在这里插入图片描述

  • 查看文件元数据(inode, 类型,权限,硬链接,uid,gid,3个时间等)
$ ls -il hello
114032647 -rw-rw-r-- 1 moonx moonx 6 12月 23 20:28 hello
$ stat hello
  File: 'hello'
  Size: 6               Blocks: 8          IO Block: 4096   regular file
Device: 800h/2048d      Inode: 114032647   Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   moonx)   Gid: ( 1000/   moonx)
Access: 2020-12-23 20:16:36.121101067 +0800
Modify: 2020-12-23 20:28:29.631534767 +0800
Change: 2020-12-23 20:28:29.631534767 +0800
 Birth: -

2 文件的 数据 和 元数据

在这里插入图片描述

  • fileA 是文件的名字,中间是struct_inod 类型的结构体变量,里面主要有有两部分内容,数据块表和元数据
  • 每个文件有且仅有一个自己的inode,通过文件的名字fileA就能找到文件的inod,找到文件的inod以后,才能操作文件的元数据,和操作文件的内容。
# ls -i 可以查看文件的inod
$ ls -i hello 
113377290 hello

2.1 数据块表

  • 数据块表:是一个整型数组,里面有15个元素,每个元素指定数据块的编号,在存储文件的时候分为很多个数据块,数据块的编号存放在这个数组当中。

  • 1Byte=8bit,1KB=1024Byte,1MB=1024KB, 1GB=1024MB

  • 0-11下标的元素分别指向的是一个单个的数据块,都是4KB,相当于每个元素可以存放4KB的数据。

  • 12,13下标的元素也指向4KB的数据块,这个4KB的数据块又可以存放1024个整数(一个整型占4个字节,4KB除以4Byte是1024)。这1024个整数又分别对应着1024 个 4KB大小数据块的编号。1024 * 4KB = 4MB。相当一个元素可以存放4M的数据。两个就是8M的数据

  • 14, 15下标的元素也指向4KB的数据块,这个4KB的数据块右可以存放1024个整数,这1024个整数又分别对应着1024个4KB大小的数据块的编号,然后每个4KB的数据块有可以存放1024个整数,每个整数又对应着4KB的数据块的编号。1024 * 1024 * 4KB = 4GB。相当于一个元素可以存放4GB的数据,两个就是8GB。

  • 综上,fileA的大小可以被允许到8GB多的大小,如果文件很大超过8GB,那么就需要在格式化的时候对块的大小重新指定,不指定为4K,指定为8K(2 * 2048 * 2048 * 8KB = 64GB),那么fileA的大小可以被允许到64GB。

2.2 元数据

2.2.1 stat 函数

在这里插入图片描述

2.2.2 struct stat 类型结构体变量成员
$ man 2 stat
struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               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) */
               off_t     st_size;        /* total size, in bytes */
               blksize_t st_blksize;     /* blocksize 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 */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };
2.2.3 stat代码实例

t_stdio.h

#ifndef T_STDIO_H_
#define T_STDIO_H_
#include <stdio.h>
#define E_MSG(STRING, VAL) do{perror(STRING); return(VAL);}while(0)
#endif

t_stat.c

#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);
    // 已经获取到文件的元数据, 并把元数据存到了buf空间里
    // buf.st_size 可用 man 2 stat 查看手册知道用法
    // buf.st_size 的类型可以用gcc -E t_stat.c -o s_stat.i 得知
    printf("file size: %ld\n", buf.st_size);     // long int -> %ld
    printf("links: %lu\n", buf.st_nlink);   // unsigned long int -> %lu
    printf("inode: %lu \n", buf.st_ino);    // unsigned long int -> %lu
    printf("uid: %u \n", buf.st_uid);    // unsigned  -> %u
    printf("gid: %u \n", buf.st_gid);    // unsigned  -> %u

    return 0;
}
## 第一次编译说 %d 占位符应该对应一个整型, 但是第二个参数是 __off_t 类型
$ gcc t_stat.c 
t_stat.c: In function ‘main’:
t_stat.c:12:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘__off_t {aka long int}[-Wformat=]
     printf("file size: %d \n", buf.st_size);
            ^
t_stat.c:13:5: warning: ‘return’ with no value, in function returning non-void
     return;

## 可以通过预处理得到__off_t的真正类型是 long int (缩写是long), 占位符改成 %l就可以了
$ gcc -E s_stat.c -o s_stat.i
cat  s_stat.i | grep __off_t
typedef long int __off_t;
....
typedef __off_t off_t;
## 同样的, 其他成员的类型也可以通过这种方法得到

$ gcc t_stat.c 
$ ./a.out hello 
file size: 6
links: 1
inode: 114032647 
uid: 1000 
gid: 1000

## 和上面一样, uid,gid在系统中存放的是一个编号,但是呈现给用户看到的是一个字符串, 后面将通过编号找到名字
$ stat hello
  File: 'hello'
  Size: 6               Blocks: 8          IO Block: 4096   regular file
Device: 800h/2048d      Inode: 114032647   Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   moonx)   Gid: ( 1000/   moonx)
Access: 2020-12-23 20:16:36.121101067 +0800
Modify: 2020-12-23 20:28:29.631534767 +0800
Change: 2020-12-23 20:28:29.631534767 +0800
 Birth: -

2.3. 文件的权限

在这里插入图片描述

2.4. 文件的硬链接数

在这里插入图片描述

  • 一个文件有唯一的inode,但一个inode可以有多个文件名与之对应
2.4.1 链接文件

在这里插入图片描述

  • 文件fileB是 文件fileA的硬链接,指向同一个inode。文件fileC 是 文件fileB的的软连接,文件fileC有自己的元数据和数据,只不过数据块表存放的文件fileB的名字。数据块表是含有15个整型元素的整型数组,大小是60个字节。如果文件fileB的名字和路径不超过60个字节,那么就把它存放在数组里面,否则存放在数据块里面。
$ touch fileA
$ ln fileA fileB
$ ln -s fileB fileC

$ ls -li *
135433 -rw-rw-r-- 2 moonx moonx 0 12月 24 22:21 fileA ## 2 代表inode 135433 对应2个文件名字
135433 -rw-rw-r-- 2 moonx moonx 0 12月 24 22:21 fileB
135382 lrwxrwxrwx 1 moonx moonx 5 12月 24 22:22 fileC -> fileB # 5代表 “fileB” 字节长度
 
$ echo 666 > fileA
$ cat fileC
666

$ rm fileB
$ cat fileC
cat: fileC: No such file or directory	# 因为找不到文件名fileB对应的inode

$ touch fileB; echo 555 > fileB
$ cat fileC
555

2.5 文件的时间

在这里插入图片描述

  • 即改变文件元数据时,状态时间就会改变
2.5.1 linux时间戳

在这里插入图片描述

2.5.2 文件的时间处理 ctime

在这里插入图片描述
t_stat.c

#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);
    // 已经获取到文件的元数据, 并把元数据存到了buf空间里

    printf("time of last access: %ld \n", buf.st_atim.tv_sec);
    printf("time of last access: %s \n", ctime(&buf.st_atim.tv_sec));

    return 0;
}
gcc t_stat.c
$ ./a.out t_stat.i
time of last access: 1608823757 
time of last access: Thu Dec 24 23:29:17 2020

3. 通过文件元数据uid, gid 获取对应字符串

  • 通过文件元数据uidgid ,找到两个id找到对应的字符串,也就是下面字段中的第一项

3.1 用户 /etc/passwd

在这里插入图片描述

3.1.1 七个字段
  • 用户名 : x(是否有密码 ): uid : gid : 备注信息 : 用户家目录 : 用户登录以后执行的第一个程序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.1.2 getpwuid 将用户的 uid 转字符串

在这里插入图片描述

  • getpwuid函数 返回NULL不一定是错误,也可能是没有找到匹配条目
  • man 3 getpwuid 查看返回值类型
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 */
};
  • 把文件元数据中的uid转为对应的字符串
#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>

int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);
    printf("uid: %u \n", buf.st_uid);    // unsigned  -> %u
    printf("gid: %u \n", buf.st_gid);    // unsigned  -> %u
    struct passwd *p = getpwuid(buf.st_gid);
    printf("usrname: %s\n", p->pw_name);
    return 0; 
}

3.2 用户组 /etc/group

在这里插入图片描述

  • 跟用户组同名的用户是归在这个这个组里的
  • 如果是x,代表没有密码, 和用户部分有区别
3.2.1 四个字段

在这里插入图片描述

3.2.2 getgrgid 将用户组的 gid 转为字符串

在这里插入图片描述

  • man getgrgid 查看返回值类型
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 */
};
#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);

    printf("groupname:%s\n", (getgrgid(buf.st_gid))->gr_name);

    return 0;
}

4. 通过元数据st_mode获取文件类型

  • 通过文件元数据 st_mode,来获取文件类型
  • 可以用手册里的那些宏来检测文件类型 man 2 stat

4.1 方法一

  • 用宏 S_ISREG(), S_ISDIR()

在这里插入图片描述

#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);

    printf("mode: %o\n", buf.st_mode); //100664, 权限是0664,以八进制形式显示的, 前面的10代表的是文件的类型是regular file
    if(S_ISREG(buf.st_mode))printf("-");
    if(S_ISDIR(buf.st_mode))printf("D");
    printf("\n");
    return 0;
}
$ touch a
$ ./a.out a
mode: 100664
-
$ mkdir b
$ ./a.out b
mode: 40775
D

4.2 方法二

  • st_mode 和 SIFMT(0170000) 做&运算,得到的是0140000就说明是socket类型
  • 后面4个0是文件的权限,最前面的0代表8进制,8进制 17代表二进制 1111,(7是3个1),文件的类型是由4位2进制表示的
  • &运算之后,只留下了表示文件类型的部分

在这里插入图片描述

#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);

    printf("mode: %o\n", buf.st_mode); //100664, 权限是0664,以八进制形式显示的, 前面的10代表的是文件的类型是regular 
    // if(S_ISREG(buf.st_mode))printf("-");
    // if(S_ISDIR(buf.st_mode))printf("D");
    switch (buf.st_mode&S_IFMT)
    {
    case S_IFREG:
        printf("-");
        break;
    case __S_IFDIR:
        printf("D");
    default:
        break;
    }
    printf("\n");
    return 0;
}
$ touch a
$ ./a.out a
mode: 100664
-
$ mkdir b
$ ./a.out b
mode: 40775
D

5. 通过元数据st_mode获取文件权限字符串形式

  • S_IRWXU 00700 是文件拥有者的权限掩码,最前面的0代表8进制,后面的0700( 二进制的 111)代表文件拥有者的权限是rwx
  • st_mode和S_IRWXU &运算之后只留下了文件拥有者的权限
  • 把上面的结果再和 S_IRUSR 等&运算
  • 同理S_IRWXG, S_IRWXO, 下面代码用演示S_IRWXU的情况
    在这里插入图片描述
#include "t_stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

int main(int argc, char *argv[]) {
    // struct stat buf
    struct stat buf;
    // 获取文件的元数据
    int s=stat(argv[1], &buf);
    if(s == -1)E_MSG("stat", -1);
    printf("mode: %o\n", buf.st_mode); //100664, 权限是0664,以八进制形式显示的, 前面的10代表的是文件的类型是regular 
    ((buf.st_mode&S_IRWXU)&S_IRUSR)?printf("r"):printf("-");
    ((buf.st_mode&S_IRWXU)&S_IWUSR)?printf("w"):printf("-");
    ((buf.st_mode&S_IRWXU)&S_IXUSR)?printf("x"):printf("-");
    printf("\n");
    return 0;
}
$ ./a.out hello 
mode: 100664
rw-
$ ls hello -l
-rw-rw-r-- 1 moonx moonx 6 12月 23 20:28 hello
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值