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