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, 2015 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
changelist_prefix(prop_changelist_t * clp)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
changelist_postfix(prop_changelist_t * clp)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 
170           /*
171            * If we're changing the mountpoint, attempt to destroy the underlying
172            * mountpoint.  All other datasets will have inherited from this dataset
173            * (in which case their mountpoints exist in the filesystem in the new
174            * location), or have explicit mountpoints set (in which case they won't
175            * be in the changelist).
176            */
177           if ((cn = uu_list_last(clp->cl_list)) == NULL)
178                     return (0);
179 
180           if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
181               !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
182                     remove_mountpoint(cn->cn_handle);
183           }
184 
185           /*
186            * It is possible that the changelist_prefix() used libshare
187            * to unshare some entries. Since libshare caches data, an
188            * attempt to reshare during postfix can fail unless libshare
189            * is uninitialized here so that it will reinitialize later.
190            */
191           if (cn->cn_handle != NULL) {
192                     hdl = cn->cn_handle->zfs_hdl;
193                     assert(hdl != NULL);
194                     zfs_uninit_libshare(hdl);
195           }
196 
197           /*
198            * We walk the datasets in reverse, because we want to mount any parent
199            * datasets before mounting the children.  We walk all datasets even if
200            * there are errors.
201            */
202           for (cn = uu_list_last(clp->cl_list); cn != NULL;
203               cn = uu_list_prev(clp->cl_list, cn)) {
204 
205                     boolean_t sharenfs;
206                     boolean_t sharesmb;
207                     boolean_t mounted;
208 
209                     /*
210                      * If we are in the global zone, but this dataset is exported
211                      * to a local zone, do nothing.
212                      */
213                     if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
214                               continue;
215 
216                     /* Only do post-processing if it's required */
217                     if (!cn->cn_needpost)
218                               continue;
219                     cn->cn_needpost = B_FALSE;
220 
221                     zfs_refresh_properties(cn->cn_handle);
222 
223                     if (ZFS_IS_VOLUME(cn->cn_handle))
224                               continue;
225 
226                     /*
227                      * Remount if previously mounted or mountpoint was legacy,
228                      * or sharenfs or sharesmb  property is set.
229                      */
230                     sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
231                         shareopts, sizeof (shareopts), NULL, NULL, 0,
232                         B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
233 
234                     sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
235                         shareopts, sizeof (shareopts), NULL, NULL, 0,
236                         B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
237 
238                     mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
239                         zfs_is_mounted(cn->cn_handle, NULL);
240 
241                     if (!mounted && (cn->cn_mounted ||
242                         ((sharenfs || sharesmb || clp->cl_waslegacy) &&
243                         (zfs_prop_get_int(cn->cn_handle,
244                         ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
245 
246                               if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
247                                         errors++;
248                               else
249                                         mounted = TRUE;
250                     }
251 
252                     /*
253                      * If the file system is mounted we always re-share even
254                      * if the filesystem is currently shared, so that we can
255                      * adopt any new options.
256                      */
257                     if (sharenfs && mounted)
258                               errors += zfs_share_nfs(cn->cn_handle);
259                     else if (cn->cn_shared || clp->cl_waslegacy)
260                               errors += zfs_unshare_nfs(cn->cn_handle, NULL);
261                     if (sharesmb && mounted)
262                               errors += zfs_share_smb(cn->cn_handle);
263                     else if (cn->cn_shared || clp->cl_waslegacy)
264                               errors += zfs_unshare_smb(cn->cn_handle, NULL);
265           }
266 
267           return (errors ? -1 : 0);
268 }
269 
270 /*
271  * Is this "dataset" a child of "parent"?
272  */
273 boolean_t
isa_child_of(const char * dataset,const char * parent)274 isa_child_of(const char *dataset, const char *parent)
275 {
276           int len;
277 
278           len = strlen(parent);
279 
280           if (strncmp(dataset, parent, len) == 0 &&
281               (dataset[len] == '@' || dataset[len] == '/' ||
282               dataset[len] == '\0'))
283                     return (B_TRUE);
284           else
285                     return (B_FALSE);
286 
287 }
288 
289 /*
290  * If we rename a filesystem, child filesystem handles are no longer valid
291  * since we identify each dataset by its name in the ZFS namespace.  As a
292  * result, we have to go through and fix up all the names appropriately.  We
293  * could do this automatically if libzfs kept track of all open handles, but
294  * this is a lot less work.
295  */
296 void
changelist_rename(prop_changelist_t * clp,const char * src,const char * dst)297 changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
298 {
299           prop_changenode_t *cn;
300           char newname[ZFS_MAX_DATASET_NAME_LEN];
301 
302           for (cn = uu_list_first(clp->cl_list); cn != NULL;
303               cn = uu_list_next(clp->cl_list, cn)) {
304                     /*
305                      * Do not rename a clone that's not in the source hierarchy.
306                      */
307                     if (!isa_child_of(cn->cn_handle->zfs_name, src))
308                               continue;
309 
310                     /*
311                      * Destroy the previous mountpoint if needed.
312                      */
313                     remove_mountpoint(cn->cn_handle);
314 
315                     (void) strlcpy(newname, dst, sizeof (newname));
316                     (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
317 
318                     (void) strlcpy(cn->cn_handle->zfs_name, newname,
319                         sizeof (cn->cn_handle->zfs_name));
320           }
321 }
322 
323 /*
324  * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
325  * unshare all the datasets in the list.
326  */
327 int
changelist_unshare(prop_changelist_t * clp,zfs_share_proto_t * proto)328 changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
329 {
330           prop_changenode_t *cn;
331           int ret = 0;
332 
333           if (clp->cl_prop != ZFS_PROP_SHARENFS &&
334               clp->cl_prop != ZFS_PROP_SHARESMB)
335                     return (0);
336 
337           for (cn = uu_list_first(clp->cl_list); cn != NULL;
338               cn = uu_list_next(clp->cl_list, cn)) {
339                     if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
340                               ret = -1;
341           }
342 
343           return (ret);
344 }
345 
346 /*
347  * Check if there is any child exported to a local zone in a given changelist.
348  * This information has already been recorded while gathering the changelist
349  * via changelist_gather().
350  */
351 int
changelist_haszonedchild(prop_changelist_t * clp)352 changelist_haszonedchild(prop_changelist_t *clp)
353 {
354           return (clp->cl_haszonedchild);
355 }
356 
357 /*
358  * Remove a node from a gathered list.
359  */
360 void
changelist_remove(prop_changelist_t * clp,const char * name)361 changelist_remove(prop_changelist_t *clp, const char *name)
362 {
363           prop_changenode_t *cn;
364 
365           for (cn = uu_list_first(clp->cl_list); cn != NULL;
366               cn = uu_list_next(clp->cl_list, cn)) {
367 
368                     if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
369                               uu_list_remove(clp->cl_list, cn);
370                               zfs_close(cn->cn_handle);
371                               free(cn);
372                               return;
373                     }
374           }
375 }
376 
377 /*
378  * Release any memory associated with a changelist.
379  */
380 void
changelist_free(prop_changelist_t * clp)381 changelist_free(prop_changelist_t *clp)
382 {
383           prop_changenode_t *cn;
384           void *cookie;
385 
386           if (clp->cl_list) {
387                     cookie = NULL;
388                     while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
389                               zfs_close(cn->cn_handle);
390                               free(cn);
391                     }
392 
393                     uu_list_destroy(clp->cl_list);
394           }
395           if (clp->cl_pool)
396                     uu_list_pool_destroy(clp->cl_pool);
397 
398           free(clp);
399 }
400 
401 static int
change_one(zfs_handle_t * zhp,void * data)402 change_one(zfs_handle_t *zhp, void *data)
403 {
404           prop_changelist_t *clp = data;
405           char property[ZFS_MAXPROPLEN];
406           char where[64];
407           prop_changenode_t *cn;
408           zprop_source_t sourcetype;
409           zprop_source_t share_sourcetype;
410 
411           /*
412            * We only want to unmount/unshare those filesystems that may inherit
413            * from the target filesystem.  If we find any filesystem with a
414            * locally set mountpoint, we ignore any children since changing the
415            * property will not affect them.  If this is a rename, we iterate
416            * over all children regardless, since we need them unmounted in
417            * order to do the rename.  Also, if this is a volume and we're doing
418            * a rename, then always add it to the changelist.
419            */
420 
421           if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
422               zfs_prop_get(zhp, clp->cl_prop, property,
423               sizeof (property), &sourcetype, where, sizeof (where),
424               B_FALSE) != 0) {
425                     zfs_close(zhp);
426                     return (0);
427           }
428 
429           /*
430            * If we are "watching" sharenfs or sharesmb
431            * then check out the companion property which is tracked
432            * in cl_shareprop
433            */
434           if (clp->cl_shareprop != ZPROP_INVAL &&
435               zfs_prop_get(zhp, clp->cl_shareprop, property,
436               sizeof (property), &share_sourcetype, where, sizeof (where),
437               B_FALSE) != 0) {
438                     zfs_close(zhp);
439                     return (0);
440           }
441 
442           if (clp->cl_alldependents || clp->cl_allchildren ||
443               sourcetype == ZPROP_SRC_DEFAULT ||
444               sourcetype == ZPROP_SRC_INHERITED ||
445               (clp->cl_shareprop != ZPROP_INVAL &&
446               (share_sourcetype == ZPROP_SRC_DEFAULT ||
447               share_sourcetype == ZPROP_SRC_INHERITED))) {
448                     if ((cn = zfs_alloc(zfs_get_handle(zhp),
449                         sizeof (prop_changenode_t))) == NULL) {
450                               zfs_close(zhp);
451                               return (-1);
452                     }
453 
454                     cn->cn_handle = zhp;
455                     cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
456                         zfs_is_mounted(zhp, NULL);
457                     cn->cn_shared = zfs_is_shared(zhp);
458                     cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
459                     cn->cn_needpost = B_TRUE;
460 
461                     /* Indicate if any child is exported to a local zone. */
462                     if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
463                               clp->cl_haszonedchild = B_TRUE;
464 
465                     uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
466 
467                     if (clp->cl_sorted) {
468                               uu_list_index_t idx;
469 
470                               (void) uu_list_find(clp->cl_list, cn, NULL,
471                                   &idx);
472                               uu_list_insert(clp->cl_list, cn, idx);
473                     } else {
474                               /*
475                                * Add this child to beginning of the list. Children
476                                * below this one in the hierarchy will get added above
477                                * this one in the list. This produces a list in
478                                * reverse dataset name order.
479                                * This is necessary when the original mountpoint
480                                * is legacy or none.
481                                */
482                               verify(uu_list_insert_before(clp->cl_list,
483                                   uu_list_first(clp->cl_list), cn) == 0);
484                     }
485 
486                     if (!clp->cl_alldependents)
487                               return (zfs_iter_children(zhp, change_one, data));
488           } else {
489                     zfs_close(zhp);
490           }
491 
492           return (0);
493 }
494 
495 /*ARGSUSED*/
496 static int
compare_mountpoints(const void * a,const void * b,void * unused)497 compare_mountpoints(const void *a, const void *b, void *unused)
498 {
499           const prop_changenode_t *ca = a;
500           const prop_changenode_t *cb = b;
501 
502           char mounta[MAXPATHLEN];
503           char mountb[MAXPATHLEN];
504 
505           boolean_t hasmounta, hasmountb;
506 
507           /*
508            * When unsharing or unmounting filesystems, we need to do it in
509            * mountpoint order.  This allows the user to have a mountpoint
510            * hierarchy that is different from the dataset hierarchy, and still
511            * allow it to be changed.  However, if either dataset doesn't have a
512            * mountpoint (because it is a volume or a snapshot), we place it at the
513            * end of the list, because it doesn't affect our change at all.
514            */
515           hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
516               sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
517           hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
518               sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
519 
520           if (!hasmounta && hasmountb)
521                     return (-1);
522           else if (hasmounta && !hasmountb)
523                     return (1);
524           else if (!hasmounta && !hasmountb)
525                     return (0);
526           else
527                     return (strcmp(mountb, mounta));
528 }
529 
530 /*
531  * Given a ZFS handle and a property, construct a complete list of datasets
532  * that need to be modified as part of this process.  For anything but the
533  * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
534  * Otherwise, we iterate over all children and look for any datasets that
535  * inherit the property.  For each such dataset, we add it to the list and
536  * mark whether it was shared beforehand.
537  */
538 prop_changelist_t *
changelist_gather(zfs_handle_t * zhp,zfs_prop_t prop,int gather_flags,int mnt_flags)539 changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
540     int mnt_flags)
541 {
542           prop_changelist_t *clp;
543           prop_changenode_t *cn;
544           zfs_handle_t *temp;
545           char property[ZFS_MAXPROPLEN];
546           uu_compare_fn_t *compare = NULL;
547           boolean_t legacy = B_FALSE;
548 
549           if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
550                     return (NULL);
551 
552           /*
553            * For mountpoint-related tasks, we want to sort everything by
554            * mountpoint, so that we mount and unmount them in the appropriate
555            * order, regardless of their position in the hierarchy.
556            */
557           if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
558               prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
559               prop == ZFS_PROP_SHARESMB) {
560 
561                     if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
562                         property, sizeof (property),
563                         NULL, NULL, 0, B_FALSE) == 0 &&
564                         (strcmp(property, "legacy") == 0 ||
565                         strcmp(property, "none") == 0)) {
566 
567                               legacy = B_TRUE;
568                     }
569                     if (!legacy) {
570                               compare = compare_mountpoints;
571                               clp->cl_sorted = B_TRUE;
572                     }
573           }
574 
575           clp->cl_pool = uu_list_pool_create("changelist_pool",
576               sizeof (prop_changenode_t),
577               offsetof(prop_changenode_t, cn_listnode),
578               compare, 0);
579           if (clp->cl_pool == NULL) {
580                     assert(uu_error() == UU_ERROR_NO_MEMORY);
581                     (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
582                     changelist_free(clp);
583                     return (NULL);
584           }
585 
586           clp->cl_list = uu_list_create(clp->cl_pool, NULL,
587               clp->cl_sorted ? UU_LIST_SORTED : 0);
588           clp->cl_gflags = gather_flags;
589           clp->cl_mflags = mnt_flags;
590 
591           if (clp->cl_list == NULL) {
592                     assert(uu_error() == UU_ERROR_NO_MEMORY);
593                     (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
594                     changelist_free(clp);
595                     return (NULL);
596           }
597 
598           /*
599            * If this is a rename or the 'zoned' property, we pretend we're
600            * changing the mountpoint and flag it so we can catch all children in
601            * change_one().
602            *
603            * Flag cl_alldependents to catch all children plus the dependents
604            * (clones) that are not in the hierarchy.
605            */
606           if (prop == ZFS_PROP_NAME) {
607                     clp->cl_prop = ZFS_PROP_MOUNTPOINT;
608                     clp->cl_alldependents = B_TRUE;
609           } else if (prop == ZFS_PROP_ZONED) {
610                     clp->cl_prop = ZFS_PROP_MOUNTPOINT;
611                     clp->cl_allchildren = B_TRUE;
612           } else if (prop == ZFS_PROP_CANMOUNT) {
613                     clp->cl_prop = ZFS_PROP_MOUNTPOINT;
614           } else if (prop == ZFS_PROP_VOLSIZE) {
615                     clp->cl_prop = ZFS_PROP_MOUNTPOINT;
616           } else {
617                     clp->cl_prop = prop;
618           }
619           clp->cl_realprop = prop;
620 
621           if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
622               clp->cl_prop != ZFS_PROP_SHARENFS &&
623               clp->cl_prop != ZFS_PROP_SHARESMB)
624                     return (clp);
625 
626           /*
627            * If watching SHARENFS or SHARESMB then
628            * also watch its companion property.
629            */
630           if (clp->cl_prop == ZFS_PROP_SHARENFS)
631                     clp->cl_shareprop = ZFS_PROP_SHARESMB;
632           else if (clp->cl_prop == ZFS_PROP_SHARESMB)
633                     clp->cl_shareprop = ZFS_PROP_SHARENFS;
634 
635           if (clp->cl_alldependents) {
636                     if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
637                               changelist_free(clp);
638                               return (NULL);
639                     }
640           } else if (zfs_iter_children(zhp, change_one, clp) != 0) {
641                     changelist_free(clp);
642                     return (NULL);
643           }
644 
645           /*
646            * We have to re-open ourselves because we auto-close all the handles
647            * and can't tell the difference.
648            */
649           if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
650               ZFS_TYPE_DATASET)) == NULL) {
651                     changelist_free(clp);
652                     return (NULL);
653           }
654 
655           /*
656            * Always add ourself to the list.  We add ourselves to the end so that
657            * we're the last to be unmounted.
658            */
659           if ((cn = zfs_alloc(zhp->zfs_hdl,
660               sizeof (prop_changenode_t))) == NULL) {
661                     zfs_close(temp);
662                     changelist_free(clp);
663                     return (NULL);
664           }
665 
666           cn->cn_handle = temp;
667           cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
668               zfs_is_mounted(temp, NULL);
669           cn->cn_shared = zfs_is_shared(temp);
670           cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
671           cn->cn_needpost = B_TRUE;
672 
673           uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
674           if (clp->cl_sorted) {
675                     uu_list_index_t idx;
676                     (void) uu_list_find(clp->cl_list, cn, NULL, &idx);
677                     uu_list_insert(clp->cl_list, cn, idx);
678           } else {
679                     /*
680                      * Add the target dataset to the end of the list.
681                      * The list is not really unsorted. The list will be
682                      * in reverse dataset name order. This is necessary
683                      * when the original mountpoint is legacy or none.
684                      */
685                     verify(uu_list_insert_after(clp->cl_list,
686                         uu_list_last(clp->cl_list), cn) == 0);
687           }
688 
689           /*
690            * If the mountpoint property was previously 'legacy', or 'none',
691            * record it as the behavior of changelist_postfix() will be different.
692            */
693           if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
694                     /*
695                      * do not automatically mount ex-legacy datasets if
696                      * we specifically set canmount to noauto
697                      */
698                     if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
699                         ZFS_CANMOUNT_NOAUTO)
700                               clp->cl_waslegacy = B_TRUE;
701           }
702 
703           return (clp);
704 }
705