一、文件的属性
Linux系统下,使用ls -il
命令,会在终端输出类似下面这种结果
676607 drwxr-xr-x 2 admin admin 4096 7月30日 20:00 dir
676599 -rw-r--r-- 1 admin admin 0 7月30日 20:00 file
676602 -rw-r--r-- 1 admin admin 0 7月30日 20:00 test
其中,
-
第一列 —— 代表文件的inode值,可以找到对应的inode,获取到文件的元数据,ls命令使用-i选项可以查看inode号
-
第二列 —— 说明文件的类型和权限
第一个字母代表文件类型:
d : 目录(文件夹)
- : 普通文件
c :字符设备文件
b :块设备文件
p :管道文件
s :socket文件
l :软连接文件
后面的9个字符代表权限,分别是
r - 读
w - 写
x - 执行
每三个一组,分别是属主权限、数组权限和其他用户权限,对应位置为-代表没有对应权限。 -
第三列 —— 硬链接数量(如果是文件夹,就是文件夹下面的文件数量,新建一个空文件夹,查看时这个值是2,因为每个文件夹下面都有.和…两个路径,)
-
第四列 —— 属主
-
第五列 —— 数组
-
第六列 —— 文件大小,如果是文件夹,那就是4096
-
第七列 —— 文件的修改时间
-
第八列 —— 文件名
这些都是文件的元数据,存储在inode信息里面。具体的说明参考下面这个链接
https://www.cnblogs.com/llife/p/11470668.html
1.2 查看文件的全部属性
# stat命令查看文件的全部属性
[root@localhost ~]# stat test.txt
File: ‘test.txt’
Size: 18 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 33574994 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:admin_home_t:s0
Access: 2019-08-28 19:55:05.920240744 +0800
Modify: 2019-08-28 19:55:05.920240744 +0800
Change: 2019-08-28 19:55:05.920240744 +0800
其中,时间属性:
ctime:change time是最后一次改变文件或目录(属性)的时间,例如执行chmod,chown等命令。
atime:access time是最后一次访问文件或目录的时间。
mtime:modify time是最后一次修改文件或目录(内容)的时间。
二、软链接和硬链接
2.1 软链接
把文件链接到一个文件,类似于windows下面的快捷方式,使用这个文件实际上是使用指向的那个文件。
如果删除了指向的文件,那么这个链接就无效了。
删除软链接,不影响指向的文件。
软链接的创建:
# 创建软链接linkfile指向srcfile
ln -s srcfile linkfile
2.2 硬链接
硬链接是两个文件具有相同的inode值,也就是引用相同的inode,修改其中一个文件,另一个也会同步修改。
但是删除一个不会对另一个造成影响。删除之后另一个可以正常使用。
硬链接创建:
#创建src的硬链接dst
ln src dst
三、C使用系统调用获取文件的元数据
3.1 记录文件元数据的结构体类型
使用man 2 sta
t查看对应的手册
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 */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
}
3.2 获取文件元数据的函数
原型:
**int stat(const char pathname, struct stat statbuf);
其中:
pathname —— 要获取的文件名
statbuf —— 获取的元数据保存到的对象指针
返回值 —— 成功返回0,失败返回-1,并设置对应的错误码
四、根据uid和gid获取对应的用户和组数据
4.1 用户信息和组信息
- 用户信息
Linux下用户信息保存在/etc/passwd里面,
使用cat /etc/passwd
命令查看
得到如下结果
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
其中,每一项以:分隔
第一项 —— 表示用户名
第二项 —— x表示有密码
第三项 —— 表示用户id
第四项 —— 表示组id
第五项 —— 表示用户的附加信息,一般是说明性的信息
第六项 —— 表示用户的家目录
第七项 —— 表示用户的默认程序?
Linux下组信息保存在/etc/group里面
使用cat /etc/group
命令查看
得到如下结果
root:x:0:
bin:x:1:
daemon:x:2:
其中:
第一项 —— 表示组名
第二项 —— x表示没有密码
第三项 —— 表示组id
第四项 —— 表示组里面的用户(和组名相同的一般默认省略)
4.2 C获取用户和组信息的函数
通过用户id获取用户信息
原型
struct passwd* getpwuid(uid_t uid) -
其中:
uid —— 用户id
返回值 —— struct 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 */
};
这个结构体的成员正好对应/etc/passwd文件的用户信息
通过组id获取组信息
原型
struct group* getgrgid(gid_t gid)
其中:
gid —— 组id
返回值 —— struct 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 */
};
这个结构体的成员正好对应/etc/group文件的组信息
五、获取文件元数据的实例
下面实例简化的实现了ls 指令,其中支持-i -l两个选项
lsm.h —— 头文件
#ifndef _LSM_H_
#define _LSM_H_
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include <time.h>
#include <getopt.h>
#include <grp.h>
#include <string.h>
void on_i(const struct stat *statbuf);
void on_l(const struct stat *statbuf);
void on_h();
#endif // _LSM_H_
lsm.c ——源文件
#include "lsm.h"
void on_i(const struct stat *statbuf)
{
printf("%u ", statbuf->st_ino);
}
void on_l(const struct stat *statbuf)
{
// 输出文件类型
switch (statbuf->st_mode & S_IFMT)
{
case S_IFREG:
printf("-");
break;
case S_IFDIR:
printf("d");
break;
case S_IFSOCK:
printf("s");
break;
case S_IFLNK:
printf("l");
break;
case S_IFBLK:
printf("b");
break;
case S_IFCHR:
printf("c");
break;
case S_IFIFO:
printf("f");
break;
default:
break;
}
// 输出文件权限
char ur = ((statbuf->st_mode & S_IRWXU) & S_IRUSR) ? 'r' : '-';
char uw = ((statbuf->st_mode & S_IRWXU) & S_IWUSR) ? 'w' : '-';
char ux = ((statbuf->st_mode & S_IRWXU) & S_IXUSR) ? 'x' : '-';
char gr = ((statbuf->st_mode & S_IRWXG) & S_IRGRP) ? 'r' : '-';
char gw = ((statbuf->st_mode & S_IRWXG) & S_IWGRP) ? 'w' : '-';
char gx = ((statbuf->st_mode & S_IRWXG) & S_IXGRP) ? 'x' : '-';
char otr = ((statbuf->st_mode & S_IRWXO) & S_IROTH) ? 'r' : '-';
char otw = ((statbuf->st_mode & S_IRWXO) & S_IWOTH) ? 'w' : '-';
char otx = ((statbuf->st_mode & S_IRWXO) & S_IXOTH) ? 'x' : '-';
printf("%c%c%c%c%c%c%c%c%c", ur, uw, ux, gr, gw, gx, otr, otw, otx);
// 输出文件链接数
printf(" %u", statbuf->st_nlink);
// 输出用户名
struct passwd *pw = getpwuid(statbuf->st_uid);
printf(" %s", pw->pw_name);
// 输出组名
struct group *grp = getgrgid(statbuf->st_gid);
printf(" %s", grp->gr_name);
// 输出文件大小
printf(" %ld", statbuf->st_size);
// 输出文件修改时间
char buf[1024];
strcpy(buf,ctime(&statbuf->st_atime));
size_t len = strlen(buf);
for (size_t i = len -2; i<len;++i)
{
if (buf[i] == '\n')
{
buf[i] = '\0';
}
}
printf(" %s ", buf);
}
void on_h()
{
printf("help:\n");
printf("the first arg must be file name which you want to watch\n");
printf("-h or --help for help tips of using this command\n");
printf("-i or --inod for printf the inode of the file\n");
printf("-l or --list for list the info of the file\n");
}
main文件 —— 完成对命令行参数的处理
#include "lsm.h"
#include <stdbool.h>
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("参数不足,请输入文件名\n");
return -1;
}
struct stat statbuf;
// 命令行参数和命令选项的区分不是十分清楚,所以这里用了两个bool类型的变量来记录是否有对应的选项,后面根据这两个标志进行相应的选项的操作
bool b_i = false;
bool b_l = false;
struct option longopt[] = {
{"inode", 0, NULL, 'i'},
{"list", 0, NULL, 'l'},
{"help", 0, NULL, 'h'},
{NULL, 0, NULL, 0}
};
const char *shortopt = "ilh";
int opt = getopt_long(argc, argv, shortopt, longopt, NULL);
while (opt != -1)
{
switch (opt)
{
case 'h':
on_h();
return 0;
break;
case 'i':
b_i = true;
break;
case 'l':
b_l = true;
break;
case '?':
printf("未知参数\n");
on_h();
return 0;
break;
default:
break;
}
opt = getopt_long(argc, argv, shortopt, longopt, NULL);
}
if (b_i || b_l)
{
int res = stat(argv[argc - 1], &statbuf);
if (res == -1)
{
perror(argv[argc - 1]);
return -1;
}
if (b_i)
{
on_i(&statbuf);
}
if (b_l)
{
on_l(&statbuf);
}
}
printf("%s\n", argv[argc - 1]);
return 0;
}