]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
MFC r368207,368607:
[FreeBSD/stable/10.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, 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
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
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
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
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
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
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
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
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
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
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 *
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 }