xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/file/file_unlink_fh.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
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