每个文件都有 3 个时间属性字段,它们的意义如下:
[table]
|字段|说明|例子|ls 选项|
|st_atim|文件数据的最后访问时间|read|-u|
|st_mtim|文件数据的最后修改时间|write|默认|
|st_ctim|i 节点状态的最后修改时间|chmod、chown|-c|
[/table]
注意,系统并不维护对一个 i 节点的最后一次访问时间,所以 access 和 stat 函数并不更改这 3 个时间中的任意一个。
futimens 和 utimensat 函数可以用来修改一个文件的访问和修改时间属性,它们都指定纳秒级精度的时间戳,用到的数据结构是与 stat 函数族相同的 timespec 结构。
其中 times 数组参数的第一个元素包含访问时间,第二元素包含修改时间。时间戳可以按以下 4 种方式之一进行指定:
1、如果 times 参数是一个空指针,则访问时间和修改时间都设置为当前时间。
2、如果 times 指向两个 timespec 结构数组,任一数组元素的 tv_nsec 字段的值为 UTIME_NOW,相应的时间戳就设置为当前时间,忽略相应的 tv_sec 字段。
3、如果 times 指向两个 timespec 结构数组,任一数组元素的 tv_nsec 字段的值为 UTIME_OMIT,相应的时间戳保持不变,忽略相应的 tv_sec 字段。
4、如果 times 指向两个 timespec 结构数组,且 tv_nsec 既不是 UTIME_NOW 也不是 UTIME_OMIT,则相应的时间戳设置为相应的 tv_sec 和 tv_nsec 字段的值。
执行这两个函数所要求的优先权取决于 times 参数的值:
1、如果 times 是一个空指针,或者任一 tv_nsec 字段设为 UTIME_NOW,则进程的有效用户 ID 必须等于该文件的所有者 ID。此外,进程对该文件必须具有写权限,或者进程是一个超级用户进程。
2、如果 times 是非空指针,并且任一 tv_nsec 既非 UTIME_NOW 也非 UTIME_OMIT,则进程的有效用户 ID 必须等于该文件的所有者 ID,或者进程必须是一个超级用户进程,对文件只具有写权限是不够的。
3、如果 times 是非空指针,并且两个 tv_nsec 字段的值都是 UTIME_OMIT,就不执行任何的权限检查。
futimens 函数需要打开文件来更改它的时间,utimensat 函数提供了一种使用文件名更改文件时间的方法。pathname 参数是相对于 fd 参数进行计算的。fd 要么是打开目录的文件描述符,要么设置为 AT_FDCWD(强制通过相对于调用进程的当前目录计算 pathname)。如果 pathname 指定了绝对路径,则忽略 fd 参数。utimensat 的 flag 参数可用于进一步修改默认行为。如果设置了 AT_SYMLINK_NOFOLLOW 标志,则符号链接本身的时间就会被修改。默认是跟随符号链接,并把文件的时间改成符号链接的时间。
futimens 和 utimensat 函数都包含在 POSIX.1 中,而在 Single UNIX Specification 的 XSI 扩展选项中还定义了一个 utimes 函数:
该函数对路径名进行操作,times 参数是指向包含两个时间戳(访问时间和修改时间)元素的数组的指针,两个时间戳是用秒和微妙来表示的。
下面这个程序演示了 futimens 函数如何修改文件的访问时间和修改时间。
结果演示:
由此可见,即使我们使用 open 函数的 O_TRUNC 标志修改了文件,但依然可使用 futimens 函数恢复文件原来的访问时间和修改时间,而文件的状态更改时间则会自动更新。
[table]
|字段|说明|例子|ls 选项|
|st_atim|文件数据的最后访问时间|read|-u|
|st_mtim|文件数据的最后修改时间|write|默认|
|st_ctim|i 节点状态的最后修改时间|chmod、chown|-c|
[/table]
注意,系统并不维护对一个 i 节点的最后一次访问时间,所以 access 和 stat 函数并不更改这 3 个时间中的任意一个。
futimens 和 utimensat 函数可以用来修改一个文件的访问和修改时间属性,它们都指定纳秒级精度的时间戳,用到的数据结构是与 stat 函数族相同的 timespec 结构。
#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
/* 返回值:若成功,都返回 0;否则,都返回 -1 */
其中 times 数组参数的第一个元素包含访问时间,第二元素包含修改时间。时间戳可以按以下 4 种方式之一进行指定:
1、如果 times 参数是一个空指针,则访问时间和修改时间都设置为当前时间。
2、如果 times 指向两个 timespec 结构数组,任一数组元素的 tv_nsec 字段的值为 UTIME_NOW,相应的时间戳就设置为当前时间,忽略相应的 tv_sec 字段。
3、如果 times 指向两个 timespec 结构数组,任一数组元素的 tv_nsec 字段的值为 UTIME_OMIT,相应的时间戳保持不变,忽略相应的 tv_sec 字段。
4、如果 times 指向两个 timespec 结构数组,且 tv_nsec 既不是 UTIME_NOW 也不是 UTIME_OMIT,则相应的时间戳设置为相应的 tv_sec 和 tv_nsec 字段的值。
执行这两个函数所要求的优先权取决于 times 参数的值:
1、如果 times 是一个空指针,或者任一 tv_nsec 字段设为 UTIME_NOW,则进程的有效用户 ID 必须等于该文件的所有者 ID。此外,进程对该文件必须具有写权限,或者进程是一个超级用户进程。
2、如果 times 是非空指针,并且任一 tv_nsec 既非 UTIME_NOW 也非 UTIME_OMIT,则进程的有效用户 ID 必须等于该文件的所有者 ID,或者进程必须是一个超级用户进程,对文件只具有写权限是不够的。
3、如果 times 是非空指针,并且两个 tv_nsec 字段的值都是 UTIME_OMIT,就不执行任何的权限检查。
futimens 函数需要打开文件来更改它的时间,utimensat 函数提供了一种使用文件名更改文件时间的方法。pathname 参数是相对于 fd 参数进行计算的。fd 要么是打开目录的文件描述符,要么设置为 AT_FDCWD(强制通过相对于调用进程的当前目录计算 pathname)。如果 pathname 指定了绝对路径,则忽略 fd 参数。utimensat 的 flag 参数可用于进一步修改默认行为。如果设置了 AT_SYMLINK_NOFOLLOW 标志,则符号链接本身的时间就会被修改。默认是跟随符号链接,并把文件的时间改成符号链接的时间。
futimens 和 utimensat 函数都包含在 POSIX.1 中,而在 Single UNIX Specification 的 XSI 扩展选项中还定义了一个 utimes 函数:
#include <sys/time.h>
int utimes(const char *pathname, const struct timeval times[2]);
/* 返回值:若成功,返回 0;否则,返回 -1 */
/*
struct timeval{
time_t tv_sec; // seconds
long tv_usec; // microseconds
};
*/
该函数对路径名进行操作,times 参数是指向包含两个时间戳(访问时间和修改时间)元素的数组的指针,两个时间戳是用秒和微妙来表示的。
下面这个程序演示了 futimens 函数如何修改文件的访问时间和修改时间。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char *argv[]){
int i, fd;
struct stat statbuf;
struct timespec times[2];
for(i=1; i<argc; i++){
if(stat(argv[i], &statbuf) < 0){ // fetch current times
printf("%s: stat error\n", argv[i]);
continue;
}
if((fd=open(argv[i], O_RDWR |O_TRUNC)) < 0){ // truncate
printf("%s: open error\n", argv[i]);
continue;
}
times[0] = statbuf.st_atim;
times[1] = statbuf.st_mtim;
if(futimens(fd, times) < 0) // reset times
printf("%s: futimens error\n", argv[i]);
close(fd);
}
exit(0);
}
结果演示:
$ ls -l changemod times # 查看文件最后修改时间
-rw-r--r--. 1 lei root 0 7月 18 00:03 changemod
-rw-r--r--. 1 lei root 0 7月 18 00:04 times
$
$ ls -lu changemod times # 查看文件最后访问时间
-rw-r--r--. 1 lei root 0 7月 18 00:04 changemod
-rw-r--r--. 1 lei root 0 7月 18 00:16 times
$
$ date # 打印当前日期
2017年 07月 18日 星期二 00:20:20 CST
$
$ ./futimensDemo.out changemod times # 执行程序
$
$ ls -l changemod times # 再次查看文件最后修改时间
-rw-r--r--. 1 lei root 0 7月 18 00:03 changemod
-rw-r--r--. 1 lei root 0 7月 18 00:04 times
$
$ ls -lu changemod times # 再次查看文件最后访问时间
-rw-r--r--. 1 lei root 0 7月 18 00:04 changemod
-rw-r--r--. 1 lei root 0 7月 18 00:16 times
$
$ ls -lc changemod times # 检查文件状态更改时间
-rw-r--r--. 1 lei root 0 7月 18 00:20 changemod
-rw-r--r--. 1 lei root 0 7月 18 00:20 times
$
由此可见,即使我们使用 open 函数的 O_TRUNC 标志修改了文件,但依然可使用 futimens 函数恢复文件原来的访问时间和修改时间,而文件的状态更改时间则会自动更新。