1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * This file and its contents are supplied under the terms of the
4 * Common Development and Distribution License ("CDDL"), version 1.0.
5 * You may only use this file in accordance with the terms of version
6 * 1.0 of the CDDL.
7 *
8 * A full copy of the text of the CDDL should have accompanied this
9 * source. A copy of the CDDL is also available via the Internet at
10 * https://opensource.org/license/CDDL-1.0.
11 */
12
13 /*
14 * Copyright 2026, tiehexue <tiehexue@hotmail.com>. All rights reserved.
15 */
16
17 /*
18 * Verify that a filehandle-based reopen of an open-unlinked file
19 * succeeds. On Linux this exercises zfs_vget() via fh_to_dentry;
20 * on FreeBSD it exercises zfs_fhtovp() via VFS_FHTOVP.
21 *
22 * Exit 0 on success, non-zero on failure.
23 *
24 * Most code copied from https://reviews.freebsd.org/D57982
25 * and https://github.com/openzfs/zfs/issues/18699
26 *
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36
37 #ifdef __FreeBSD__
38 #include <sys/param.h>
39 #include <sys/mount.h>
40 #endif
41
42 int
main(int argc,char * argv[])43 main(int argc, char *argv[])
44 {
45 const char *filename;
46 int file_fd = -1, handle_fd = -1;
47 int ret = 2;
48
49 if (argc != 2) {
50 (void) fprintf(stderr, "usage: %s <filename>\n", argv[0]);
51 return (2);
52 }
53 filename = argv[1];
54
55 file_fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
56 if (file_fd < 0) {
57 perror("open");
58 goto out;
59 }
60
61 #ifdef __linux__
62 struct file_handle *fhp;
63 int mount_fd, mnt_id;
64
65 mount_fd = open(".", O_RDONLY | O_DIRECTORY);
66 if (mount_fd < 0) {
67 perror("open mount_fd");
68 goto out;
69 }
70
71 fhp = malloc(sizeof (struct file_handle) + 128);
72 if (fhp == NULL) {
73 perror("malloc");
74 (void) close(mount_fd);
75 goto out;
76 }
77 fhp->handle_bytes = 128;
78
79 /* Obtain filehandle while the file still has a name */
80 if (name_to_handle_at(AT_FDCWD, filename, fhp,
81 &mnt_id, 0) < 0) {
82 perror("name_to_handle_at");
83 free(fhp);
84 (void) close(mount_fd);
85 goto out;
86 }
87
88 /* Unlink — file_fd keeps the inode alive */
89 if (unlink(filename) < 0) {
90 perror("unlink");
91 free(fhp);
92 (void) close(mount_fd);
93 goto out;
94 }
95
96 /* Reopen via filehandle — exercises fh_to_dentry/zfs_vget */
97 handle_fd = open_by_handle_at(mount_fd, fhp, O_RDONLY);
98 free(fhp);
99 (void) close(mount_fd);
100 #elif defined(__FreeBSD__)
101 fhandle_t fh;
102
103 /* Obtain ZFS filehandle while the file still has a name */
104 if (getfh(filename, &fh) < 0) {
105 perror("getfh");
106 goto out;
107 }
108
109 /* Unlink — file_fd keeps the vnode alive */
110 if (unlink(filename) < 0) {
111 perror("unlink");
112 goto out;
113 }
114
115 /* Reopen via filehandle — exercises VFS_FHTOVP/zfs_fhtovp */
116 handle_fd = fhopen(&fh, O_RDONLY);
117 #endif
118
119 if (handle_fd < 0) {
120 (void) fprintf(stderr, "FAIL: filehandle reopen: %s\n",
121 strerror(errno));
122 ret = 1;
123 } else {
124 (void) printf("filehandle reopen succeeded, fd=%d\n",
125 handle_fd);
126 ret = 0;
127 }
128
129 out:
130 if (handle_fd >= 0)
131 (void) close(handle_fd);
132 if (file_fd >= 0)
133 (void) close(file_fd);
134 return (ret);
135 }
136