xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/libzfs_mnttab_cache_check.c (revision efa8679e7f69c9cc225613827d9f75644cca5b3b)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * This file and its contents are supplied under the terms of the
6  * Common Development and Distribution License ("CDDL"), version 1.0.
7  * You may only use this file in accordance with the terms of version
8  * 1.0 of the CDDL.
9  *
10  * A full copy of the text of the CDDL should have accompanied this
11  * source.  A copy of the CDDL is also available via the Internet at
12  * http://www.illumos.org/license/CDDL.
13  *
14  * CDDL HEADER END
15  */
16 
17 /*
18  * Copyright (c) 2026 by Delphix. All rights reserved.
19  */
20 
21 /*
22  * libzfs_mnttab_cache_check.c
23  *
24  * Tests that libzfs_mnttab_cache(hdl, B_FALSE) does indeed disable the
25  * per-handle mnttab cache. It does this by adding a fake entry to it, then
26  * trying to read the status of a known-mounted dataset from it.
27  *
28  * As currently implemented, when enabled, libzfs_mnttab_find() assumes the
29  * cache is correct and up to date if it has any entries in it at all. So by
30  * putting something in it before searching, the initial load from /etc/mtab
31  * never happens, and the real mounted datasets are never seen.
32  *
33  * When disabled, the entire cache is discarded and reloaded on every lookup,
34  * so the fake entry will disappear and the real state will be found correctly.
35  * to date if it has any entries in it at all.
36  *
37  * Run (as a user that can read /etc/mtab):
38  *   ./libzfs_mnttab_cache_check <name-of-any-currently-mounted-zfs-dataset>
39  */
40 
41 #include <errno.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/mnttab.h>
46 #include <libzfs.h>
47 
48 int
main(int argc,char * argv[])49 main(int argc, char *argv[])
50 {
51 	if (argc != 2) {
52 		fprintf(stderr,
53 		    "usage: %s <currently-mounted-zfs-dataset>\n", argv[0]);
54 		return (2);
55 	}
56 	const char *real_ds = argv[1];
57 
58 	libzfs_handle_t *hdl = libzfs_init();
59 	if (hdl == NULL) {
60 		fprintf(stderr, "libzfs_init failed\n");
61 		return (1);
62 	}
63 
64 	/* Ask libzfs to disable the per-handle mnttab cache. */
65 	libzfs_mnttab_cache(hdl, B_FALSE);
66 
67 	/*
68 	 * Stand-in for what zfs_mount() does internally on every successful
69 	 * mount: zfs_mount_at() calls libzfs_mnttab_add(hdl, ...) after
70 	 * do_mount(). In a real consumer, this happens implicitly; we call it
71 	 * directly here so the reproducer doesn't need root or a mountable
72 	 * dataset.
73 	 */
74 	libzfs_mnttab_add(hdl, "fake/dataset", "/fake/mountpoint", "rw");
75 
76 	/*
77 	 * Now query ZFS_PROP_MOUNTED on a real, currently-mounted dataset.
78 	 * This is the standard libzfs API a consumer uses to check mount
79 	 * state. Internally it calls libzfs_mnttab_find().
80 	 */
81 	zfs_handle_t *zhp = zfs_open(hdl, real_ds, ZFS_TYPE_FILESYSTEM);
82 	if (zhp == NULL) {
83 		fprintf(stderr, "zfs_open(%s) failed\n", real_ds);
84 		libzfs_fini(hdl);
85 		return (1);
86 	}
87 
88 	uint64_t mounted = zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED);
89 	zfs_close(zhp);
90 
91 	int rc;
92 	if (mounted) {
93 		printf("OK: ZFS_PROP_MOUNTED reports %s as mounted\n", real_ds);
94 		rc = 0;
95 	} else {
96 		printf("BUG: ZFS_PROP_MOUNTED reports %s as NOT mounted\n",
97 		    real_ds);
98 		printf("     but %s IS mounted (see /etc/mtab and "
99 		    "`zfs get mounted`).\n", real_ds);
100 		printf("     libzfs_mnttab_cache(hdl, B_FALSE) did not "
101 		    "actually disable the cache.\n");
102 		rc = 1;
103 	}
104 
105 	libzfs_fini(hdl);
106 	return (rc);
107 }
108