您的位置:首页 > 编程语言 > C语言/C++

【C语言】【unix c】获取文件的元数据(软链接,硬链接)(meta data)

2017-08-24 22:43 936 查看
一、获取文件的元数据(软链接,硬链接)(meta data)
1、元数据也就是常说的文件的属性
举例:用ls -l察看详细信息:
-      rw-rw-r--        1      tarena tarena     0     8月  9 09:15      这些都是文件的元信息(不包括文件的名字)
文件的类型 文件的权限 文件的硬链接数 属主   属组  文件大小       时间

2、文件的链接分为两种:硬链接和软链接

3、每个文件有且仅有一个自己的inode(这是一个结构体,里面分为2部分,上部分是存储元数据的,下面部分(有15个区域的整形变量(60字节))是指向数据存储的地方)(文件和inode是多对一的关系)




4、硬链接:

a、硬链接:两个文件有同一个inode,也就是读取两个文件所读取到的数据是一样的,文件信息也是一样的

b、如何为一个文件创建硬链接:
【ln 源文件 链接文件】

命令: tarena@ubuntu:~/day/day29$ ln hello h
结果: 总用量 0
-rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 h    //创建后硬链接数变为2
-rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 hello

察看inode号:【ls -li】
命令: tarena@ubuntu:~/day/day29$ ls -li
结果: 总用量 0
1055687 -rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 h  //inode号是相同的,所以信息都是一样的
1055687 -rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 hello

分析: 两个文件指向同一个inode,对其中一个的内容进行修改,另一个的内容也被修改,因为他们指向同一个数据区,删除一个文件,数据不会丢失

5、软链接: 就是inode下部分原本指向数据区的,现在指向一个文件的inode(文件名字要小于60字节,大于60的话会将名字存在数据区里然后来寻找)
如何创建软链接文件:
【ln -s 源文件 链接文件】

命令: tarena@ubuntu:~/day/day29$ ln -s h sh
命令: tarena@ubuntu:~/day/day29$ ls -li
结果: 总用量 0
1055687 -rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 h
1055687 -rw-rw-r-- 2 tarena tarena 0  8月  9 09:15 hello
1055689 lrwxrwxrwx 1 tarena tarena 1  8月  9 09:44 sh -> h   //sh是h的软链接 (文件类型是 l)

实验:软链接的关系
未删除h:
tarena@ubuntu:~/day/day29$ cat sh
hello

删除h:
tarena@ubuntu:~/day/day29$ cat sh
cat: sh: 没有那个文件或目录
分析: 软链接中,源文件丢失,软链接的文件也无法访问

实验:将软链接的文件复制后有什么效果:

命令:  tarena@ubuntu:~/day/day29$ cp sh ../
命令:  tarena@ubuntu:~/day$ ls -li
结果:  总用量 56
662421 -rw-rw-r--  1 tarena tarena    0  8月  9 09:52 h
662420 -rw-rw-r--  1 tarena tarena    7  8月  9 09:54 sh //不再是软链接文件
分析:将文件拷贝后会创建一个新文件,inode也不一样了

6、如何通过程序获得文件的元数据:
使用stat(2)获取:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);

功能:获取文件的信息
参数:
path:指定了文件的名字
buf:用来存储文件的属性的信息(元数据)
返回值:0 成功
-1 错误 errno被设置

第二个参数里包含了文件的许多信息,所以使用了结构体:
struct stat:这个结构体中成员的信息:
struct stat {
dev_t     st_dev;     /* ID of device containing file */    设备号
ino_t     st_ino;     /* inode number */      inode号(索引号)
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 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 */     文件状态改变时间
};

举例:获取文件的元数据(pstat.c)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
struct stat b;
//获取文件的元数据
int s = stat(argv[1], &b);
if(s == -1) {
perror("stat");
return -1;
}
//输出文件的元数据
printf("length:%ld\n", b.st_size);
return 0;
}

命令: tarena@ubuntu:~/day/day29$ a.o
d880
ut hello
结果: length:7
分析: 这里可以输出文件的更多的信息,但是像时间,权限等等这里输出的格式还有很大的问题,所以还需要进行下面的转换

时间问题:系统记录的是1970年1月1日0点0分0秒到现在的秒数,需要将这个长整形的数转换为字符串格式

使用ctime(3)函数

#include <time.h>
char *ctime(const time_t *timep);

功能:将时间的整数值转换为字符串的格式格式如:Wed Aug  9 09:54:03 2017
参数:timep:指定要转换的长整形的数
返回值:NULL: 错误
非空: 转换后的字符串的首地址
转换前:
printf("last modification:%ld\n",b.st_mtime);
last modification:1502243643
转换后:
printf("last modification:%s\n",ctime(&b.st_mtime)); //要求传入的是一个地址
last modification:Wed Aug  9 09:54:03 2017

用户问题:用户看到的是文件的名字,而系统管理中使用的是用户的ID,数字
在程序中输出的是数字,但是需要转为字符串格式,方便用户的使用。那如何根据用户的id,找到用户的名字
getpwuid(3)
#include <sys/types.h>
#include <pwd.h>

struct passwd *getpwuid(uid_t uid);

功能:获取passwd中的一条记录

参数:
uid:指定用户的uid

返回值:指向匹配uid记录的首地址
NULL 未找到匹配的记录或者产生了错误
产生错误的话会设置errno

passwd是一个结构体,该结构体的内容是:
struct passwd {
char   *pw_name;       /* username */         名字
char   *pw_passwd;     /* user password */    密码
uid_t   pw_uid;        /* user ID */      UID
gid_t   pw_gid;        /* group ID */        GID
char   *pw_gecos;      /* user information */ 使用着信息????
char   *pw_dir;        /* home directory */   目录
char   *pw_shell;      /* shell program */    壳程序????
};
转换前:
命令: printf("user ID:%d\n", b.st_uid);
结果: user ID:1000
转换后:
命令: printf("user ID:%s\n", getpwuid(b.st_uid)->pw_name);
结果: user ID:tarena

getgrgid(3)
#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);

功能:获取passwd中的一条记录

参数:
gid:指定用户组的GID

返回值:
NULL 未找到匹配的记录或者产生了错误
产生错误的话会设置errno
group结构体内的内容是:
struct group {
char   *gr_name;       /* group name */      组名
char   *gr_passwd;     /* group password */  组密码
gid_t   gr_gid;        /* group ID */     组ID
char  **gr_mem;        /* group members */   组成员
};

文件权限的问题:
使用下列宏可以判断文件的类型:

b.st_mode & S_IFMT 得到的值和其他宏进行比较,用switch来选择
文件类型:
S_IFMT     0170000   bit mask for the file type bit fields
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
S_ISUID    0004000   set UID bit
S_ISGID    0002000   set-group-ID bit (see below)
S_ISVTX    0001000   sticky bit (see below)

b.st_mode & S_IRWXU/S_IRWXG/S_IRWXO 得到一个值,将这个值和这一组的其他宏定义再按位与得值如果是1的话就输出该宏定义的类型/权限
属主权限:
S_IRWXU    00700     mask for file owner permissions
S_IRUSR    00400     owner has read permission
S_IWUSR    00200     owner has write permission
S_IXUSR    00100     owner has execute permission
属组权限:
S_IRWXG    00070     mask for group permissions
S_IRGRP    00040     group has read permission
S_IWGRP    00020     group has write permission
S_IXGRP    00010     group has execute permission
其他用户权限
S_IRWXO    00007     mask for permissions for others (not in group)
S_IROTH    00004     others have read permission
S_IWOTH    00002     others have write permission
S_IXOTH    00001     others have execute permission

举例:
//判断文件的类型
switch(b.st_mode & S_IFMT) {
case S_IFBLK:  printf("b");        break;
case S_IFCHR:  printf("c");        break;
case S_IFDIR:  printf("d");        break;
case S_IFIFO:  printf("p|");       break;
case S_IFLNK:  printf("l@");       break;
case S_IFREG:  printf("-");        break;
case S_IFSOCK: printf("s=");       break;
default:       printf("unknown?\n");    break;
}
//判断文件拥有者的权限
int u = b.st_mode & S_IRWXU;
if(u & S_IRUSR) printf("r");
else printf("-");
if(u & S_IWUSR) printf("w");
else printf("-");
if(u & S_IXUSR) printf("x");
else printf("-");
//判断文件拥有者组的权限
int g = b.st_mode & S_IRWXG;
if(g & S_IRGRP) printf("r");
else printf("-");
if(g & S_IWGRP) printf("w");
else printf("-");
if(g & S_IXGRP) printf("x");
else printf("-");
//判断其他用户的权限
int o = b.st_mode & S_IRWXO;
if(o & S_IROTH) printf("r");
else printf("-");
if(o & S_IWOTH) printf("w");
else printf("-");
if(o & S_IWOTH) printf("x");
else printf("-");
printf("\n");
命令:
tarena@ubuntu:~/day/day29$ a.out hello
-rw-rw-r--

补充:
在linux系统中用户的信息存放在/etc/passwd文件中
root:x:0:0:root:/root:/bin/bash
tarena:x:1000:1000:tarena,,,:/home/tarena:/bin/bash
以:分割的7列
用户名字
是否有密码(x代表有密码)
用户的UID
用户的组ID(GID)
用户的注释信息
用户的工作主目录
用户登陆成功启动的第一个程序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unix c语言 数据