]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
MFC r337063: MFV r316926:
[FreeBSD/FreeBSD.git] / cddl / contrib / opensolaris / lib / libzfs / common / libzfs_changelist.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Portions Copyright 2007 Ramprakash Jelari
27  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
28  * All rights reserved.
29  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
30  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
31  */
32
33 #include <libintl.h>
34 #include <libuutil.h>
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <zone.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         uu_list_node_t          cn_listnode;
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         uu_list_pool_t          *cl_pool;
81         uu_list_t               *cl_list;
82         boolean_t               cl_waslegacy;
83         boolean_t               cl_allchildren;
84         boolean_t               cl_alldependents;
85         int                     cl_mflags;      /* Mount flags */
86         int                     cl_gflags;      /* Gather request flags */
87         boolean_t               cl_haszonedchild;
88         boolean_t               cl_sorted;
89 };
90
91 /*
92  * If the property is 'mountpoint', go through and unmount filesystems as
93  * necessary.  We don't do the same for 'sharenfs', because we can just re-share
94  * with different options without interrupting service. We do handle 'sharesmb'
95  * since there may be old resource names that need to be removed.
96  */
97 int
98 changelist_prefix(prop_changelist_t *clp)
99 {
100         prop_changenode_t *cn;
101         int ret = 0;
102
103         if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
104             clp->cl_prop != ZFS_PROP_SHARESMB)
105                 return (0);
106
107         for (cn = uu_list_first(clp->cl_list); cn != NULL;
108             cn = uu_list_next(clp->cl_list, cn)) {
109
110                 /* if a previous loop failed, set the remaining to false */
111                 if (ret == -1) {
112                         cn->cn_needpost = B_FALSE;
113                         continue;
114                 }
115
116                 /*
117                  * If we are in the global zone, but this dataset is exported
118                  * to a local zone, do nothing.
119                  */
120                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
121                         continue;
122
123                 if (!ZFS_IS_VOLUME(cn->cn_handle)) {
124                         /*
125                          * Do the property specific processing.
126                          */
127                         switch (clp->cl_prop) {
128                         case ZFS_PROP_MOUNTPOINT:
129                                 if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
130                                         break;
131                                 if (zfs_unmount(cn->cn_handle, NULL,
132                                     clp->cl_mflags) != 0) {
133                                         ret = -1;
134                                         cn->cn_needpost = B_FALSE;
135                                 }
136                                 break;
137                         case ZFS_PROP_SHARESMB:
138                                 (void) zfs_unshare_smb(cn->cn_handle, NULL);
139                                 break;
140
141                         default:
142                                 break;
143                         }
144                 }
145         }
146
147         if (ret == -1)
148                 (void) changelist_postfix(clp);
149
150         return (ret);
151 }
152
153 /*
154  * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
155  * reshare the filesystems as necessary.  In changelist_gather() we recorded
156  * whether the filesystem was previously shared or mounted.  The action we take
157  * depends on the previous state, and whether the value was previously 'legacy'.
158  * For non-legacy properties, we only remount/reshare the filesystem if it was
159  * previously mounted/shared.  Otherwise, we always remount/reshare the
160  * filesystem.
161  */
162 int
163 changelist_postfix(prop_changelist_t *clp)
164 {
165         prop_changenode_t *cn;
166         char shareopts[ZFS_MAXPROPLEN];
167         int errors = 0;
168         libzfs_handle_t *hdl;
169 #ifdef illumos
170         size_t num_datasets = 0, i;
171         zfs_handle_t **zhandle_arr;
172         sa_init_selective_arg_t sharearg;
173 #endif
174
175         /*
176          * If we're changing the mountpoint, attempt to destroy the underlying
177          * mountpoint.  All other datasets will have inherited from this dataset
178          * (in which case their mountpoints exist in the filesystem in the new
179          * location), or have explicit mountpoints set (in which case they won't
180          * be in the changelist).
181          */
182         if ((cn = uu_list_last(clp->cl_list)) == NULL)
183                 return (0);
184
185         if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
186             !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
187                 remove_mountpoint(cn->cn_handle);
188         }
189
190         /*
191          * It is possible that the changelist_prefix() used libshare
192          * to unshare some entries. Since libshare caches data, an
193          * attempt to reshare during postfix can fail unless libshare
194          * is uninitialized here so that it will reinitialize later.
195          */
196         if (cn->cn_handle != NULL) {
197                 hdl = cn->cn_handle->zfs_hdl;
198                 assert(hdl != NULL);
199                 zfs_uninit_libshare(hdl);
200
201 #ifdef illumos
202                 /*
203                  * For efficiencies sake, we initialize libshare for only a few
204                  * shares (the ones affected here). Future initializations in
205                  * this process should just use the cached initialization.
206                  */
207                 for (cn = uu_list_last(clp->cl_list); cn != NULL;
208                     cn = uu_list_prev(clp->cl_list, cn)) {
209                         num_datasets++;
210                 }
211
212                 zhandle_arr = zfs_alloc(hdl,
213                     num_datasets * sizeof (zfs_handle_t *));
214                 for (i = 0, cn = uu_list_last(clp->cl_list); cn != NULL;
215                     cn = uu_list_prev(clp->cl_list, cn)) {
216                         zhandle_arr[i++] = cn->cn_handle;
217                         zfs_refresh_properties(cn->cn_handle);
218                 }
219                 assert(i == num_datasets);
220                 sharearg.zhandle_arr = zhandle_arr;
221                 sharearg.zhandle_len = num_datasets;
222                 errors = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
223                     &sharearg);
224                 free(zhandle_arr);
225 #endif
226         }
227         /*
228          * We walk the datasets in reverse, because we want to mount any parent
229          * datasets before mounting the children.  We walk all datasets even if
230          * there are errors.
231          */
232         for (cn = uu_list_last(clp->cl_list); cn != NULL;
233             cn = uu_list_prev(clp->cl_list, cn)) {
234
235                 boolean_t sharenfs;
236                 boolean_t sharesmb;
237                 boolean_t mounted;
238
239                 /*
240                  * If we are in the global zone, but this dataset is exported
241                  * to a local zone, do nothing.
242                  */
243                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
244                         continue;
245
246                 /* Only do post-processing if it's required */
247                 if (!cn->cn_needpost)
248                         continue;
249                 cn->cn_needpost = B_FALSE;
250
251 #ifndef illumos
252                 zfs_refresh_properties(cn->cn_handle);
253 #endif
254
255                 if (ZFS_IS_VOLUME(cn->cn_handle))
256                         continue;
257
258                 /*
259                  * Remount if previously mounted or mountpoint was legacy,
260                  * or sharenfs or sharesmb  property is set.
261                  */
262                 sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
263                     shareopts, sizeof (shareopts), NULL, NULL, 0,
264                     B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
265
266                 sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
267                     shareopts, sizeof (shareopts), NULL, NULL, 0,
268                     B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
269
270                 mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
271                     zfs_is_mounted(cn->cn_handle, NULL);
272
273                 if (!mounted && (cn->cn_mounted ||
274                     ((sharenfs || sharesmb || clp->cl_waslegacy) &&
275                     (zfs_prop_get_int(cn->cn_handle,
276                     ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
277
278                         if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
279                                 errors++;
280                         else
281                                 mounted = TRUE;
282                 }
283
284                 /*
285                  * If the file system is mounted we always re-share even
286                  * if the filesystem is currently shared, so that we can
287                  * adopt any new options.
288                  */
289                 if (sharenfs && mounted)
290                         errors += zfs_share_nfs(cn->cn_handle);
291                 else if (cn->cn_shared || clp->cl_waslegacy)
292                         errors += zfs_unshare_nfs(cn->cn_handle, NULL);
293                 if (sharesmb && mounted)
294                         errors += zfs_share_smb(cn->cn_handle);
295                 else if (cn->cn_shared || clp->cl_waslegacy)
296                         errors += zfs_unshare_smb(cn->cn_handle, NULL);
297         }
298
299         return (errors ? -1 : 0);
300 }
301
302 /*
303  * Is this "dataset" a child of "parent"?
304  */
305 boolean_t
306 isa_child_of(const char *dataset, const char *parent)
307 {
308         int len;
309
310         len = strlen(parent);
311
312         if (strncmp(dataset, parent, len) == 0 &&
313             (dataset[len] == '@' || dataset[len] == '/' ||
314             dataset[len] == '\0'))
315                 return (B_TRUE);
316         else
317                 return (B_FALSE);
318
319 }
320
321 /*
322  * If we rename a filesystem, child filesystem handles are no longer valid
323  * since we identify each dataset by its name in the ZFS namespace.  As a
324  * result, we have to go through and fix up all the names appropriately.  We
325  * could do this automatically if libzfs kept track of all open handles, but
326  * this is a lot less work.
327  */
328 void
329 changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
330 {
331         prop_changenode_t *cn;
332         char newname[ZFS_MAX_DATASET_NAME_LEN];
333
334         for (cn = uu_list_first(clp->cl_list); cn != NULL;
335             cn = uu_list_next(clp->cl_list, cn)) {
336                 /*
337                  * Do not rename a clone that's not in the source hierarchy.
338                  */
339                 if (!isa_child_of(cn->cn_handle->zfs_name, src))
340                         continue;
341
342                 /*
343                  * Destroy the previous mountpoint if needed.
344                  */
345                 remove_mountpoint(cn->cn_handle);
346
347                 (void) strlcpy(newname, dst, sizeof (newname));
348                 (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
349
350                 (void) strlcpy(cn->cn_handle->zfs_name, newname,
351                     sizeof (cn->cn_handle->zfs_name));
352         }
353 }
354
355 /*
356  * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
357  * unshare all the datasets in the list.
358  */
359 int
360 changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
361 {
362         prop_changenode_t *cn;
363         int ret = 0;
364
365         if (clp->cl_prop != ZFS_PROP_SHARENFS &&
366             clp->cl_prop != ZFS_PROP_SHARESMB)
367                 return (0);
368
369         for (cn = uu_list_first(clp->cl_list); cn != NULL;
370             cn = uu_list_next(clp->cl_list, cn)) {
371                 if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
372                         ret = -1;
373         }
374
375         return (ret);
376 }
377
378 /*
379  * Check if there is any child exported to a local zone in a given changelist.
380  * This information has already been recorded while gathering the changelist
381  * via changelist_gather().
382  */
383 int
384 changelist_haszonedchild(prop_changelist_t *clp)
385 {
386         return (clp->cl_haszonedchild);
387 }
388
389 /*
390  * Remove a node from a gathered list.
391  */
392 void
393 changelist_remove(prop_changelist_t *clp, const char *name)
394 {
395         prop_changenode_t *cn;
396
397         for (cn = uu_list_first(clp->cl_list); cn != NULL;
398             cn = uu_list_next(clp->cl_list, cn)) {
399
400                 if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
401                         uu_list_remove(clp->cl_list, cn);
402                         zfs_close(cn->cn_handle);
403                         free(cn);
404                         return;
405                 }
406         }
407 }
408
409 /*
410  * Release any memory associated with a changelist.
411  */
412 void
413 changelist_free(prop_changelist_t *clp)
414 {
415         prop_changenode_t *cn;
416         void *cookie;
417
418         if (clp->cl_list) {
419                 cookie = NULL;
420                 while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
421                         zfs_close(cn->cn_handle);
422                         free(cn);
423                 }
424
425                 uu_list_destroy(clp->cl_list);
426         }
427         if (clp->cl_pool)
428                 uu_list_pool_destroy(clp->cl_pool);
429
430         free(clp);
431 }
432
433 static int
434 change_one(zfs_handle_t *zhp, void *data)
435 {
436         prop_changelist_t *clp = data;
437         char property[ZFS_MAXPROPLEN];
438         char where[64];
439         prop_changenode_t *cn;
440         zprop_source_t sourcetype;
441         zprop_source_t share_sourcetype;
442
443         /*
444          * We only want to unmount/unshare those filesystems that may inherit
445          * from the target filesystem.  If we find any filesystem with a
446          * locally set mountpoint, we ignore any children since changing the
447          * property will not affect them.  If this is a rename, we iterate
448          * over all children regardless, since we need them unmounted in
449          * order to do the rename.  Also, if this is a volume and we're doing
450          * a rename, then always add it to the changelist.
451          */
452
453         if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
454             zfs_prop_get(zhp, clp->cl_prop, property,
455             sizeof (property), &sourcetype, where, sizeof (where),
456             B_FALSE) != 0) {
457                 zfs_close(zhp);
458                 return (0);
459         }
460
461         /*
462          * If we are "watching" sharenfs or sharesmb
463          * then check out the companion property which is tracked
464          * in cl_shareprop
465          */
466         if (clp->cl_shareprop != ZPROP_INVAL &&
467             zfs_prop_get(zhp, clp->cl_shareprop, property,
468             sizeof (property), &share_sourcetype, where, sizeof (where),
469             B_FALSE) != 0) {
470                 zfs_close(zhp);
471                 return (0);
472         }
473
474         if (clp->cl_alldependents || clp->cl_allchildren ||
475             sourcetype == ZPROP_SRC_DEFAULT ||
476             sourcetype == ZPROP_SRC_INHERITED ||
477             (clp->cl_shareprop != ZPROP_INVAL &&
478             (share_sourcetype == ZPROP_SRC_DEFAULT ||
479             share_sourcetype == ZPROP_SRC_INHERITED))) {
480                 if ((cn = zfs_alloc(zfs_get_handle(zhp),
481                     sizeof (prop_changenode_t))) == NULL) {
482                         zfs_close(zhp);
483                         return (-1);
484                 }
485
486                 cn->cn_handle = zhp;
487                 cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
488                     zfs_is_mounted(zhp, NULL);
489                 cn->cn_shared = zfs_is_shared(zhp);
490                 cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
491                 cn->cn_needpost = B_TRUE;
492
493                 /* Indicate if any child is exported to a local zone. */
494                 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
495                         clp->cl_haszonedchild = B_TRUE;
496
497                 uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
498
499                 if (clp->cl_sorted) {
500                         uu_list_index_t idx;
501
502                         (void) uu_list_find(clp->cl_list, cn, NULL,
503                             &idx);
504                         uu_list_insert(clp->cl_list, cn, idx);
505                 } else {
506                         /*
507                          * Add this child to beginning of the list. Children
508                          * below this one in the hierarchy will get added above
509                          * this one in the list. This produces a list in
510                          * reverse dataset name order.
511                          * This is necessary when the original mountpoint
512                          * is legacy or none.
513                          */
514                         verify(uu_list_insert_before(clp->cl_list,
515                             uu_list_first(clp->cl_list), cn) == 0);
516                 }
517
518                 if (!clp->cl_alldependents)
519                         return (zfs_iter_children(zhp, change_one, data));
520         } else {
521                 zfs_close(zhp);
522         }
523
524         return (0);
525 }
526
527 /*ARGSUSED*/
528 static int
529 compare_mountpoints(const void *a, const void *b, void *unused)
530 {
531         const prop_changenode_t *ca = a;
532         const prop_changenode_t *cb = b;
533
534         char mounta[MAXPATHLEN];
535         char mountb[MAXPATHLEN];
536
537         boolean_t hasmounta, hasmountb;
538
539         /*
540          * When unsharing or unmounting filesystems, we need to do it in
541          * mountpoint order.  This allows the user to have a mountpoint
542          * hierarchy that is different from the dataset hierarchy, and still
543          * allow it to be changed.  However, if either dataset doesn't have a
544          * mountpoint (because it is a volume or a snapshot), we place it at the
545          * end of the list, because it doesn't affect our change at all.
546          */
547         hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
548             sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
549         hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
550             sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
551
552         if (!hasmounta && hasmountb)
553                 return (-1);
554         else if (hasmounta && !hasmountb)
555                 return (1);
556         else if (!hasmounta && !hasmountb)
557                 return (0);
558         else
559                 return (strcmp(mountb, mounta));
560 }
561
562 /*
563  * Given a ZFS handle and a property, construct a complete list of datasets
564  * that need to be modified as part of this process.  For anything but the
565  * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
566  * Otherwise, we iterate over all children and look for any datasets that
567  * inherit the property.  For each such dataset, we add it to the list and
568  * mark whether it was shared beforehand.
569  */
570 prop_changelist_t *
571 changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
572     int mnt_flags)
573 {
574         prop_changelist_t *clp;
575         prop_changenode_t *cn;
576         zfs_handle_t *temp;
577         char property[ZFS_MAXPROPLEN];
578         uu_compare_fn_t *compare = NULL;
579         boolean_t legacy = B_FALSE;
580
581         if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
582                 return (NULL);
583
584         /*
585          * For mountpoint-related tasks, we want to sort everything by
586          * mountpoint, so that we mount and unmount them in the appropriate
587          * order, regardless of their position in the hierarchy.
588          */
589         if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
590             prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
591             prop == ZFS_PROP_SHARESMB) {
592
593                 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
594                     property, sizeof (property),
595                     NULL, NULL, 0, B_FALSE) == 0 &&
596                     (strcmp(property, "legacy") == 0 ||
597                     strcmp(property, "none") == 0)) {
598
599                         legacy = B_TRUE;
600                 }
601                 if (!legacy) {
602                         compare = compare_mountpoints;
603                         clp->cl_sorted = B_TRUE;
604                 }
605         }
606
607         clp->cl_pool = uu_list_pool_create("changelist_pool",
608             sizeof (prop_changenode_t),
609             offsetof(prop_changenode_t, cn_listnode),
610             compare, 0);
611         if (clp->cl_pool == NULL) {
612                 assert(uu_error() == UU_ERROR_NO_MEMORY);
613                 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
614                 changelist_free(clp);
615                 return (NULL);
616         }
617
618         clp->cl_list = uu_list_create(clp->cl_pool, NULL,
619             clp->cl_sorted ? UU_LIST_SORTED : 0);
620         clp->cl_gflags = gather_flags;
621         clp->cl_mflags = mnt_flags;
622
623         if (clp->cl_list == NULL) {
624                 assert(uu_error() == UU_ERROR_NO_MEMORY);
625                 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
626                 changelist_free(clp);
627                 return (NULL);
628         }
629
630         /*
631          * If this is a rename or the 'zoned' property, we pretend we're
632          * changing the mountpoint and flag it so we can catch all children in
633          * change_one().
634          *
635          * Flag cl_alldependents to catch all children plus the dependents
636          * (clones) that are not in the hierarchy.
637          */
638         if (prop == ZFS_PROP_NAME) {
639                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
640                 clp->cl_alldependents = B_TRUE;
641         } else if (prop == ZFS_PROP_ZONED) {
642                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
643                 clp->cl_allchildren = B_TRUE;
644         } else if (prop == ZFS_PROP_CANMOUNT) {
645                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
646         } else if (prop == ZFS_PROP_VOLSIZE) {
647                 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
648         } else {
649                 clp->cl_prop = prop;
650         }
651         clp->cl_realprop = prop;
652
653         if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
654             clp->cl_prop != ZFS_PROP_SHARENFS &&
655             clp->cl_prop != ZFS_PROP_SHARESMB)
656                 return (clp);
657
658         /*
659          * If watching SHARENFS or SHARESMB then
660          * also watch its companion property.
661          */
662         if (clp->cl_prop == ZFS_PROP_SHARENFS)
663                 clp->cl_shareprop = ZFS_PROP_SHARESMB;
664         else if (clp->cl_prop == ZFS_PROP_SHARESMB)
665                 clp->cl_shareprop = ZFS_PROP_SHARENFS;
666
667         if (clp->cl_alldependents) {
668                 if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
669                         changelist_free(clp);
670                         return (NULL);
671                 }
672         } else if (zfs_iter_children(zhp, change_one, clp) != 0) {
673                 changelist_free(clp);
674                 return (NULL);
675         }
676
677         /*
678          * We have to re-open ourselves because we auto-close all the handles
679          * and can't tell the difference.
680          */
681         if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
682             ZFS_TYPE_DATASET)) == NULL) {
683                 changelist_free(clp);
684                 return (NULL);
685         }
686
687         /*
688          * Always add ourself to the list.  We add ourselves to the end so that
689          * we're the last to be unmounted.
690          */
691         if ((cn = zfs_alloc(zhp->zfs_hdl,
692             sizeof (prop_changenode_t))) == NULL) {
693                 zfs_close(temp);
694                 changelist_free(clp);
695                 return (NULL);
696         }
697
698         cn->cn_handle = temp;
699         cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
700             zfs_is_mounted(temp, NULL);
701         cn->cn_shared = zfs_is_shared(temp);
702         cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
703         cn->cn_needpost = B_TRUE;
704
705         uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
706         if (clp->cl_sorted) {
707                 uu_list_index_t idx;
708                 (void) uu_list_find(clp->cl_list, cn, NULL, &idx);
709                 uu_list_insert(clp->cl_list, cn, idx);
710         } else {
711                 /*
712                  * Add the target dataset to the end of the list.
713                  * The list is not really unsorted. The list will be
714                  * in reverse dataset name order. This is necessary
715                  * when the original mountpoint is legacy or none.
716                  */
717                 verify(uu_list_insert_after(clp->cl_list,
718                     uu_list_last(clp->cl_list), cn) == 0);
719         }
720
721         /*
722          * If the mountpoint property was previously 'legacy', or 'none',
723          * record it as the behavior of changelist_postfix() will be different.
724          */
725         if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
726                 /*
727                  * do not automatically mount ex-legacy datasets if
728                  * we specifically set canmount to noauto
729                  */
730                 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
731                     ZFS_CANMOUNT_NOAUTO)
732                         clp->cl_waslegacy = B_TRUE;
733         }
734
735         return (clp);
736 }