在参考多篇文章后,自己做出的小结。
在Linux中,可以利用stat()函数来获取一个文件的状态。
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);
这个函数通过文件名filename获取文件信息,并保存在buf所指的结构体stat中。执行成功返回0,失败返回-1。
struct stat的定义如下:
struct stat
{
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; /* 文件的类型和存取的权限*/
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; /* user ID of owner -user id*/
gid_t st_gid; /* group ID of owner - group id*/
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 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 -最后权限修改时间 */
};
其中, st_mode这个变量用来判断文件类型,它的类型为mode_t,其实就是普通的unsigned int,但是只是用了低16位。
st_mode 主要包含了 3 部分信息:
a. 15bit ~ 12bit 保存文件类型
b. 11bit ~ 9bit 保存执行文件时设置的信息
c. 8bit ~ 0bit 保存文件访问权限
一、bit15 ~ bit12 , 文件类型属性区域
在现代linux操作系统上文件类型共分为7种,分别是:
- : 普通文件(regular file)
d : 目录(directory)
c : 字符设备(character device)
b : 块设备(block device)
p : 管道(FIFO)
l : 符号链接文件(symbolic link)
s : 套接口文件(socket)
所以文件类型属性只需3bit就够了。
二、bit11 ~ bit9,权限的特殊属性区域
bit11:set-user-ID位,执行时设置用户ID
bit10:set-group-ID位,执行时设置组ID
bit9:sticky位,仅对目录有效。设置后所有用户都可在这个目录下创建文件,但该目录下的文件只能被owner和root删除。
下面来解释sticky位的作用:
首先要明白,要删除一个文件,一定要有这个文件的上级目录的写权限,可以没有文件的写权限。且没有一个目录的写权限,就不能在这个目录下创建文件。
sticky就是能使一个目录既可以让任何用户写入文件,又不让用户删除这个目录下他人的文件。sticky一般只用在目录上,普通文件的sticky位会被linux内核忽略。
sticky位出现执行许可x的位置上,用t表示(子目录不继承该权限,要再设置)。如下方的代码:
$ ls -dl /tmp
drwxrwxrwt 15 root root ......... // 注意 other 用户的权限位,x 变成了 t
那么原来的执行标志x到哪里去了呢? 系统是这样规定的, 假如本来在该位上有x, 则这些特别标志 (suid, sgid, sticky) 显示为小写字母 (s, s, t). 否则, 显示为大写字母 (S, S, T) 。
另外
chmod 777 abc
chmod +t abc
等价于
chmod 1777 abc
即在设置sticky位后,目录的权限多了一个最高位的1。
三、bit8 ~ bit0,权限属性区域
即st_mode字段的最低9位,代表文件的许可权限,标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。
四、实例分析
例一:st_mode 10进制值是 33204,转换成 8 进制后为 100664
图1 33204 填到 st_mode 中
根据图1 我们可以得到如下信息:
- 1000: 这是一个常规文件
- 000: 执行时设置信息为空,黏着位为 0
- 110-110-100: 用户权限为
RW-
,组员权限为RW-
,其他人权限为R--
例2:st_mode 值为 35309. 转换成 8 进制后为 104755
图2 35309填充 st_mode
分析图2,可以知道:
- 1000:这是一个普通文件
- 100:suid 为 1
- 111-101-101:用户权限为
RWX
,组员权限为R-X
,其他人权限为R-X
五、宏
通过手工分析 st_mode 字段,实际上是很不方便的。实际写程序,可使用 st_mode & 掩码来得到 st_mode 中特定的部分。比如:
st_mode & 0170000 : 得到文件类型
st_mode & 0007000 : 得到执行文件时设置信息
st_mode & 0000777 : 得到权限位
st_mode & 00100: 判断所有者是否可执行
(以上数字均为八进制)
…
为了方便使用,用 linux 预定义的一些宏来代替这些生硬的数字。这些宏定义在 include <sys/stat.h> 头文件中。
//bit15 ~ bit12 , 文件类型属性区域
#define S_IFMT 0170000 文件类型的位遮罩
#define S_IFSOCK 0140000 socket
#define S_IFLNK 0120000 符号链接(symbolic link)
#define S_IFREG 0100000 一般文件
#define S_IFBLK 0060000 区块装置(block device)
#define S_IFDIR 0040000 目录
#define S_IFCHR 0020000 字符装置(character device)
#define S_IFIFO 0010000 先进先出(fifo)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) //提供了一些宏来帮助用户执行&操作,是则返回1
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
//bit11 ~ bit9,权限的特殊属性区域
#define S_ISUID 0004000 文件的(set user-id on execution)位
#define S_ISGID 0002000 文件的(set group-id on execution)位
#define S_ISVTX 0001000 文件的sticky位
//bit8 ~ bit0,权限属性区域
//文件所有者(owner)
#define S_IRWXU 00700 /* mask for file owner permissions */
#define S_IRUSR 00400 /* owner has read permission */
#define S_IWUSR 00200 /* owner has write permission */
#define S_IXUSR 00100 /* owner has execute permission */
//组用户(group)
#define S_IRWXG 00070 /* mask for group permissions */
#define S_IRGRP 00040 /* group has read permission */
#define S_IWGRP 00020 /* group has write permission */
#define S_IXGRP 00010 /* group has execute permission */
//其他用户(other)
#define S_IRWXO 00007 /* mask for permissions for others (not in group) */
#define S_IROTH 00004 /* others have read permission */
#define S_IWOTH 00002 /* others have write permission */
#define S_IXOTH 00001 /* others have execute permission */
以上宏可以自由组合使用。