获取文件信息:stat()
利用系统调用stat()
、lstat()
、以及fstat()
,可获取与文件有关的信息,其中大部分提取自文件i节点。
#include <sys/stat.h>
int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
int fstat(int fd. struct stat *statbuf);
All returns 0 on success, or -1 on error.
以上3个系统调用之间仅有的区别在于对文件的描述方式不同。
stat()
会返回所命名文件的相关信息。lstat()
与stat()
类似,区别在于如果文件属于符号链接,那么所返回的信息针对的是符号链接本身(而非符号链接所指向的文件)。fstat()
则会返回由某个打开文件描述符所指代文件的相关信息。
系统调用stat()
和lstat()
无需对其所操作的文件本身拥有任何权限,但针对指定pathname
的父目录要有执行(搜索)权限。而只要供之以有效的文件描述符,fstat()
系统调用就总是成功。
上述所有系统调用都会在缓冲区返回一个由statbuf()
指向的stat
结构,其格式如下:
struct stat{
dev_t st_dev; /*IDs of devices on which file resides */
ino_t st_ino; /*I-node number of file*/
mode_t st_mode; /*File type and permissions*/
nlink_t st_nlink; /*Number if (hard) links to file*/
uid_t st_uid; /*User ID of file owner*/
gid_t st_gid; /*Group ID of file owner*/
dev_t st_rdev; /*IDs for device special files*/
off_t st_size; /*Total file size (bytes)*/
blksize_t st_blksize; /*Optimal block size for I/O (bytes)*/
blkcnt_t st_blocks; /*Number of (512B) blocks allocated*/
time_t st_atime; /*Time of last file access*/
time_t st_mtime; /*Time of last file modification*/
time_t st_ctime; /*Time of last status change*/
};
关于其中一些重要字段,说明如下:
设备号和i节点号
st_dev
字段标识文件所驻留的设备,st_ino
字段标识文件的i节点号。
利用以上两者,可在文件系统中唯一标识某个文件。dev_t
类型记录了设备的主、辅ID。
如果是针对设备的i节点,那么st_rdev
字段则包含设备的主、辅ID。
利用宏major()
和minor()
,可提取dev_t
值的主、辅ID。获取对两个宏声明的头文件则随UNIX实现而异。在Linux系统上,若定义了_BSD_SOURCE
宏,则两个宏定义于<sys/types.h>
中。
由major()
和minor()
所返回的整型值大小也随UNIX实现的不同而各不相同。为保证可移植性,打印时应总是将返回值强制转换为long
。
文件所有权
st_uid
和st_gid
字段分别标识文件的属主(用户ID)和属组(组ID)。
链接数
st_nlink
字段包含了指向文件的(硬)链接数。
文件类型及权限
st_mode
字段内含有位掩码,起标识文件类型和指定文件权限的双重作用。
与常量S_IFMT
相与(&),可从该字段中提取文件类型。(Linux使用了st_mode
字段中的4位来标识文件类型位。但由于SUSv3并未对文件类型位的表示方法做出任何规定,故而其具体实现细节随各实现而异。)将计算结果与一系列常量进行比较,即可确定文件类型。如下所示:
if( (statbuf.st_mode & S_IFMT) == S_IFREG)
printf("regular file\n");
鉴于上述操作属于常见操作,因此可以利用标准宏将其简化为:
if(S_ISREG(statbuf.st_mode))
printf("regular file\n");
下表所列为全套文件类型宏(定义于<sys/stat.h>
)。这些宏均由SUSv3定义,并为Linux所支持。一些其他的UNIX还定义了别的文件类型。因为调用stat()
时会循符号链接而直抵实际文件,所以只有在调用lstat()
时才可能会返回类型S_IFLNK
。
- 想从
<sys/stat.h>
中获得S_IFSOCK
和S_ISSOCK
的定义,必须定义_BSD_SOURCE
特性测试宏,或是将_XOPEN_SOURCE
定义为不小于500的值。(具体规则随glibc
版本而异。在某些情况下,需将X_OPEN_SOURCE
的值定义为不小于600。)
常量 | 测试宏 | 文件类型 |
---|---|---|
S_IFREG | S_ISREG() | 常规文件 |
S_IFDIR | S_ISDIR() | 目录 |
S_IFCHR | S_ISCHR() | 字符设备 |
S_IFBLK | S_ISBLK() | 块设备 |
S_IFIFO | S_ISFIFO() | FIFO或管道 |
S_IFSOCK | S_ISSOCK() | 套接字 |
S_IFLNK | S_ISLNK() | 符号链接 |
st_mode
的低12位定义了文件权限。其中最低9位分别用来表示文件属主、属组以及其他用户的读、写、执行权限。
文件大小、已分配块以及最优I/O块大小
-
对于常规文件,
st_size
字段表示文件的字节数。对于符号链接,则表示链接所指路径名的长度,以字节为单位。对于共享内存对象,该字段则表示对象的大小。 -
st_blocks
字段表示分配给文件的总块数,块大小为512字节,其中包括了为指针块所分配的空间。更为现代的UNIX系统则使用更大尺寸的逻辑块。
st_blocks
记录了实际分配给文件的磁盘块的数量。如果文件含空洞,该值将小于相应文件字节数字段(st_size
)的值。 -
st_blksize
字段是针对文件系统上文件进行I/O时最优块大小,若I/O所采用的块大小小于该值,则被视为低效。一般而言,st_blksize
的返回值为4096.
文件时间戳
st_atime
、st_mtime
、st_ctime
字段,分别记录了对文件的上次访问时间、上次修改时间,以及文件状态发生改变的上次时间。这三个字段的类型均属time_t
,是标准的UNIX时间格式,记录了自Epoch以来的秒数。
文件属主
新建文件的属主
- 新建文件的用户ID,“取自”进程的有效用户ID。
- 新建文件的组ID“,取自”进程的有效组ID(System V系统默认行为),或父目录的组ID(BSD系统默认)。
当为项目创建目录时,需要该目录下的所有文件隶属于某一特定组,并且可为该组所有成员访问。这是采用后一种行为就非常使用。
新建文件的组ID在这两者间如何取舍,由多种因素决定,新文件所在文件系统类型就是其中之一。
改变文件属主:chown()、fchown()和lchown()
系统调用chown()
、fchown()
、lchown()
可用来改变文件的属主(用户ID)和属组(组ID)。
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
#define _XOPEN_SOURCE 500 /*Or: #define _BSD_SOURCE*/
#include <unistd.h>
int lchown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
All returns 0 on success, or -1 on error.
以上3个系统调用之间的区别类似于stat()
系统调用一族。
chown()
改变由pathname
参数命名文件的所有权。lchown()
用途与chown()
相同,不同之处在与若参数pathname
为一符号链接,则将会改变链接文件本身的所有权,而与该链接所指代的文件无干。fchown()
也会改变文件的所有权,只是文件由打开文件描述符fd
所引用。
参数owner
和group
分别为文件指定新的用户ID和组ID。若只打算改变其中之一,只需将另一参数置为-1,即可令与之相关的ID保持不变。
只有特权级进程(CAP_CHOWN
)才能使用chwon()
改变文件的用户ID。非特权级进程,可使用chown()
将自己所拥有文件的组ID改为其所从属的任一属组的ID,前提是进程的有效用户ID与文件的用户ID相匹配。特权级进程则可将文件的组ID修改为任意值。
文件权限
普通文件的权限
stat
结构中st_mode
字段的低12位定义了文件权限。前3位为专有位,分别是set-user-ID
、set-group-ID
和sticky
位。
头文件<sys/stat.h>
定义了可与stat
结构中st_mode
相与(&)的常量,用于检查特定权限位置位与否。
常量 | 其他值 | 权限位 |
---|---|---|
S_ISUID | 04000 | Set-user-ID |
S_ISGID | 02000 | Set-group-ID |
S_ISVTX | 01000 | Sticky |
———— | ———— | ———— |
S_IRUSR | 0400 | User-read |
S_IWUSR | 0200 | User-write |
S_IXUSR | 0100 | User-execute |
———— | ———— | ———— |
S_IRGRP | 040 | Group-read |
S_IWGRP | 020 | Group-write |
S_IXGRP | 010 | Group-execute |
———— | ———— | ———— |
S_IROTH | 04 | Other-read |
S_IWOTH | 02 | Other-write |
S_IXOTH | 01 | Other-execute |
除表中常量外,还分别将各类权限掩码定义为常量:
S_IRWXU(0700)
、S_IRWXG(070)
和S_IRWXO(07)
。
使用示例
将文件权限掩码转换为ls
风格的字符串。
#define FILE_PERMS_H
#include <sys/types.h>
#include <stdio.h>
#define FP_SPECIAL 1 /*Include set-user-ID, set-group-ID, and sticky
bit information in returned string */
char *filePermStr(mode_t perm, int flags);
#define STR_SIZE sizeof("rwxrwxrwx")
char * /*Returns ls(1)-style string for file permissions mask*/
filePermStr(mode_t perm, int flags)
{
static char str[STR_SIZE];
snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
(perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
(perm & S_IXUSR) ?
(((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x'):
(((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
(perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-' ,
(perm & S_IXGRP) ?
(((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x'):
(((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
(perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
(perm & S_IXOTH) ?
(((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 's' : 'x'):
(((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'S' : '-'),
);
return str;
}
更改文件权限:chmod()和fchmod()
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
#define _XOPEN_SOURCE 500 /*Or: #define _BSD_SOURCE*/
#include <sys/stat.h>
int fchmod(int fd, mode_t mode);
Both returns 0 on success, or -1 on error
-
系统调用
chmod()
更改由pathname
参数所指定文件的权限。若该参数所指为符号链接,调用chmod()
会改变符号链接所指代文件的访问权限,而非对符号链接自身的访问权限。(符号链接自创建起,其所有权限便为所有用户共享,且这些权限也不的更改。对符号链接解引用时,将忽略所有这些权限。) -
系统调用
fchmod()
更改由打开文件描述符fd
所指代文件的权限。
参数mode
用于描述文件的新权限,可以采用八进制数字形式,或者时权限未相或(|)而成的掩码。要想更改文件权限,进程要么具有特权级别(CAP_FOWNER
),要么其有效用户ID与文件的用户ID(属主)相匹配。(准确说来,对Linux系统上的非特权级进程,需与文件用户ID相匹配的是进程的文件系统用户ID,而非其有效用户ID。)
示例:将文件权限设为所有用户仅具有读权限:
if(chmod("myfile", S_IRUSR | S_IRGRP | S_IROTH) == -1)
errExit("chmod");
/*Or equivalently: chmod("myfile", 0444);*/
要修改文件的特定权限位,需要先调用stat()
来获取文件的现有权限,调整想修改的权限位,然后使用chmod()
去更新权限。
struct stat sb;
mode_t mode;
if(stat("myfile", &sb) == -1)
errExit("stat");
mode = (sb.st_mode | S_IWUSR) & ~S_IROTH;
/*onwer-write on, other-read off, remaining bits unchanged*/
if(chmod("myfile", mode) == -1)
errExit("chmod");
执行以上代码,等价于如下shell命令:
$chmod u+w,o-r myfile
i节点标志
体验极差,还比较难,以后再看…
参考资料:《Linux/UNIX系统编程手册》