Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libzfs/libzfs_changelist.c
109341 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 2010 Sun Microsystems, Inc. All rights reserved.
25
* Use is subject to license terms.
26
*
27
* Portions Copyright 2007 Ramprakash Jelari
28
* Copyright (c) 2014, 2020 by Delphix. All rights reserved.
29
* Copyright 2016 Igor Kozhukhov <[email protected]>
30
* Copyright (c) 2018 Datto Inc.
31
*/
32
33
#include <libintl.h>
34
#include <stddef.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include <zone.h>
39
#include <sys/avl.h>
40
41
#include <libzfs.h>
42
43
#include "libzfs_impl.h"
44
45
/*
46
* Structure to keep track of dataset state. Before changing the 'sharenfs' or
47
* 'mountpoint' property, we record whether the filesystem was previously
48
* mounted/shared. This prior state dictates whether we remount/reshare the
49
* dataset after the property has been changed.
50
*
51
* The interface consists of the following sequence of functions:
52
*
53
* changelist_gather()
54
* changelist_prefix()
55
* < change property >
56
* changelist_postfix()
57
* changelist_free()
58
*
59
* Other interfaces:
60
*
61
* changelist_remove() - remove a node from a gathered list
62
* changelist_rename() - renames all datasets appropriately when doing a rename
63
* changelist_unshare() - unshares all the nodes in a given changelist
64
* changelist_haszonedchild() - check if there is any child exported to
65
* a local zone
66
*/
67
typedef struct prop_changenode {
68
zfs_handle_t *cn_handle;
69
int cn_shared;
70
int cn_mounted;
71
int cn_zoned;
72
boolean_t cn_needpost; /* is postfix() needed? */
73
avl_node_t cn_treenode;
74
} prop_changenode_t;
75
76
struct prop_changelist {
77
zfs_prop_t cl_prop;
78
zfs_prop_t cl_realprop;
79
zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
80
avl_tree_t cl_tree;
81
boolean_t cl_waslegacy;
82
boolean_t cl_allchildren;
83
boolean_t cl_alldependents;
84
int cl_mflags; /* Mount flags */
85
int cl_gflags; /* Gather request flags */
86
boolean_t cl_haszonedchild;
87
};
88
89
/*
90
* If the property is 'mountpoint', go through and unmount filesystems as
91
* necessary. We don't do the same for 'sharenfs', because we can just re-share
92
* with different options without interrupting service. We do handle 'sharesmb'
93
* since there may be old resource names that need to be removed.
94
*/
95
int
96
changelist_prefix(prop_changelist_t *clp)
97
{
98
prop_changenode_t *cn;
99
int ret = 0;
100
const enum sa_protocol smb[] = {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
101
boolean_t commit_smb_shares = B_FALSE;
102
103
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
104
clp->cl_prop != ZFS_PROP_SHARESMB)
105
return (0);
106
107
/*
108
* If CL_GATHER_DONT_UNMOUNT is set, don't want to unmount/unshare and
109
* later (re)mount/(re)share the filesystem in postfix phase, so we
110
* return from here. If filesystem is mounted or unmounted, leave it
111
* as it is.
112
*/
113
if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
114
return (0);
115
116
for (cn = avl_first(&clp->cl_tree); cn != NULL;
117
cn = AVL_NEXT(&clp->cl_tree, cn)) {
118
119
/* if a previous loop failed, set the remaining to false */
120
if (ret == -1) {
121
cn->cn_needpost = B_FALSE;
122
continue;
123
}
124
125
/*
126
* If we are in the global zone, but this dataset is exported
127
* to a local zone, do nothing.
128
*/
129
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
130
continue;
131
132
if (!ZFS_IS_VOLUME(cn->cn_handle)) {
133
/*
134
* Do the property specific processing.
135
*/
136
switch (clp->cl_prop) {
137
case ZFS_PROP_MOUNTPOINT:
138
if (zfs_unmount(cn->cn_handle, NULL,
139
clp->cl_mflags) != 0) {
140
ret = -1;
141
cn->cn_needpost = B_FALSE;
142
}
143
break;
144
case ZFS_PROP_SHARESMB:
145
(void) zfs_unshare(cn->cn_handle, NULL,
146
smb);
147
commit_smb_shares = B_TRUE;
148
break;
149
150
default:
151
break;
152
}
153
}
154
}
155
156
if (commit_smb_shares)
157
zfs_commit_shares(smb);
158
159
if (ret == -1)
160
(void) changelist_postfix(clp);
161
162
return (ret);
163
}
164
165
/*
166
* If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
167
* reshare the filesystems as necessary. In changelist_gather() we recorded
168
* whether the filesystem was previously shared or mounted. The action we take
169
* depends on the previous state, and whether the value was previously 'legacy'.
170
* For non-legacy properties, we always remount/reshare the filesystem,
171
* if CL_GATHER_DONT_UNMOUNT is not set.
172
*/
173
int
174
changelist_postfix(prop_changelist_t *clp)
175
{
176
prop_changenode_t *cn;
177
char shareopts[ZFS_MAXPROPLEN];
178
boolean_t commit_smb_shares = B_FALSE;
179
boolean_t commit_nfs_shares = B_FALSE;
180
181
/*
182
* If CL_GATHER_DONT_UNMOUNT is set, it means we don't want to (un)mount
183
* or (re/un)share the filesystem, so we return from here. If filesystem
184
* is mounted or unmounted, leave it as it is.
185
*/
186
if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
187
return (0);
188
189
/*
190
* If we're changing the mountpoint, attempt to destroy the underlying
191
* mountpoint. All other datasets will have inherited from this dataset
192
* (in which case their mountpoints exist in the filesystem in the new
193
* location), or have explicit mountpoints set (in which case they won't
194
* be in the changelist).
195
*/
196
if ((cn = avl_last(&clp->cl_tree)) == NULL)
197
return (0);
198
199
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
200
!(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
201
remove_mountpoint(cn->cn_handle);
202
203
/*
204
* We walk the datasets in reverse, because we want to mount any parent
205
* datasets before mounting the children. We walk all datasets even if
206
* there are errors.
207
*/
208
for (cn = avl_last(&clp->cl_tree); cn != NULL;
209
cn = AVL_PREV(&clp->cl_tree, cn)) {
210
211
boolean_t sharenfs;
212
boolean_t sharesmb;
213
boolean_t mounted;
214
boolean_t needs_key;
215
216
/*
217
* If we are in the global zone, but this dataset is exported
218
* to a local zone, do nothing.
219
*/
220
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
221
continue;
222
223
/* Only do post-processing if it's required */
224
if (!cn->cn_needpost)
225
continue;
226
cn->cn_needpost = B_FALSE;
227
228
zfs_refresh_properties(cn->cn_handle);
229
230
if (ZFS_IS_VOLUME(cn->cn_handle))
231
continue;
232
233
/*
234
* Remount if previously mounted or mountpoint was legacy,
235
* or sharenfs or sharesmb property is set.
236
*/
237
sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
238
shareopts, sizeof (shareopts), NULL, NULL, 0,
239
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
240
241
sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
242
shareopts, sizeof (shareopts), NULL, NULL, 0,
243
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
244
245
needs_key = (zfs_prop_get_int(cn->cn_handle,
246
ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
247
248
mounted = zfs_is_mounted(cn->cn_handle, NULL);
249
250
if (!mounted && !needs_key && (cn->cn_mounted ||
251
(((clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
252
clp->cl_prop == clp->cl_realprop) ||
253
sharenfs || sharesmb || clp->cl_waslegacy) &&
254
(zfs_prop_get_int(cn->cn_handle,
255
ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
256
257
if (zfs_mount(cn->cn_handle, NULL, 0) == 0)
258
mounted = TRUE;
259
}
260
261
/*
262
* If the file system is mounted we always re-share even
263
* if the filesystem is currently shared, so that we can
264
* adopt any new options.
265
*/
266
const enum sa_protocol nfs[] =
267
{SA_PROTOCOL_NFS, SA_NO_PROTOCOL};
268
if (sharenfs && mounted) {
269
zfs_share(cn->cn_handle, nfs);
270
commit_nfs_shares = B_TRUE;
271
} else if (cn->cn_shared || clp->cl_waslegacy) {
272
zfs_unshare(cn->cn_handle, NULL, nfs);
273
commit_nfs_shares = B_TRUE;
274
}
275
const enum sa_protocol smb[] =
276
{SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
277
if (sharesmb && mounted) {
278
zfs_share(cn->cn_handle, smb);
279
commit_smb_shares = B_TRUE;
280
} else if (cn->cn_shared || clp->cl_waslegacy) {
281
zfs_unshare(cn->cn_handle, NULL, smb);
282
commit_smb_shares = B_TRUE;
283
}
284
}
285
286
enum sa_protocol proto[SA_PROTOCOL_COUNT + 1], *p = proto;
287
if (commit_nfs_shares)
288
*p++ = SA_PROTOCOL_NFS;
289
if (commit_smb_shares)
290
*p++ = SA_PROTOCOL_SMB;
291
*p++ = SA_NO_PROTOCOL;
292
zfs_commit_shares(proto);
293
294
return (0);
295
}
296
297
/*
298
* Is this "dataset" a child of "parent"?
299
*/
300
static boolean_t
301
isa_child_of(const char *dataset, const char *parent)
302
{
303
int len;
304
305
len = strlen(parent);
306
307
if (strncmp(dataset, parent, len) == 0 &&
308
(dataset[len] == '@' || dataset[len] == '/' ||
309
dataset[len] == '\0'))
310
return (B_TRUE);
311
else
312
return (B_FALSE);
313
314
}
315
316
/*
317
* If we rename a filesystem, child filesystem handles are no longer valid
318
* since we identify each dataset by its name in the ZFS namespace. As a
319
* result, we have to go through and fix up all the names appropriately. We
320
* could do this automatically if libzfs kept track of all open handles, but
321
* this is a lot less work.
322
*/
323
void
324
changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
325
{
326
prop_changenode_t *cn;
327
char newname[ZFS_MAX_DATASET_NAME_LEN];
328
329
for (cn = avl_first(&clp->cl_tree); cn != NULL;
330
cn = AVL_NEXT(&clp->cl_tree, cn)) {
331
/*
332
* Do not rename a clone that's not in the source hierarchy.
333
*/
334
if (!isa_child_of(cn->cn_handle->zfs_name, src))
335
continue;
336
337
/*
338
* Destroy the previous mountpoint if needed.
339
*/
340
remove_mountpoint(cn->cn_handle);
341
342
(void) strlcpy(newname, dst, sizeof (newname));
343
(void) strlcat(newname, cn->cn_handle->zfs_name + strlen(src),
344
sizeof (newname));
345
346
(void) strlcpy(cn->cn_handle->zfs_name, newname,
347
sizeof (cn->cn_handle->zfs_name));
348
}
349
}
350
351
/*
352
* Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
353
* unshare all the datasets in the list.
354
*/
355
int
356
changelist_unshare(prop_changelist_t *clp, const enum sa_protocol *proto)
357
{
358
prop_changenode_t *cn;
359
int ret = 0;
360
361
if (clp->cl_prop != ZFS_PROP_SHARENFS &&
362
clp->cl_prop != ZFS_PROP_SHARESMB)
363
return (0);
364
365
for (cn = avl_first(&clp->cl_tree); cn != NULL;
366
cn = AVL_NEXT(&clp->cl_tree, cn)) {
367
if (zfs_unshare(cn->cn_handle, NULL, proto) != 0)
368
ret = -1;
369
}
370
371
for (const enum sa_protocol *p = proto; *p != SA_NO_PROTOCOL; ++p)
372
sa_commit_shares(*p);
373
374
return (ret);
375
}
376
377
/*
378
* Check if there is any child exported to a local zone in a given changelist.
379
* This information has already been recorded while gathering the changelist
380
* via changelist_gather().
381
*/
382
int
383
changelist_haszonedchild(prop_changelist_t *clp)
384
{
385
return (clp->cl_haszonedchild);
386
}
387
388
/*
389
* Remove a node from a gathered list.
390
*/
391
void
392
changelist_remove(prop_changelist_t *clp, const char *name)
393
{
394
prop_changenode_t *cn;
395
396
for (cn = avl_first(&clp->cl_tree); cn != NULL;
397
cn = AVL_NEXT(&clp->cl_tree, cn)) {
398
if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
399
avl_remove(&clp->cl_tree, cn);
400
zfs_close(cn->cn_handle);
401
free(cn);
402
return;
403
}
404
}
405
}
406
407
/*
408
* Release any memory associated with a changelist.
409
*/
410
void
411
changelist_free(prop_changelist_t *clp)
412
{
413
prop_changenode_t *cn;
414
void *cookie = NULL;
415
416
while ((cn = avl_destroy_nodes(&clp->cl_tree, &cookie)) != NULL) {
417
zfs_close(cn->cn_handle);
418
free(cn);
419
}
420
421
avl_destroy(&clp->cl_tree);
422
free(clp);
423
}
424
425
/*
426
* Add one dataset to changelist
427
*/
428
static int
429
changelist_add_mounted(zfs_handle_t *zhp, void *data)
430
{
431
prop_changelist_t *clp = data;
432
prop_changenode_t *cn;
433
avl_index_t idx;
434
435
ASSERT3U(clp->cl_prop, ==, ZFS_PROP_MOUNTPOINT);
436
437
cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
438
cn->cn_handle = zhp;
439
cn->cn_mounted = zfs_is_mounted(zhp, NULL);
440
ASSERT3U(cn->cn_mounted, ==, B_TRUE);
441
cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
442
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
443
cn->cn_needpost = B_TRUE;
444
445
/* Indicate if any child is exported to a local zone. */
446
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
447
clp->cl_haszonedchild = B_TRUE;
448
449
if (avl_find(&clp->cl_tree, cn, &idx) == NULL) {
450
avl_insert(&clp->cl_tree, cn, idx);
451
} else {
452
free(cn);
453
zfs_close(zhp);
454
}
455
456
return (0);
457
}
458
459
static int
460
change_one(zfs_handle_t *zhp, void *data)
461
{
462
prop_changelist_t *clp = data;
463
char property[ZFS_MAXPROPLEN];
464
char where[64];
465
prop_changenode_t *cn = NULL;
466
zprop_source_t sourcetype = ZPROP_SRC_NONE;
467
zprop_source_t share_sourcetype = ZPROP_SRC_NONE;
468
int ret = 0;
469
470
/*
471
* We only want to unmount/unshare those filesystems that may inherit
472
* from the target filesystem. If we find any filesystem with a
473
* locally set mountpoint, we ignore any children since changing the
474
* property will not affect them. If this is a rename, we iterate
475
* over all children regardless, since we need them unmounted in
476
* order to do the rename. Also, if this is a volume and we're doing
477
* a rename, then always add it to the changelist.
478
*/
479
480
if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
481
zfs_prop_get(zhp, clp->cl_prop, property,
482
sizeof (property), &sourcetype, where, sizeof (where),
483
B_FALSE) != 0) {
484
goto out;
485
}
486
487
/*
488
* If we are "watching" sharenfs or sharesmb
489
* then check out the companion property which is tracked
490
* in cl_shareprop
491
*/
492
if (clp->cl_shareprop != ZPROP_INVAL &&
493
zfs_prop_get(zhp, clp->cl_shareprop, property,
494
sizeof (property), &share_sourcetype, where, sizeof (where),
495
B_FALSE) != 0) {
496
goto out;
497
}
498
499
if (clp->cl_alldependents || clp->cl_allchildren ||
500
sourcetype == ZPROP_SRC_DEFAULT ||
501
sourcetype == ZPROP_SRC_INHERITED ||
502
(clp->cl_shareprop != ZPROP_INVAL &&
503
(share_sourcetype == ZPROP_SRC_DEFAULT ||
504
share_sourcetype == ZPROP_SRC_INHERITED))) {
505
cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
506
cn->cn_handle = zhp;
507
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
508
zfs_is_mounted(zhp, NULL);
509
cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
510
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
511
cn->cn_needpost = B_TRUE;
512
513
/* Indicate if any child is exported to a local zone. */
514
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
515
clp->cl_haszonedchild = B_TRUE;
516
517
avl_index_t idx;
518
if (avl_find(&clp->cl_tree, cn, &idx) == NULL) {
519
avl_insert(&clp->cl_tree, cn, idx);
520
} else {
521
free(cn);
522
cn = NULL;
523
}
524
525
if (!clp->cl_alldependents) {
526
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT) {
527
ret = zfs_iter_filesystems_v2(zhp, 0,
528
change_one, data);
529
} else {
530
ret = zfs_iter_children_v2(zhp, 0, change_one,
531
data);
532
}
533
}
534
535
/*
536
* If we added the handle to the changelist, we will re-use it
537
* later so return without closing it.
538
*/
539
if (cn != NULL)
540
return (ret);
541
}
542
543
out:
544
zfs_close(zhp);
545
return (ret);
546
}
547
548
static int
549
compare_props(const void *a, const void *b, zfs_prop_t prop)
550
{
551
const prop_changenode_t *ca = a;
552
const prop_changenode_t *cb = b;
553
554
char propa[MAXPATHLEN];
555
char propb[MAXPATHLEN];
556
557
boolean_t haspropa, haspropb;
558
559
haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
560
NULL, NULL, 0, B_FALSE) == 0);
561
haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
562
NULL, NULL, 0, B_FALSE) == 0);
563
564
if (!haspropa && haspropb)
565
return (-1);
566
else if (haspropa && !haspropb)
567
return (1);
568
else if (!haspropa && !haspropb)
569
return (0);
570
else
571
return (TREE_ISIGN(strcmp(propb, propa)));
572
}
573
574
static int
575
compare_mountpoints(const void *a, const void *b)
576
{
577
/*
578
* When unsharing or unmounting filesystems, we need to do it in
579
* mountpoint order. This allows the user to have a mountpoint
580
* hierarchy that is different from the dataset hierarchy, and still
581
* allow it to be changed.
582
*/
583
return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
584
}
585
586
static int
587
compare_dataset_names(const void *a, const void *b)
588
{
589
return (compare_props(a, b, ZFS_PROP_NAME));
590
}
591
592
/*
593
* Given a ZFS handle and a property, construct a complete list of datasets
594
* that need to be modified as part of this process. For anything but the
595
* 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
596
* Otherwise, we iterate over all children and look for any datasets that
597
* inherit the property. For each such dataset, we add it to the list and
598
* mark whether it was shared beforehand.
599
*/
600
prop_changelist_t *
601
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
602
int mnt_flags)
603
{
604
prop_changelist_t *clp;
605
prop_changenode_t *cn;
606
zfs_handle_t *temp;
607
char property[ZFS_MAXPROPLEN];
608
boolean_t legacy = B_FALSE;
609
610
clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t));
611
612
/*
613
* For mountpoint-related tasks, we want to sort everything by
614
* mountpoint, so that we mount and unmount them in the appropriate
615
* order, regardless of their position in the hierarchy.
616
*/
617
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
618
prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
619
prop == ZFS_PROP_SHARESMB) {
620
621
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
622
property, sizeof (property),
623
NULL, NULL, 0, B_FALSE) == 0 &&
624
(strcmp(property, "legacy") == 0 ||
625
strcmp(property, "none") == 0)) {
626
legacy = B_TRUE;
627
}
628
}
629
630
avl_create(&clp->cl_tree,
631
legacy ? compare_dataset_names : compare_mountpoints,
632
sizeof (prop_changenode_t),
633
offsetof(prop_changenode_t, cn_treenode));
634
635
clp->cl_gflags = gather_flags;
636
clp->cl_mflags = mnt_flags;
637
638
/*
639
* If this is a rename or the 'zoned' property, we pretend we're
640
* changing the mountpoint and flag it so we can catch all children in
641
* change_one().
642
*
643
* Flag cl_alldependents to catch all children plus the dependents
644
* (clones) that are not in the hierarchy.
645
*/
646
if (prop == ZFS_PROP_NAME) {
647
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
648
clp->cl_alldependents = B_TRUE;
649
} else if (prop == ZFS_PROP_ZONED) {
650
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
651
clp->cl_allchildren = B_TRUE;
652
} else if (prop == ZFS_PROP_CANMOUNT) {
653
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
654
} else if (prop == ZFS_PROP_VOLSIZE) {
655
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
656
} else {
657
clp->cl_prop = prop;
658
}
659
clp->cl_realprop = prop;
660
661
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
662
clp->cl_prop != ZFS_PROP_SHARENFS &&
663
clp->cl_prop != ZFS_PROP_SHARESMB)
664
return (clp);
665
666
/*
667
* If watching SHARENFS or SHARESMB then
668
* also watch its companion property.
669
*/
670
if (clp->cl_prop == ZFS_PROP_SHARENFS)
671
clp->cl_shareprop = ZFS_PROP_SHARESMB;
672
else if (clp->cl_prop == ZFS_PROP_SHARESMB)
673
clp->cl_shareprop = ZFS_PROP_SHARENFS;
674
675
if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
676
(clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
677
/*
678
* Instead of iterating through all of the dataset children we
679
* gather mounted dataset children from MNTTAB
680
*/
681
if (zfs_iter_mounted(zhp, changelist_add_mounted, clp) != 0) {
682
changelist_free(clp);
683
return (NULL);
684
}
685
} else if (clp->cl_alldependents) {
686
if (zfs_iter_dependents_v2(zhp, 0, B_TRUE, change_one,
687
clp) != 0) {
688
changelist_free(clp);
689
return (NULL);
690
}
691
} else if (clp->cl_prop != ZFS_PROP_MOUNTPOINT) {
692
if (zfs_iter_filesystems_v2(zhp, 0, change_one, clp) != 0) {
693
changelist_free(clp);
694
return (NULL);
695
}
696
} else if (zfs_iter_children_v2(zhp, 0, change_one, clp) != 0) {
697
changelist_free(clp);
698
return (NULL);
699
}
700
701
/*
702
* We have to re-open ourselves because we auto-close all the handles
703
* and can't tell the difference.
704
*/
705
if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
706
ZFS_TYPE_DATASET)) == NULL) {
707
changelist_free(clp);
708
return (NULL);
709
}
710
711
/*
712
* Always add ourself to the list. We add ourselves to the end so that
713
* we're the last to be unmounted.
714
*/
715
cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t));
716
cn->cn_handle = temp;
717
cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
718
zfs_is_mounted(temp, NULL);
719
cn->cn_shared = zfs_is_shared(temp, NULL, NULL);
720
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
721
cn->cn_needpost = B_TRUE;
722
723
avl_index_t idx;
724
if (avl_find(&clp->cl_tree, cn, &idx) == NULL) {
725
avl_insert(&clp->cl_tree, cn, idx);
726
} else {
727
free(cn);
728
zfs_close(temp);
729
}
730
731
/*
732
* If the mountpoint property was previously 'legacy', or 'none',
733
* record it as the behavior of changelist_postfix() will be different.
734
*/
735
if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
736
/*
737
* do not automatically mount ex-legacy datasets if
738
* we specifically set canmount to noauto
739
*/
740
if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
741
ZFS_CANMOUNT_NOAUTO)
742
clp->cl_waslegacy = B_TRUE;
743
}
744
745
return (clp);
746
}
747
748