#include
int stat(const char *restrict pathname, struct stat
*restrict buf);//stat函数以文件的路径名pathname为参数,返回与此命名文件相关的信息结构。
示例:获取利用stat获取文件的修改时间、创建时间等信息
struct timespec{
time_t tv_sec;
long tv_nsec;
}
char* formatdate(char *str, time_t val){
strftime(str, 36, "%Y.%m.%d %H:%M:%S",
localtime(&val));
return str;
}
void Stat(char *pathname){
struct stat *file_info = malloc(sizeof(struct
stat));
stat(pathname, file_info);
char date[36];
printf("Access:%s\n", formatdate(date,
file_info->st_atimespec.tv_sec));
printf("Modify:%s\n", formatdate(date, file_info->st_mtimespec.tv_sec));
printf("Change: %s\n", formatdate(date, file_info->st_ctimespec.tv_sec));
free(file_info);}
以文件路径名为参数,得到的输出结果为:
Access:2017.07.13 14:25:31
Modify:2017.07.13 14:25:31
Change: 2017.07.13 14:25:32
文件类型判断
void CheckFileType(char *pathname){
struct stat *buf = (struct stat *)malloc(sizeof(struct stat));
char *ptr;
stat(pathname, buf);
if(S_ISREG(buf->st_mode))
ptr = "regular file";
else if(S_ISDIR(buf->st_mode))
ptr = "directory";
else if(S_ISCHR(buf->st_mode))
ptr = "character file";
else if(S_ISBLK(buf->st_mode))
ptr = "block file";
else if(S_ISFIFO(buf->st_mode))
ptr = "fifo";
else if(S_ISLNK(buf->st_mode))
ptr = "symbolic link";
else if(S_ISSOCK(buf->st_mode))
ptr = "socket file";
else ptr = "unknown mode";
printf("%s\n", ptr);
}
struct stat数据结构中的st_mode设置了进程的有效uid和gid。
与进程相关联的ID大致有以下这6个
我们实际上是谁:以下两个字段在登录时取自口令文件中的登录项。
实际用户ID
实际组ID
用于文件访问权限检查:以下三个字段决定了我们的文件访问权限
有效用户ID
有效组ID
附属组ID
由exec函数保存:包含了有效用户ID和有效组ID的副本
保存的设置用户ID
保存的设置组ID
通常有效用户ID等于实际用户ID,有效组ID等于实际组ID。
printf(“%d, %d”, getuid(), geteuid());
在main中直接执行得到的结果确实相等。
当执行一个程序文件时,进程的有效用户ID就是实际用户ID,有效组ID就是实际组ID。但是可以在文件模式字(st_mode)中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)。
st_mode值也包含了对文件的访问权限位,所有类型的文件都有访问权限。
每个文件有9个访问权限为,可将它们分为3类:
进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试。注意,以下四部为顺序执行
新文件的用户ID设置为进程的有效用户ID
(1)新文件的组ID可以是进程的有效组ID
(2)新文件的组ID可以是它所在目录的组ID
以下代码说明上述观点:
printf("进程有效组ID%d\n", getegid());
printf("进程有效用户ID%d\n", geteuid());
int fd = creat("/Users/hupac/Public/a.c", 0666);
struct stat *buf = (struct stat *)malloc(sizeof(struct stat));
fstat(fd, buf);
printf("文件有效用户ID%d\n", buf->st_uid);
printf("文件有效组ID%d\n", buf->st_gid);
access和faccessat函数是按实际用户ID和实际组ID进程访问权限测试的。
#include
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
//成功返回0,出错返回-1
示例:
void Access(){
if (access(path, F_OK))
err_sys("access error for %s", path);
else
err_msg("access ok");
if (open(path, 0666) < 0 )
err_sys("open error for %s", path);
else
err_msg("open ok");
}
与文件相关联的有9个访问权限位,umask函数为进程设置文件模式创建屏蔽字,也就是说umask的参数在文件访问权限中是要屏蔽的。
#include
mode_t umask(mode_t cmask);
//参数cmask是9个访问权限位中的若干个按位或构成的
//例如S_IWUSR|S_IWGRP|S_IWOTH,若用这三个参数作为
//umask的参数,则创建文件时,用户不能写、同组用户不能写、
//其他用户也不能写(需首先定义文件创建权限为rwrwrw)。
示例
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) //u.g.o都有读写权限
void Umask(){
umask(0);
if (creat("/Users/hupac/Public/a.c", RWRWRW) < 0)
err_sys("creat error for foo");
umask(S_IROTH|S_IWOTH); //从rw中去掉其他用户的读和写权限
if (creat("/Users/hupac/Public/b.c", RWRWRW) < 0)
err_sys("creat error for bar");
}
运行结果:
-rw-rw-rw- 1 hupac staff 0 7 13 17:16 a.c
-rw-rw—- 1 hupac staff 0 7 13 17:16 b.c
结论:用户可以设置umask值以控制他们所创建文件的默认权限。该值表示成八进制数。如果我们想确保任何用户都能读取文件,则应将umask设置为0。
如umask 027可以更改文件模式创建屏蔽字。
#include
int chmod(const char *pathname, mode_t mode);
int fchmod(const char *pathname, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
//若成功返回0,出错返回-1
chmod(path, S_IRWXU | S_IRGRP);
chmod类函数可以修改文件的访问权限。mode为9个文件访问权限中的若干个按位或组成。
注意:
1. chmod函数更新的只是索引节点最近一次被修改的时间,而文件内容最后被修改的时间并不更改。
2. 为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者该进程必须具有超级用户权限。
概念:如果一个可执行程序文件的这一位被设置了,那么当改程序第一次被执行,在其终止时,程序正文部分(机器指令)的一个副本仍被保存在交换区。因为在系统自举前,文件的正文部分总是在交换区中,此现象称为“粘着”。
#include
int chown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
//成功返回0,出错返回-1
chown函数族可以更改文件的用户ID和组ID。若两个参数owner或group任意一个是-1,则*对应的*ownerID或groupID不变。
stat结构成员st_size表示以字节为单位的文件长度。此字段只对普通文件、目录文件和符号链接有意义。
st_blksize指示对文件I/O较合适的块长度,st_blocks是所分配的实际512字节块块数。
空洞是由所设置的文件偏移量超过文件尾端,并写入了某些数据后造成的。若有大小相同的两个文件,则包含洞的文件比不包含洞的文件占有的磁盘块多。
文件的洞是普通文件的一部分,它是一些空字符但没有存放在磁盘的任何数据块中。洞是Unix文件一直存在的一个特点。
引入文件的洞是为了避免磁盘空间的浪费。
文件洞在Ext2的实现是基于动态数据块的分配:只有当进程需要向一个块写数据时,才真正把这个块分配给文件。
磁盘实际存储中,与文件关联的inode的i_block数组(inode关联的具体物理块号数组)仅存放已分配块的逻辑块号,而数组中的其它元素都为空。
#include
int truncate(const char *pathname, off_t lenth);
int ftruncate(int fd, off_t length);
//成功返回0,出错返回-1
不难想到,若文件原本的长度小于length,则截断时文件长度会增加,而增加的那部分将成为洞。
#include
int link(const char *epath, const char *npath);
int linkat(int efd, const char *existingpath, int nfd, const cahr *new path, int flag);
//成功返回0,出错返回-1
link函数是在npath上复制一个epath文件,两个文件共享一个索引节点,所以link函数创建的硬链接。
当现有文件是符号链接时,flag参数设置了AT_SYMLINK_FOLLOW标志,就创建指向符号链接目标的链接。如果未设置flag参数,则创建一个指向符号链接本身的链接。
void Link(){
if(link(path, "/Users/hupac/Public/a.c") < 0)
err_sys("link error\n");
struct stat *buf = (struct stat *)malloc(sizeof(struct stat));
stat("/Users/hupac/Public/a.c", buf);
printf("inode %llu\n", buf->st_ino);
struct stat *buf1 = (struct stat *)malloc(sizeof(struct stat));
stat(path, buf1);
printf("inode %llu\n", buf1->st_ino);
int fd;
if((fd = open(path, 0644))< 0) //因为x.c文件的权限为rw-r-xr-x
err_sys("%s open error\n", path);
linkat(fd, path, 4, "/Users/hupac/Public/b.c", AT_SYMLINK_FOLLOW);
struct stat *buf2 = (struct stat *)malloc(sizeof(struct stat));
stat("/Users/hupac/Public/b.c", buf2);
printf("symbol link inode%llu\n", buf2->st_ino);
}
运行完成后通过stat命令看到,上例所创建的a.c和b.c与源文件path有着相同的索引节点号,所以这里确实创建的是硬链接,从而得到linkat函数操作非符号链接文件时,并不能创建符号链接。
#include
int unlink(const char *path);
int unlinkat(int fd, const char *path, int flag);
//成功返回0,出错返回-1
测试示例:
void Unlink(){
if(unlink("/Users/hupac/Public/a.c") < 0)
err_sys("unlink error");
else
err_msg("unlink ok");
}
#include
int remove(const char *path);
remove函数也可以解除对一个文件或目录的链接。对于普通文件,remove的功能与unlink相同;对于目录,remove的功能和rmdir相同。
对文件重命名。
#include
int rename(const char *oname, contst char *nname);
int renameat(int ofd, const char *oname, int nfd, const char *nname);
测试示例:
void Rename(){
if(rename(path, "/Users/hupac/public/xx.c") < 0)
err_sys("rename error\n");
else
err_sys("rename ok\n");
}
符号链接是对一个文件的间接指针,它与上一节所述的硬链接有所不同,硬链接直接指向文件的i节点,即是说硬链接文件与原文件共享一个i节点。引入符号链接是为了避开硬链接的一些限制:
测试示例:
先通过 ln -s x.c b.c
void Slink(){
int fd;
if((fd = open("/Users/hupac/Public/b.c", 0644)) < 0)
err_sys("oepn err \n");
}
此时程序open函数无法打开b.c,并且提示too many levels of slinks。
问题在于使用符号链接可能在文件系统中引入循环。
再看一例:
$ mkdir foo
$ touch foo/a
$ ln -s ../foo foo/testdir //创建一个符号链接
此时testdir为目录软链接,指向父目录foo,并且构成了一个循环。同样的,利用fd = open(“/Users/hupac/Public/foo/testdir”, 0644)依然会提示Too many levels of slinks。
错误原因(个人理解):对于open函数,成功时会返回打开文件的文件描述符,此时无法确定返回testdir的文件描述符还是foo的文件描述符。
#include
int symlink(const char *actualpath,const char *sympath)
int symlinkat(const char *apath,int fd, char *spath);
int ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);
//成功返回0,失败返回-1
测试示例
void Symlink(){
if (symlink(path, "/Users/hupac/Public/a.c") < 0)
printf("symlink error\n");
else
printf("symlink ok\n");
char buf[10];
if (readlink( "/Users/hupac/Public/a.c", buf, 10) <0)
printf("read error\n");
else
printf("read ok\n");
printf("buf = %s\n", buf);
}
struct timespec{
time_t tv_sec; /*second*/
long tv_nsec;/*nanosecond*/
}
stat结构中的st_atim, st_ctim, st_mtim,分别标志了文件数据的最后访问时间、i节点状态的最后修改时间、文件数据的最后修改时间。
stat获取文件相关时间信息的示例:
void getTime(){
struct stat * buf = (struct stat *)malloc(sizeof(stat));
stat(path, buf);
char time[50];
printf("atime:%s\n", formatdate(time, buf->st_atimespec.tv_sec));
printf("ctime:%s\n", formatdate(time, buf->st_ctimespec.tv_sec));
printf("mtime:%s\n", formatdate(time, buf->st_mtimespec.tv_sec));
}
输出结果为:
atime:2017.07.14 14:43:22
ctime:2017.07.14 14:37:10
mtime:2017.07.14 14:37:10
状态修改时间st_ctim和修改时间st_mtim的区别:ctime是文件索引节点最后一次被修改的时间,mtime是文件内容最后一次被修改的时间。例如:chmod命令可以只更新ctime,而mtime不变。
#include
int mkdir(const char *path, mode_t mode);
int mkdirat(int fd,const char *path, mode_t mode);
//成功返回0,出错返回-1