open_by_handle_at系统调用及示例

open_by_handle_at 函数详解

  1. 函数介绍

open_by_handle_at 是 Linux 系统中用于通过文件句柄打开文件的系统调用。可以把文件句柄想象成”文件的身份证号码”,而 open_by_handle_at 就是通过这个”身份证号码”来访问文件的工具。

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

与传统的通过路径名打开文件不同,open_by_handle_at 不依赖于文件路径,即使文件被移动、重命名或删除后恢复,只要文件系统支持,仍然可以通过句柄访问文件。这就像你通过身份证号码在任何地方都能找到一个人一样。

  1. 函数原型
1
2
3
4
5
#define _GNU_SOURCE
#include <fcntl.h>

int open_by_handle_at(int mount_fd, struct file_handle *handle, int flags);

  1. 功能

open_by_handle_at 函数用于通过文件句柄打开文件,返回一个文件描述符,可以像普通文件一样进行读写操作。

  1. 参数

mount_fd: 挂载点文件描述符

  • 可以是任何该文件系统中的文件描述符

  • 通常使用 AT_FDCWD 表示当前工作目录

  • 也可以是该文件系统根目录的文件描述符

handle: 指向 file_handle 结构体的指针

  • 包含之前通过 name_to_handle_at 获取的文件句柄

flags: 文件打开标志

  • O_RDONLY: 只读打开

  • O_WRONLY: 只写打开

  • O_RDWR: 读写打开

  • O_CREAT, O_TRUNC 等标志不适用(文件必须已存在)

  1. file_handle 结构体
1
2
3
4
5
6
struct file_handle {
unsigned int handle_bytes; /* 句柄数据的字节数 */
int handle_type; /* 句柄类型 */
unsigned char f_handle&#91;0]; /* 句柄数据(变长数组)*/
};

  1. 返回值
  • 成功: 返回文件描述符(非负整数)

  • 失败: 返回 -1,并设置相应的 errno 错误码

  1. 常见错误码
  • EACCES: 权限不足

  • EBADF: mount_fd 不是有效的文件描述符

  • EFAULT: handle 指针无效

  • EINVAL: 参数无效(如 handle 为 NULL 或 flags 无效)

  • EMFILE: 进程文件描述符过多

  • ENFILE: 系统文件描述符过多

  • ENOMEM: 内存不足

  • ENOSPC: 磁盘空间不足(写操作)

  • ENOTDIR: mount_fd 不是目录

  • EOPNOTSUPP: 文件系统不支持文件句柄

  • ESTALE: 文件句柄已失效(文件可能已被删除)

  1. 相似函数或关联函数
  • name_to_handle_at: 获取文件句柄

  • open/openat: 通过路径名打开文件

  • openat2: 增强版的 openat

  • fstat: 通过文件描述符获取文件状态

  • read/write: 文件读写操作

  1. 示例代码

示例1:基础用法 - 通过句柄打开文件

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

// 创建测试文件
int create_test_file(const char *filename) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

const char *content = "这是测试文件的内容\n用于演示通过句柄打开文件的功能\n";
ssize_t bytes_written = write(fd, content, strlen(content));
if (bytes_written == -1) {
perror("写入文件失败");
close(fd);
return -1;
}

printf("创建测试文件: %s (写入 %zd 字节)\n", filename, bytes_written);
close(fd);
return 0;
}

// 获取文件句柄
int get_file_handle(const char *filename, struct file_handle **handle, int *mount_id) {
size_t handle_size = sizeof(struct file_handle);
*handle = malloc(handle_size);
if (!*handle) {
perror("内存分配失败");
return -1;
}

(*handle)->handle_bytes = 0;

int result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
if (result == -1 && errno == EOVERFLOW) {
handle_size = sizeof(struct file_handle) + (*handle)->handle_bytes;
free(*handle);

*handle = malloc(handle_size);
if (!*handle) {
perror("内存分配失败");
return -1;
}

result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
}

return result;
}

// 通过句柄打开文件
int open_file_by_handle(struct file_handle *handle, int flags) {
printf("通过句柄打开文件 (标志: 0x%x)...\n", flags);

int fd = open_by_handle_at(AT_FDCWD, handle, flags);
if (fd != -1) {
printf("✓ 成功打开文件,文件描述符: %d\n", fd);

// 获取文件信息
struct stat st;
if (fstat(fd, &st) == 0) {
printf(" 文件大小: %ld 字节\n", (long)st.st_size);
printf(" 修改时间: %s", ctime(&st.st_mtime));
printf(" 权限: %o\n", st.st_mode & 0777);
}

return fd;
} else {
printf("✗ 打开文件失败: %s\n", strerror(errno));
return -1;
}
}

// 读取文件内容
int read_file_content(int fd) {
char buffer&#91;256];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);

if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf("读取到的内容 (%zd 字节):\n%s", bytes_read, buffer);
return 0;
} else if (bytes_read == 0) {
printf("文件为空\n");
return 0;
} else {
perror("读取文件失败");
return -1;
}
}

int main() {
const char *test_file = "handle_open_test.txt";
struct file_handle *handle = NULL;
int mount_id;
int fd;

printf("=== open_by_handle_at 基础示例 ===\n\n");

// 创建测试文件
if (create_test_file(test_file) == -1) {
return 1;
}

// 获取文件句柄
printf("\n1. 获取文件句柄:\n");
if (get_file_handle(test_file, &handle, &mount_id) == 0) {
printf(" ✓ 成功获取文件句柄\n");
printf(" 挂载 ID: %d\n", mount_id);
printf(" 句柄类型: %d\n", handle->handle_type);
printf(" 句柄大小: %u 字节\n", handle->handle_bytes);
} else {
printf(" ✗ 获取文件句柄失败: %s\n", strerror(errno));
unlink(test_file);
return 1;
}

// 通过句柄以只读方式打开文件
printf("\n2. 通过句柄以只读方式打开文件:\n");
fd = open_file_by_handle(handle, O_RDONLY);
if (fd != -1) {
read_file_content(fd);
close(fd);
}

// 通过句柄以读写方式打开文件
printf("\n3. 通过句柄以读写方式打开文件:\n");
fd = open_file_by_handle(handle, O_RDWR);
if (fd != -1) {
printf(" 向文件追加内容...\n");
const char *append_content = "追加的内容\n";
lseek(fd, 0, SEEK_END); // 移动到文件末尾
ssize_t bytes_written = write(fd, append_content, strlen(append_content));
if (bytes_written > 0) {
printf(" ✓ 成功追加 %zd 字节\n", bytes_written);
}

// 重新读取文件内容
printf(" 重新读取文件内容:\n");
lseek(fd, 0, SEEK_SET); // 移动到文件开头
read_file_content(fd);

close(fd);
}

// 测试错误情况
printf("\n4. 测试错误情况:\n");
printf(" 尝试使用无效句柄打开文件:\n");
struct file_handle invalid_handle = {0};
int invalid_fd = open_by_handle_at(AT_FDCWD, &invalid_handle, O_RDONLY);
if (invalid_fd == -1) {
printf(" ✓ 正确处理无效句柄: %s\n", strerror(errno));
}

// 清理资源
if (handle) {
free(handle);
}
unlink(test_file);

printf("\n=== 文件句柄打开特点 ===\n");
printf("1. 路径无关: 不依赖文件路径名\n");
printf("2. 持久性: 文件移动后仍可访问\n");
printf("3. 安全性: 防止路径遍历攻击\n");
printf("4. 唯一性: 每个文件有唯一句柄\n");
printf("5. 系统级: 由内核维护,无法伪造\n");
printf("\n");
printf("使用场景:\n");
printf("1. 文件监控系统\n");
printf("2. 备份和同步工具\n");
printf("3. 容器文件系统\n");
printf("4. 网络文件传输\n");
printf("5. 安全文件访问\n");

return 0;
}

示例2:文件句柄的持久性和安全性

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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <time.h>

// 文件信息结构体
struct persistent_file {
char original_name&#91;256];
char current_name&#91;256];
struct file_handle *handle;
int mount_id;
time_t create_time;
};

// 创建测试文件
int create_test_file_with_content(const char *filename, const char *content) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

ssize_t bytes_written = write(fd, content, strlen(content));
if (bytes_written == -1) {
perror("写入文件失败");
close(fd);
return -1;
}

printf("创建测试文件: %s (%zd 字节)\n", filename, bytes_written);
close(fd);
return 0;
}

// 获取文件句柄
int get_file_handle_safe(const char *filename, struct file_handle **handle, int *mount_id) {
size_t handle_size = sizeof(struct file_handle);
*handle = malloc(handle_size);
if (!*handle) {
return -1;
}

(*handle)->handle_bytes = 0;

int result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
if (result == -1 && errno == EOVERFLOW) {
handle_size = sizeof(struct file_handle) + (*handle)->handle_bytes;
free(*handle);

*handle = malloc(handle_size);
if (!*handle) {
return -1;
}

result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
}

return result;
}

// 通过句柄安全地打开文件
int open_file_by_handle_safe(struct file_handle *handle, int flags, const char *description) {
printf("通过句柄打开文件: %s\n", description ? description : "未知文件");

int fd = open_by_handle_at(AT_FDCWD, handle, flags);
if (fd != -1) {
printf(" ✓ 成功打开文件 (fd: %d)\n", fd);
return fd;
} else {
printf(" ✗ 打开文件失败: %s\n", strerror(errno));
return -1;
}
}

// 读取并验证文件内容
int verify_file_content(int fd, const char *expected_content, const char *description) {
if (lseek(fd, 0, SEEK_SET) == -1) {
perror("定位文件开头失败");
return -1;
}

char *buffer = malloc(strlen(expected_content) + 1);
if (!buffer) {
perror("内存分配失败");
return -1;
}

ssize_t bytes_read = read(fd, buffer, strlen(expected_content));
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" %s内容验证: ", description ? description : "");
if (strcmp(buffer, expected_content) == 0) {
printf("通过 ✓\n");
free(buffer);
return 0;
} else {
printf("失败 ✗\n");
printf(" 期望: %s", expected_content);
printf(" 实际: %s", buffer);
free(buffer);
return -1;
}
} else {
perror("读取文件失败");
free(buffer);
return -1;
}
}

int main() {
struct persistent_file file_info;
const char *original_name = "persistent_original.txt";
const char *renamed_name = "persistent_renamed.txt";
const char *content = "这是持久化文件的内容\n创建时间: ";

printf("=== 文件句柄持久性和安全性示例 ===\n\n");

// 构造带时间戳的内容
char full_content&#91;512];
time_t now = time(NULL);
snprintf(full_content, sizeof(full_content), "%s%s", content, ctime(&now));

// 创建测试文件
printf("1. 创建测试文件:\n");
if (create_test_file_with_content(original_name, full_content) == -1) {
return 1;
}

strncpy(file_info.original_name, original_name, sizeof(file_info.original_name) - 1);
file_info.original_name&#91;sizeof(file_info.original_name) - 1] = '\0';
file_info.create_time = now;

// 获取文件句柄
printf("\n2. 获取文件句柄:\n");
if (get_file_handle_safe(original_name, &file_info.handle, &file_info.mount_id) == 0) {
printf(" ✓ 成功获取文件句柄\n");
printf(" 挂载 ID: %d\n", file_info.mount_id);
printf(" 句柄大小: %u 字节\n", file_info.handle->handle_bytes);
} else {
printf(" ✗ 获取文件句柄失败: %s\n", strerror(errno));
unlink(original_name);
return 1;
}

// 通过句柄访问原始文件
printf("\n3. 通过句柄访问原始文件:\n");
int fd = open_file_by_handle_safe(file_info.handle, O_RDONLY, "原始文件");
if (fd != -1) {
if (verify_file_content(fd, full_content, "原始文件") == 0) {
printf(" ✓ 原始文件内容验证通过\n");
}
close(fd);
}

// 重命名文件
printf("\n4. 重命名文件 (模拟文件移动):\n");
if (rename(original_name, renamed_name) == 0) {
printf(" ✓ 成功重命名文件: %s -> %s\n", original_name, renamed_name);
strncpy(file_info.current_name, renamed_name, sizeof(file_info.current_name) - 1);
file_info.current_name&#91;sizeof(file_info.current_name) - 1] = '\0';
} else {
printf(" ✗ 重命名文件失败: %s\n", strerror(errno));
free(file_info.handle);
unlink(original_name);
return 1;
}

// 通过句柄访问重命名后的文件
printf("\n5. 通过句柄访问重命名后的文件:\n");
fd = open_file_by_handle_safe(file_info.handle, O_RDONLY, "重命名后的文件");
if (fd != -1) {
if (verify_file_content(fd, full_content, "重命名文件") == 0) {
printf(" ✓ 重命名文件内容验证通过\n");
printf(" ✓ 证明: 文件句柄不受文件名变化影响\n");
}
close(fd);
}

// 创建符号链接并测试
printf("\n6. 创建符号链接测试:\n");
const char *symlink_name = "persistent_symlink.txt";
if (symlink(renamed_name, symlink_name) == 0) {
printf(" ✓ 创建符号链接: %s -> %s\n", symlink_name, renamed_name);

// 获取符号链接的句柄
struct file_handle *symlink_handle = NULL;
int symlink_mount_id;
if (get_file_handle_safe(symlink_name, &symlink_handle, &symlink_mount_id) == 0) {
printf(" ✓ 获取符号链接句柄成功\n");

// 通过符号链接句柄打开
fd = open_file_by_handle_safe(symlink_handle, O_RDONLY, "符号链接");
if (fd != -1) {
if (verify_file_content(fd, full_content, "符号链接") == 0) {
printf(" ✓ 符号链接内容验证通过\n");
}
close(fd);
}
free(symlink_handle);
}
unlink(symlink_name);
}

// 测试不同打开标志
printf("\n7. 测试不同打开标志:\n");

// 只读打开
printf(" 只读打开 (O_RDONLY):\n");
fd = open_file_by_handle_safe(file_info.handle, O_RDONLY, "只读模式");
if (fd != -1) {
printf(" ✓ 只读打开成功\n");
close(fd);
}

// 读写打开
printf(" 读写打开 (O_RDWR):\n");
fd = open_file_by_handle_safe(file_info.handle, O_RDWR, "读写模式");
if (fd != -1) {
printf(" ✓ 读写打开成功\n");
close(fd);
}

// 只写打开
printf(" 只写打开 (O_WRONLY):\n");
fd = open_file_by_handle_safe(file_info.handle, O_WRONLY, "只写模式");
if (fd != -1) {
printf(" ✓ 只写打开成功\n");
close(fd);
}

// 尝试写入只读打开的文件
printf(" 测试写入权限:\n");
fd = open_file_by_handle_safe(file_info.handle, O_RDONLY, "只读模式测试写入");
if (fd != -1) {
const char *test_write = "测试写入";
ssize_t write_result = write(fd, test_write, strlen(test_write));
if (write_result == -1) {
printf(" ✓ 正确拒绝写入操作: %s\n", strerror(errno));
} else {
printf(" ✗ 意外允许写入操作\n");
}
close(fd);
}

// 清理资源
printf("\n8. 清理资源:\n");
free(file_info.handle);
unlink(renamed_name);
printf(" ✓ 清理完成\n");

printf("\n=== 文件句柄安全性和持久性总结 ===\n");
printf("安全性优势:\n");
printf("1. 路径无关: 不受符号链接攻击影响\n");
printf("2. 权限控制: 仍然遵循文件系统权限\n");
printf("3. 系统级: 由内核维护,无法伪造\n");
printf("4. 访问控制: 可以通过打开标志控制访问权限\n");
printf("\n");
printf("持久性优势:\n");
printf("1. 文件移动: 重命名后句柄仍然有效\n");
printf("2. 目录重组: 目录结构调整不影响句柄\n");
printf("3. 跨会话: 可以在不同进程间传递\n");
printf("4. 稳定标识: 提供稳定的文件标识机制\n");
printf("\n");
printf("适用场景:\n");
printf("1. 文件监控和审计\n");
printf("2. 备份和同步系统\n");
printf("3. 容器文件系统\n");
printf("4. 网络文件传输\n");
printf("5. 安全文件访问控制\n");

return 0;
}

示例3:完整的文件句柄管理工具

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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <time.h>

// 配置结构体
struct handle_tool_config {
char *filename;
char *handle_file;
int create_handle;
int open_file;
int show_info;
int verbose;
int flags;
char *output_file;
};

// 保存文件句柄到文件
int save_handle_to_file(const struct file_handle *handle, int mount_id, const char *filename) {
FILE *fp = fopen(filename, "wb");
if (!fp) {
perror("打开句柄文件失败");
return -1;
}

// 写入挂载 ID
if (fwrite(&mount_id, sizeof(mount_id), 1, fp) != 1) {
perror("写入挂载 ID 失败");
fclose(fp);
return -1;
}

// 写入句柄大小
if (fwrite(&handle->handle_bytes, sizeof(handle->handle_bytes), 1, fp) != 1) {
perror("写入句柄大小失败");
fclose(fp);
return -1;
}

// 写入句柄类型
if (fwrite(&handle->handle_type, sizeof(handle->handle_type), 1, fp) != 1) {
perror("写入句柄类型失败");
fclose(fp);
return -1;
}

// 写入句柄数据
if (fwrite(handle->f_handle, handle->handle_bytes, 1, fp) != 1) {
perror("写入句柄数据失败");
fclose(fp);
return -1;
}

fclose(fp);
printf("✓ 句柄已保存到: %s\n", filename);
return 0;
}

// 从文件加载文件句柄
struct file_handle* load_handle_from_file(const char *filename, int *mount_id) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
perror("打开句柄文件失败");
return NULL;
}

// 读取挂载 ID
if (fread(mount_id, sizeof(*mount_id), 1, fp) != 1) {
perror("读取挂载 ID 失败");
fclose(fp);
return NULL;
}

// 读取句柄大小
unsigned int handle_bytes;
if (fread(&handle_bytes, sizeof(handle_bytes), 1, fp) != 1) {
perror("读取句柄大小失败");
fclose(fp);
return NULL;
}

// 分配句柄内存
size_t handle_size = sizeof(struct file_handle) + handle_bytes;
struct file_handle *handle = malloc(handle_size);
if (!handle) {
perror("内存分配失败");
fclose(fp);
return NULL;
}

handle->handle_bytes = handle_bytes;

// 读取句柄类型
if (fread(&handle->handle_type, sizeof(handle->handle_type), 1, fp) != 1) {
perror("读取句柄类型失败");
free(handle);
fclose(fp);
return NULL;
}

// 读取句柄数据
if (fread(handle->f_handle, handle_bytes, 1, fp) != 1) {
perror("读取句柄数据失败");
free(handle);
fclose(fp);
return NULL;
}

fclose(fp);
printf("✓ 从 %s 加载句柄成功\n", filename);
return handle;
}

// 获取文件句柄
int get_file_handle_safe(const char *filename, struct file_handle **handle, int *mount_id) {
size_t handle_size = sizeof(struct file_handle);
*handle = malloc(handle_size);
if (!*handle) {
return -1;
}

(*handle)->handle_bytes = 0;

int result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
if (result == -1 && errno == EOVERFLOW) {
handle_size = sizeof(struct file_handle) + (*handle)->handle_bytes;
free(*handle);

*handle = malloc(handle_size);
if (!*handle) {
return -1;
}

result = name_to_handle_at(AT_FDCWD, filename, *handle, mount_id, 0);
}

return result;
}

// 通过句柄打开文件
int open_file_by_handle_safe(struct file_handle *handle, int flags, const char *description) {
if (description) {
printf("通过句柄打开文件: %s\n", description);
}

int fd = open_by_handle_at(AT_FDCWD, handle, flags);
if (fd != -1) {
if (description) {
printf(" ✓ 成功打开文件 (fd: %d)\n", fd);
}
return fd;
} else {
if (description) {
printf(" ✗ 打开文件失败: %s\n", strerror(errno));
}
return -1;
}
}

// 显示句柄信息
void show_handle_info(const struct file_handle *handle, int mount_id) {
printf("=== 文件句柄信息 ===\n");
printf("挂载 ID: %d\n", mount_id);
printf("句柄类型: %d\n", handle->handle_type);
printf("句柄大小: %u 字节\n", handle->handle_bytes);

printf("句柄数据 (十六进制): ");
for (unsigned int i = 0; i < handle->handle_bytes && i < 64; i++) {
printf("%02x", handle->f_handle&#91;i]);
}
if (handle->handle_bytes > 64) {
printf("...(还有 %u 字节)", handle->handle_bytes - 64);
}
printf("\n");
}

// 复制文件内容
int copy_file_content(int src_fd, const char *output_filename) {
int dst_fd = open(output_filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (dst_fd == -1) {
perror("创建输出文件失败");
return -1;
}

char buffer&#91;4096];
ssize_t bytes_read, bytes_written;
off_t total_bytes = 0;

while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("写入输出文件失败");
close(dst_fd);
return -1;
}
total_bytes += bytes_written;
}

if (bytes_read == -1) {
perror("读取源文件失败");
close(dst_fd);
return -1;
}

close(dst_fd);
printf("✓ 成功复制 %ld 字节到: %s\n", (long)total_bytes, output_filename);
return 0;
}

// 显示帮助信息
void show_help(const char *program_name) {
printf("用法: %s &#91;选项]\n", program_name);
printf("\n选项:\n");
printf(" -f, --file=FILE 源文件名\n");
printf(" -h, --handle=FILE 句柄文件名\n");
printf(" -c, --create 创建文件句柄\n");
printf(" -o, --open 通过句柄打开文件\n");
printf(" -i, --info 显示句柄信息\n");
printf(" -r, --read-only 以只读方式打开\n");
printf(" -w, --read-write 以读写方式打开\n");
printf(" -O, --output=FILE 输出文件名(用于复制)\n");
printf(" -v, --verbose 详细输出\n");
printf(" --help 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -f /etc/passwd -c -h passwd.handle # 创建句柄\n", program_name);
printf(" %s -h passwd.handle -o -r # 通过句柄只读打开\n", program_name);
printf(" %s -h passwd.handle -o -w -O copy.txt # 通过句柄复制文件\n", program_name);
printf(" %s -h passwd.handle -i # 显示句柄信息\n", program_name);
}

int main(int argc, char *argv&#91;]) {
struct handle_tool_config config = {
.filename = NULL,
.handle_file = NULL,
.create_handle = 0,
.open_file = 0,
.show_info = 0,
.verbose = 0,
.flags = O_RDONLY,
.output_file = NULL
};

printf("=== 文件句柄管理工具 ===\n\n");

// 解析命令行参数
static struct option long_options&#91;] = {
{"file", required_argument, 0, 'f'},
{"handle", required_argument, 0, 'h'},
{"create", no_argument, 0, 'c'},
{"open", no_argument, 0, 'o'},
{"info", no_argument, 0, 'i'},
{"read-only", no_argument, 0, 'r'},
{"read-write", no_argument, 0, 'w'},
{"output", required_argument, 0, 'O'},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 1000},
{0, 0, 0, 0}
};

int opt;
while ((opt = getopt_long(argc, argv, "f:h:coirwO:v", long_options, NULL)) != -1) {
switch (opt) {
case 'f':
config.filename = optarg;
break;
case 'h':
config.handle_file = optarg;
break;
case 'c':
config.create_handle = 1;
break;
case 'o':
config.open_file = 1;
break;
case 'i':
config.show_info = 1;
break;
case 'r':
config.flags = O_RDONLY;
break;
case 'w':
config.flags = O_RDWR;
break;
case 'O':
config.output_file = optarg;
break;
case 'v':
config.verbose = 1;
break;
case 1000: // --help
show_help(argv&#91;0]);
return 0;
default:
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv&#91;0]);
return 1;
}
}

// 验证参数
if (!config.create_handle && !config.open_file && !config.show_info) {
show_help(argv&#91;0]);
return 0;
}

struct file_handle *handle = NULL;
int mount_id;

// 如果需要创建句柄
if (config.create_handle && config.filename) {
if (access(config.filename, F_OK) != 0) {
fprintf(stderr, "文件不存在: %s\n", config.filename);
return 1;
}

printf("创建文件句柄: %s\n", config.filename);

if (get_file_handle_safe(config.filename, &handle, &mount_id) == 0) {
printf("✓ 成功获取文件句柄\n");

if (config.show_info) {
show_handle_info(handle, mount_id);
}

if (config.handle_file) {
if (save_handle_to_file(handle, mount_id, config.handle_file) == 0) {
printf("✓ 句柄保存成功\n");
} else {
fprintf(stderr, "句柄保存失败\n");
free(handle);
return 1;
}
}
} else {
fprintf(stderr, "获取文件句柄失败: %s\n", strerror(errno));
return 1;
}
}
// 如果需要加载句柄
else if (config.handle_file) {
if (access(config.handle_file, F_OK) != 0) {
fprintf(stderr, "句柄文件不存在: %s\n", config.handle_file);
return 1;
}

handle = load_handle_from_file(config.handle_file, &mount_id);
if (!handle) {
return 1;
}

if (config.show_info) {
show_handle_info(handle, mount_id);
}
} else {
fprintf(stderr, "需要指定文件或句柄文件\n");
show_help(argv&#91;0]);
return 1;
}

// 通过句柄打开文件
if (config.open_file && handle) {
int fd = open_file_by_handle_safe(handle, config.flags,
config.filename ? config.filename : "加载的句柄");
if (fd != -1) {
printf("✓ 文件打开成功\n");

// 获取文件信息
struct stat st;
if (fstat(fd, &st) == 0) {
printf("文件信息:\n");
printf(" 大小: %ld 字节\n", (long)st.st_size);
printf(" 权限: %o\n", st.st_mode & 0777);
printf(" 修改时间: %s", ctime(&st.st_mtime));
}

// 如果指定了输出文件,复制内容
if (config.output_file) {
if (copy_file_content(fd, config.output_file) != 0) {
close(fd);
free(handle);
return 1;
}
}
// 否则显示部分内容
else if (config.flags & (O_RDONLY | O_RDWR)) {
printf("文件内容预览:\n");
char buffer&#91;512];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
// 只显示前 200 个字符
if (strlen(buffer) > 200) {
buffer&#91;200] = '\0';
printf("%s...\n", buffer);
} else {
printf("%s\n", buffer);
}
}
}

close(fd);
} else {
free(handle);
return 1;
}
}

// 清理资源
if (handle) {
free(handle);
}

printf("\n=== 文件句柄工具使用建议 ===\n");
printf("适用场景:\n");
printf("1. 文件监控和审计系统\n");
printf("2. 备份和同步工具\n");
printf("3. 容器和虚拟化环境\n");
printf("4. 网络文件传输\n");
printf("5. 安全文件访问控制\n");
printf("\n");
printf("安全建议:\n");
printf("1. 妥善保管句柄文件\n");
printf("2. 使用适当的文件权限\n");
printf("3. 验证句柄的有效性\n");
printf("4. 及时关闭文件描述符\n");
printf("5. 处理句柄失效的情况\n");
printf("\n");
printf("性能优化:\n");
printf("1. 批量处理多个文件\n");
printf("2. 缓存常用文件句柄\n");
printf("3. 异步操作大文件\n");
printf("4. 合理设置缓冲区大小\n");

return 0;
}

编译和运行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 编译示例程序
gcc -o open_by_handle_at_example1 example1.c
gcc -o open_by_handle_at_example2 example2.c
gcc -o open_by_handle_at_example3 example3.c

# 运行示例
./open_by_handle_at_example1
./open_by_handle_at_example2
./open_by_handle_at_example3 --help

# 基本操作示例
./open_by_handle_at_example3 -f /etc/passwd -c -h passwd.handle
./open_by_handle_at_example3 -h passwd.handle -o -r
./open_by_handle_at_example3 -h passwd.handle -i
./open_by_handle_at_example3 -h passwd.handle -o -w -O passwd_copy.txt

系统要求检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检查内核版本(需要 2.6.39+)
uname -r

# 检查文件系统支持
grep -i handle /boot/config-$(uname -r)

# 检查系统调用支持
grep -w open_by_handle_at /usr/include/asm/unistd_64.h

# 查看文件系统类型
df -T /etc/passwd

# 检查当前用户权限
id

重要注意事项

内核版本: 需要 Linux 2.6.39+ 内核支持

文件系统: 不是所有文件系统都支持文件句柄

权限要求: 需要对文件有适当访问权限

错误处理: 始终检查返回值和 errno

内存管理: 正确分配和释放句柄内存

文件描述符: 及时关闭打开的文件描述符

句柄失效: 处理文件删除导致的句柄失效

实际应用场景

文件监控: 监控特定文件的变更而不依赖路径

备份系统: 标识和跟踪备份文件

容器技术: 容器内文件系统管理

网络传输: 安全的文件标识和传输

审计系统: 文件访问审计和追踪

数据库系统: 文件标识和管理

最佳实践

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
// 安全的文件句柄打开函数
int safe_open_by_handle(struct file_handle *handle, int flags, const char *description) {
// 验证参数
if (!handle) {
errno = EINVAL;
return -1;
}

// 验证标志
if (flags & (O_CREAT | O_EXCL | O_TRUNC)) {
fprintf(stderr, "警告: 文件句柄打开不支持创建/截断标志\n");
flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
}

// 打开文件
int fd = open_by_handle_at(AT_FDCWD, handle, flags);
if (fd == -1) {
switch (errno) {
case EACCES:
fprintf(stderr, "权限不足访问文件");
if (description) fprintf(stderr, ": %s", description);
fprintf(stderr, "\n");
break;
case ESTALE:
fprintf(stderr, "文件句柄已失效");
if (description) fprintf(stderr, ": %s", description);
fprintf(stderr, "\n");
break;
case EOPNOTSUPP:
fprintf(stderr, "文件系统不支持文件句柄");
if (description) fprintf(stderr, ": %s", description);
fprintf(stderr, "\n");
break;
}
} else if (description) {
printf("通过句柄成功打开文件: %s (fd: %d)\n", description, fd);
}

return fd;
}

// 句柄管理结构体
typedef struct {
struct file_handle *handle;
int mount_id;
int fd;
char *filename;
time_t create_time;
} handle_manager_t;

// 初始化句柄管理器
int handle_manager_init(handle_manager_t *mgr, const char *filename) {
mgr->filename = strdup(filename);
if (!mgr->filename) {
return -1;
}

mgr->create_time = time(NULL);
mgr->fd = -1;
mgr->handle = NULL;

return get_file_handle_safe(filename, &mgr->handle, &mgr->mount_id);
}

// 通过句柄打开文件
int handle_manager_open(handle_manager_t *mgr, int flags) {
if (mgr->fd != -1) {
close(mgr->fd);
}

mgr->fd = safe_open_by_handle(mgr->handle, flags, mgr->filename);
return mgr->fd;
}

// 清理句柄管理器
void handle_manager_cleanup(handle_manager_t *mgr) {
if (mgr->fd != -1) {
close(mgr->fd);
mgr->fd = -1;
}

if (mgr->handle) {
free(mgr->handle);
mgr->handle = NULL;
}

if (mgr->filename) {
free(mgr->filename);
mgr->filename = NULL;
}
}

这些示例展示了 open_by_handle_at 函数的各种使用方法,从基础的句柄打开到完整的管理工具,帮助你全面掌握 Linux 系统中通过文件句柄访问文件的机制。

data-ad-format="auto" data-full-width-responsive="true">