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