Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_iter.c
48378 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
25
* Copyright (c) 2013, 2019 by Delphix. All rights reserved.
26
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27
* Copyright (c) 2019 Datto Inc.
28
*/
29
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <unistd.h>
34
#include <stddef.h>
35
#include <libintl.h>
36
#include <libzfs.h>
37
#include <libzutil.h>
38
#include <sys/mntent.h>
39
40
#include "libzfs_impl.h"
41
42
static int
43
zfs_iter_clones(zfs_handle_t *zhp, int flags __maybe_unused, zfs_iter_f func,
44
void *data)
45
{
46
nvlist_t *nvl = zfs_get_clones_nvl(zhp);
47
nvpair_t *pair;
48
49
if (nvl == NULL)
50
return (0);
51
52
for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
53
pair = nvlist_next_nvpair(nvl, pair)) {
54
zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
55
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
56
if (clone != NULL) {
57
int err = func(clone, data);
58
if (err != 0)
59
return (err);
60
}
61
}
62
return (0);
63
}
64
65
static int
66
zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
67
{
68
int rc;
69
uint64_t orig_cookie;
70
71
orig_cookie = zc->zc_cookie;
72
top:
73
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
74
zc->zc_objset_stats.dds_creation_txg = 0;
75
rc = zfs_ioctl(zhp->zfs_hdl, arg, zc);
76
77
if (rc == -1) {
78
switch (errno) {
79
case ENOMEM:
80
/* expand nvlist memory and try again */
81
zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc);
82
zc->zc_cookie = orig_cookie;
83
goto top;
84
/*
85
* An errno value of ESRCH indicates normal completion.
86
* If ENOENT is returned, then the underlying dataset
87
* has been removed since we obtained the handle.
88
*/
89
case ESRCH:
90
case ENOENT:
91
rc = 1;
92
break;
93
default:
94
rc = zfs_standard_error(zhp->zfs_hdl, errno,
95
dgettext(TEXT_DOMAIN,
96
"cannot iterate filesystems"));
97
break;
98
}
99
}
100
return (rc);
101
}
102
103
/*
104
* Iterate over all child filesystems
105
*/
106
int
107
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
108
{
109
return (zfs_iter_filesystems_v2(zhp, 0, func, data));
110
}
111
112
int
113
zfs_iter_filesystems_v2(zfs_handle_t *zhp, int flags, zfs_iter_f func,
114
void *data)
115
{
116
zfs_cmd_t zc = {"\0"};
117
zfs_handle_t *nzhp;
118
int ret;
119
120
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
121
return (0);
122
123
zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);
124
125
if ((flags & ZFS_ITER_SIMPLE) == ZFS_ITER_SIMPLE)
126
zc.zc_simple = B_TRUE;
127
128
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
129
&zc)) == 0) {
130
if (zc.zc_simple)
131
nzhp = make_dataset_simple_handle_zc(zhp, &zc);
132
else
133
nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
134
/*
135
* Silently ignore errors, as the only plausible explanation is
136
* that the pool has since been removed.
137
*/
138
if (nzhp == NULL)
139
continue;
140
141
if ((ret = func(nzhp, data)) != 0) {
142
zcmd_free_nvlists(&zc);
143
return (ret);
144
}
145
}
146
zcmd_free_nvlists(&zc);
147
return ((ret < 0) ? ret : 0);
148
}
149
150
/*
151
* Iterate over all snapshots
152
*/
153
int
154
zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
155
void *data, uint64_t min_txg, uint64_t max_txg)
156
{
157
return (zfs_iter_snapshots_v2(zhp, simple ? ZFS_ITER_SIMPLE : 0, func,
158
data, min_txg, max_txg));
159
}
160
161
int
162
zfs_iter_snapshots_v2(zfs_handle_t *zhp, int flags, zfs_iter_f func,
163
void *data, uint64_t min_txg, uint64_t max_txg)
164
{
165
zfs_cmd_t zc = {"\0"};
166
zfs_handle_t *nzhp;
167
int ret;
168
nvlist_t *range_nvl = NULL;
169
170
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
171
zhp->zfs_type == ZFS_TYPE_BOOKMARK)
172
return (0);
173
174
zc.zc_simple = (flags & ZFS_ITER_SIMPLE) != 0;
175
176
zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);
177
178
if (min_txg != 0) {
179
range_nvl = fnvlist_alloc();
180
fnvlist_add_uint64(range_nvl, SNAP_ITER_MIN_TXG, min_txg);
181
}
182
if (max_txg != 0) {
183
if (range_nvl == NULL)
184
range_nvl = fnvlist_alloc();
185
fnvlist_add_uint64(range_nvl, SNAP_ITER_MAX_TXG, max_txg);
186
}
187
188
if (range_nvl != NULL)
189
zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, range_nvl);
190
191
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
192
&zc)) == 0) {
193
194
if (zc.zc_simple)
195
nzhp = make_dataset_simple_handle_zc(zhp, &zc);
196
else
197
nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
198
if (nzhp == NULL)
199
continue;
200
201
if ((ret = func(nzhp, data)) != 0) {
202
zcmd_free_nvlists(&zc);
203
fnvlist_free(range_nvl);
204
return (ret);
205
}
206
}
207
zcmd_free_nvlists(&zc);
208
fnvlist_free(range_nvl);
209
return ((ret < 0) ? ret : 0);
210
}
211
212
/*
213
* Iterate over all bookmarks
214
*/
215
int
216
zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
217
{
218
return (zfs_iter_bookmarks_v2(zhp, 0, func, data));
219
}
220
221
int
222
zfs_iter_bookmarks_v2(zfs_handle_t *zhp, int flags __maybe_unused,
223
zfs_iter_f func, void *data)
224
{
225
zfs_handle_t *nzhp;
226
nvlist_t *props = NULL;
227
nvlist_t *bmarks = NULL;
228
int err;
229
nvpair_t *pair;
230
231
if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
232
return (0);
233
234
/* Setup the requested properties nvlist. */
235
props = fnvlist_alloc();
236
for (zfs_prop_t p = 0; p < ZFS_NUM_PROPS; p++) {
237
if (zfs_prop_valid_for_type(p, ZFS_TYPE_BOOKMARK, B_FALSE)) {
238
fnvlist_add_boolean(props, zfs_prop_to_name(p));
239
}
240
}
241
fnvlist_add_boolean(props, "redact_complete");
242
243
if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
244
goto out;
245
246
for (pair = nvlist_next_nvpair(bmarks, NULL);
247
pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
248
char name[ZFS_MAX_DATASET_NAME_LEN];
249
const char *bmark_name;
250
nvlist_t *bmark_props;
251
252
bmark_name = nvpair_name(pair);
253
bmark_props = fnvpair_value_nvlist(pair);
254
255
if (snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
256
bmark_name) >= sizeof (name)) {
257
err = EINVAL;
258
goto out;
259
}
260
261
nzhp = make_bookmark_handle(zhp, name, bmark_props);
262
if (nzhp == NULL)
263
continue;
264
265
if ((err = func(nzhp, data)) != 0)
266
goto out;
267
}
268
269
out:
270
fnvlist_free(props);
271
fnvlist_free(bmarks);
272
273
return (err);
274
}
275
276
/*
277
* Routines for dealing with the sorted snapshot functionality
278
*/
279
typedef struct zfs_node {
280
zfs_handle_t *zn_handle;
281
avl_node_t zn_avlnode;
282
} zfs_node_t;
283
284
static int
285
zfs_sort_snaps(zfs_handle_t *zhp, void *data)
286
{
287
avl_tree_t *avl = data;
288
zfs_node_t *node;
289
zfs_node_t search;
290
291
search.zn_handle = zhp;
292
node = avl_find(avl, &search, NULL);
293
if (node) {
294
/*
295
* If this snapshot was renamed while we were creating the
296
* AVL tree, it's possible that we already inserted it under
297
* its old name. Remove the old handle before adding the new
298
* one.
299
*/
300
zfs_close(node->zn_handle);
301
avl_remove(avl, node);
302
free(node);
303
}
304
305
node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
306
node->zn_handle = zhp;
307
avl_add(avl, node);
308
309
return (0);
310
}
311
312
static int
313
zfs_snapshot_compare(const void *larg, const void *rarg)
314
{
315
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
316
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
317
uint64_t lcreate, rcreate;
318
319
/*
320
* Sort them according to creation time. We use the hidden
321
* CREATETXG property to get an absolute ordering of snapshots.
322
*/
323
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
324
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
325
326
return (TREE_CMP(lcreate, rcreate));
327
}
328
329
int
330
zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback,
331
void *data, uint64_t min_txg, uint64_t max_txg)
332
{
333
return (zfs_iter_snapshots_sorted_v2(zhp, 0, callback, data,
334
min_txg, max_txg));
335
}
336
337
int
338
zfs_iter_snapshots_sorted_v2(zfs_handle_t *zhp, int flags, zfs_iter_f callback,
339
void *data, uint64_t min_txg, uint64_t max_txg)
340
{
341
int ret = 0;
342
zfs_node_t *node;
343
avl_tree_t avl;
344
void *cookie = NULL;
345
346
avl_create(&avl, zfs_snapshot_compare,
347
sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
348
349
ret = zfs_iter_snapshots_v2(zhp, flags, zfs_sort_snaps, &avl, min_txg,
350
max_txg);
351
352
for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
353
ret |= callback(node->zn_handle, data);
354
355
while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
356
free(node);
357
358
avl_destroy(&avl);
359
360
return (ret);
361
}
362
363
typedef struct {
364
char *ssa_first;
365
char *ssa_last;
366
boolean_t ssa_seenfirst;
367
boolean_t ssa_seenlast;
368
zfs_iter_f ssa_func;
369
void *ssa_arg;
370
} snapspec_arg_t;
371
372
static int
373
snapspec_cb(zfs_handle_t *zhp, void *arg)
374
{
375
snapspec_arg_t *ssa = arg;
376
const char *shortsnapname;
377
int err = 0;
378
379
if (ssa->ssa_seenlast)
380
return (0);
381
382
shortsnapname = strchr(zfs_get_name(zhp), '@') + 1;
383
if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
384
ssa->ssa_seenfirst = B_TRUE;
385
if (strcmp(shortsnapname, ssa->ssa_last) == 0)
386
ssa->ssa_seenlast = B_TRUE;
387
388
if (ssa->ssa_seenfirst) {
389
err = ssa->ssa_func(zhp, ssa->ssa_arg);
390
} else {
391
zfs_close(zhp);
392
}
393
394
return (err);
395
}
396
397
/*
398
* spec is a string like "A,B%C,D"
399
*
400
* <snaps>, where <snaps> can be:
401
* <snap> (single snapshot)
402
* <snap>%<snap> (range of snapshots, inclusive)
403
* %<snap> (range of snapshots, starting with earliest)
404
* <snap>% (range of snapshots, ending with last)
405
* % (all snapshots)
406
* <snaps>[,...] (comma separated list of the above)
407
*
408
* If a snapshot can not be opened, continue trying to open the others, but
409
* return ENOENT at the end.
410
*/
411
int
412
zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
413
zfs_iter_f func, void *arg)
414
{
415
return (zfs_iter_snapspec_v2(fs_zhp, 0, spec_orig, func, arg));
416
}
417
418
int
419
zfs_iter_snapspec_v2(zfs_handle_t *fs_zhp, int flags, const char *spec_orig,
420
zfs_iter_f func, void *arg)
421
{
422
char *buf, *comma_separated, *cp;
423
int err = 0;
424
int ret = 0;
425
426
buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
427
cp = buf;
428
429
while ((comma_separated = strsep(&cp, ",")) != NULL) {
430
char *pct = strchr(comma_separated, '%');
431
if (pct != NULL) {
432
snapspec_arg_t ssa = { 0 };
433
ssa.ssa_func = func;
434
ssa.ssa_arg = arg;
435
436
if (pct == comma_separated)
437
ssa.ssa_seenfirst = B_TRUE;
438
else
439
ssa.ssa_first = comma_separated;
440
*pct = '\0';
441
ssa.ssa_last = pct + 1;
442
443
/*
444
* If there is a lastname specified, make sure it
445
* exists.
446
*/
447
if (ssa.ssa_last[0] != '\0') {
448
char snapname[ZFS_MAX_DATASET_NAME_LEN];
449
(void) snprintf(snapname, sizeof (snapname),
450
"%s@%s", zfs_get_name(fs_zhp),
451
ssa.ssa_last);
452
if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
453
snapname, ZFS_TYPE_SNAPSHOT)) {
454
ret = ENOENT;
455
continue;
456
}
457
}
458
459
err = zfs_iter_snapshots_sorted_v2(fs_zhp, flags,
460
snapspec_cb, &ssa, 0, 0);
461
if (ret == 0)
462
ret = err;
463
if (ret == 0 && (!ssa.ssa_seenfirst ||
464
(ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
465
ret = ENOENT;
466
}
467
} else {
468
char snapname[ZFS_MAX_DATASET_NAME_LEN];
469
zfs_handle_t *snap_zhp;
470
(void) snprintf(snapname, sizeof (snapname), "%s@%s",
471
zfs_get_name(fs_zhp), comma_separated);
472
snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
473
snapname);
474
if (snap_zhp == NULL) {
475
ret = ENOENT;
476
continue;
477
}
478
err = func(snap_zhp, arg);
479
if (ret == 0)
480
ret = err;
481
}
482
}
483
484
free(buf);
485
return (ret);
486
}
487
488
/*
489
* Iterate over all children, snapshots and filesystems
490
* Process snapshots before filesystems because they are nearer the input
491
* handle: this is extremely important when used with zfs_iter_f functions
492
* looking for data, following the logic that we would like to find it as soon
493
* and as close as possible.
494
*/
495
int
496
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
497
{
498
return (zfs_iter_children_v2(zhp, 0, func, data));
499
}
500
501
int
502
zfs_iter_children_v2(zfs_handle_t *zhp, int flags, zfs_iter_f func, void *data)
503
{
504
int ret;
505
506
if ((ret = zfs_iter_snapshots_v2(zhp, flags, func, data, 0, 0)) != 0)
507
return (ret);
508
509
return (zfs_iter_filesystems_v2(zhp, flags, func, data));
510
}
511
512
513
typedef struct iter_stack_frame {
514
struct iter_stack_frame *next;
515
zfs_handle_t *zhp;
516
} iter_stack_frame_t;
517
518
typedef struct iter_dependents_arg {
519
boolean_t first;
520
int flags;
521
boolean_t allowrecursion;
522
iter_stack_frame_t *stack;
523
zfs_iter_f func;
524
void *data;
525
} iter_dependents_arg_t;
526
527
static int
528
iter_dependents_cb(zfs_handle_t *zhp, void *arg)
529
{
530
iter_dependents_arg_t *ida = arg;
531
int err = 0;
532
boolean_t first = ida->first;
533
ida->first = B_FALSE;
534
535
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
536
err = zfs_iter_clones(zhp, ida->flags, iter_dependents_cb, ida);
537
} else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
538
iter_stack_frame_t isf;
539
iter_stack_frame_t *f;
540
541
/*
542
* check if there is a cycle by seeing if this fs is already
543
* on the stack.
544
*/
545
for (f = ida->stack; f != NULL; f = f->next) {
546
if (f->zhp->zfs_dmustats.dds_guid ==
547
zhp->zfs_dmustats.dds_guid) {
548
if (ida->allowrecursion) {
549
zfs_close(zhp);
550
return (0);
551
} else {
552
zfs_error_aux(zhp->zfs_hdl,
553
dgettext(TEXT_DOMAIN,
554
"recursive dependency at '%s'"),
555
zfs_get_name(zhp));
556
err = zfs_error(zhp->zfs_hdl,
557
EZFS_RECURSIVE,
558
dgettext(TEXT_DOMAIN,
559
"cannot determine dependent "
560
"datasets"));
561
zfs_close(zhp);
562
return (err);
563
}
564
}
565
}
566
567
isf.zhp = zhp;
568
isf.next = ida->stack;
569
ida->stack = &isf;
570
err = zfs_iter_filesystems_v2(zhp, ida->flags,
571
iter_dependents_cb, ida);
572
if (err == 0)
573
err = zfs_iter_snapshots_sorted_v2(zhp, ida->flags,
574
iter_dependents_cb, ida, 0, 0);
575
ida->stack = isf.next;
576
}
577
578
if (!first && err == 0)
579
err = ida->func(zhp, ida->data);
580
else
581
zfs_close(zhp);
582
583
return (err);
584
}
585
586
int
587
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
588
zfs_iter_f func, void *data)
589
{
590
return (zfs_iter_dependents_v2(zhp, 0, allowrecursion, func, data));
591
}
592
593
int
594
zfs_iter_dependents_v2(zfs_handle_t *zhp, int flags, boolean_t allowrecursion,
595
zfs_iter_f func, void *data)
596
{
597
iter_dependents_arg_t ida;
598
ida.flags = flags;
599
ida.allowrecursion = allowrecursion;
600
ida.stack = NULL;
601
ida.func = func;
602
ida.data = data;
603
ida.first = B_TRUE;
604
return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
605
}
606
607
/*
608
* Iterate over mounted children of the specified dataset
609
*/
610
int
611
zfs_iter_mounted(zfs_handle_t *zhp, zfs_iter_f func, void *data)
612
{
613
char mnt_prop[ZFS_MAXPROPLEN];
614
struct mnttab entry;
615
zfs_handle_t *mtab_zhp;
616
size_t namelen = strlen(zhp->zfs_name);
617
FILE *mnttab;
618
int err = 0;
619
620
if ((mnttab = fopen(MNTTAB, "re")) == NULL)
621
return (ENOENT);
622
623
while (err == 0 && getmntent(mnttab, &entry) == 0) {
624
/* Ignore non-ZFS entries */
625
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
626
continue;
627
628
/* Ignore datasets not within the provided dataset */
629
if (strncmp(entry.mnt_special, zhp->zfs_name, namelen) != 0 ||
630
entry.mnt_special[namelen] != '/')
631
continue;
632
633
/* Skip snapshot of any child dataset */
634
if (strchr(entry.mnt_special, '@') != NULL)
635
continue;
636
637
if ((mtab_zhp = zfs_open(zhp->zfs_hdl, entry.mnt_special,
638
ZFS_TYPE_FILESYSTEM)) == NULL)
639
continue;
640
641
/* Ignore legacy mounts as they are user managed */
642
verify(zfs_prop_get(mtab_zhp, ZFS_PROP_MOUNTPOINT, mnt_prop,
643
sizeof (mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
644
if (strcmp(mnt_prop, "legacy") == 0) {
645
zfs_close(mtab_zhp);
646
continue;
647
}
648
649
err = func(mtab_zhp, data);
650
}
651
652
fclose(mnttab);
653
654
return (err);
655
}
656
657