1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
| #define _GNU_SOURCE // 启用 GNU 扩展 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> // 包含 open, O_* flags, AT_FDCWD, AT_SYMLINK_NOFOLLOW #include <sys/stat.h> // 包含 utimensat, futimens, timespec, stat #include <string.h> #include <errno.h> #include <time.h> // 包含 time, localtime, strftime
// 辅助函数:打印文件的时间信息 void print_file_times(const char *filename) { struct stat sb; if (stat(filename, &sb) == -1) { perror("stat"); return; }
printf("File: %s\n", filename); printf(" Last Status Change (ctime): %s", ctime(&sb.st_ctime)); // ctime 包含换行符 printf(" Last Modification (mtime): %s", ctime(&sb.st_mtime)); printf(" Last Access (atime): %s", ctime(&sb.st_atime)); printf("\n"); }
// 辅助函数:创建一个测试文件 void create_test_file(const char *filename) { FILE *f = fopen(filename, "w"); if (!f) { perror("fopen"); exit(EXIT_FAILURE); } fprintf(f, "This is a test file for utimensat/futimens example.\n"); fclose(f); printf("Created test file: %s\n\n", filename); }
int main() { const char *test_file = "utimensat_test_file.txt"; const char *test_symlink = "utimensat_test_symlink.txt"; struct timespec new_times[2]; time_t fixed_time_sec; struct tm tm_tmp;
printf("--- Demonstrating utimensat and futimens ---\n");
// 1. 创建一个测试文件 create_test_file(test_file);
// 2. 创建一个指向测试文件的符号链接 if (symlink(test_file, test_symlink) == -1) { perror("symlink"); unlink(test_file); exit(EXIT_FAILURE); } printf("Created symlink: %s -> %s\n\n", test_symlink, test_file);
// 3. 显示初始时间 printf("1. Initial timestamps:\n"); print_file_times(test_file); print_file_times(test_symlink); // 符号链接的时间通常和目标文件一样(除非文件系统特殊支持)
// 4. 准备一个固定的时间 printf("2. Preparing fixed time...\n"); memset(&tm_tmp, 0, sizeof(tm_tmp)); tm_tmp.tm_year = 2023 - 1900; // tm_year is years since 1900 tm_tmp.tm_mon = 10 - 1; // tm_mon is 0-11 tm_tmp.tm_mday = 27; tm_tmp.tm_hour = 10; tm_tmp.tm_min = 0; tm_tmp.tm_sec = 0; tm_tmp.tm_isdst = 0; fixed_time_sec = timegm(&tm_tmp); if (fixed_time_sec == -1) { perror("timegm"); unlink(test_file); unlink(test_symlink); exit(EXIT_FAILURE); }
// 5. 使用 futimens 设置时间 printf("3. --- Using futimens() ---\n"); int fd = open(test_file, O_RDONLY); if (fd == -1) { perror("open test_file"); unlink(test_file); unlink(test_symlink); exit(EXIT_FAILURE); }
new_times[0].tv_sec = fixed_time_sec; // atime new_times[0].tv_nsec = 123456789; // atime 纳秒 new_times[1].tv_sec = fixed_time_sec; // mtime new_times[1].tv_nsec = 987654321; // mtime 纳秒
printf("Setting timestamps using futimens()...\n"); if (futimens(fd, new_times) == -1) { perror("futimens"); } else { printf("futimens() succeeded.\n"); } close(fd); printf("Timestamps after futimens:\n"); print_file_times(test_file);
// 6. 使用 utimensat 设置时间 (相对路径) printf("4. --- Using utimensat() with relative path ---\n"); // 等待几秒,让时间不同 sleep(2);
// 将 atime 设置为当前时间,mtime 保持不变 new_times[0].tv_sec = 0; // 无关紧要 new_times[0].tv_nsec = UTIME_NOW; // 设置为当前时间 new_times[1].tv_sec = 0; // 无关紧要 new_times[1].tv_nsec = UTIME_OMIT; // 保持 mtime 不变
printf("Setting atime to NOW and mtime to OMIT using utimensat(AT_FDCWD, ...)...\n"); // AT_FDCWD 表示相对于当前工作目录解析路径 if (utimensat(AT_FDCWD, test_file, new_times, 0) == -1) { perror("utimensat"); } else { printf("utimensat() succeeded.\n"); } printf("Timestamps after utimensat (atime updated, mtime unchanged):\n"); print_file_times(test_file);
// 7. 使用 utimensat 处理符号链接 printf("5. --- Using utimensat() with symlinks ---\n"); // 准备新的时间 new_times[0].tv_sec = fixed_time_sec + 3600; // atime + 1 小时 new_times[0].tv_nsec = 111111111; new_times[1].tv_sec = fixed_time_sec + 7200; // mtime + 2 小时 new_times[1].tv_nsec = 222222222;
// 默认情况下,utimensat 会跟随符号链接,修改目标文件 printf("Calling utimensat() on symlink WITHOUT AT_SYMLINK_NOFOLLOW...\n"); printf(" This will modify the TARGET file's timestamps.\n"); if (utimensat(AT_FDCWD, test_symlink, new_times, 0) == -1) { perror("utimensat (follow symlink)"); } else { printf("utimensat() succeeded (followed symlink).\n"); } printf("Target file timestamps after utimensat (followed symlink):\n"); print_file_times(test_file); printf("Symlink file timestamps (should be unchanged or linked):\n"); print_file_times(test_symlink);
// 现在使用 AT_SYMLINK_NOFOLLOW 来修改符号链接本身 new_times[0].tv_sec = fixed_time_sec + 10800; // atime + 3 小时 new_times[0].tv_nsec = 333333333; new_times[1].tv_sec = fixed_time_sec + 14400; // mtime + 4 小时 new_times[1].tv_nsec = 444444444;
printf("\nCalling utimensat() on symlink WITH AT_SYMLINK_NOFOLLOW...\n"); printf(" This will modify the SYMLINK's timestamps (if filesystem supports it).\n"); if (utimensat(AT_FDCWD, test_symlink, new_times, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == EOPNOTSUPP) { printf("utimensat with AT_SYMLINK_NOFOLLOW failed: %s\n", strerror(errno)); printf(" This is expected on many filesystems (e.g., ext4).\n"); } else { perror("utimensat (nofollow symlink)"); } } else { printf("utimensat() succeeded (modified symlink itself).\n"); print_file_times(test_symlink); }
// 8. 清理 printf("\n6. --- Cleaning up ---\n"); if (unlink(test_file) == 0) { printf("Deleted test file '%s'.\n", test_file); } else { perror("unlink test_file"); } if (unlink(test_symlink) == 0) { printf("Deleted symlink '%s'.\n", test_symlink); } else { perror("unlink test_symlink"); }
printf("\n--- Summary ---\n"); printf("1. futimens(fd, times[2]): Sets atime/mtime for a file via its file descriptor.\n"); printf("2. utimensat(dirfd, pathname, times[2], flags): Sets atime/mtime via pathname, with more options.\n"); printf("3. Both use struct timespec, providing nanosecond precision.\n"); printf("4. Special timespec values:\n"); printf(" - tv_nsec = UTIME_NOW: Set timestamp to current time.\n"); printf(" - tv_nsec = UTIME_OMIT: Leave timestamp unchanged.\n"); printf("5. utimensat flags:\n"); printf(" - 0 (default): Follow symlinks.\n"); printf(" - AT_SYMLINK_NOFOLLOW: Modify symlink itself (filesystem support varies).\n"); printf(" - dirfd allows relative path resolution (like openat).\n"); printf("6. These are the modern, preferred functions for changing file timestamps.\n");
return 0; }
|