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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2012 by Delphix. All rights reserved. 25 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 #include <assert.h> 29 #include <libintl.h> 30 #include <libnvpair.h> 31 #include <libzfs.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <unistd.h> 38 39 #include <libbe.h> 40 #include <libbe_priv.h> 41 #include <libzfsbootenv.h> 42 43 /* ******************************************************************** */ 44 /* Public Functions */ 45 /* ******************************************************************** */ 46 47 /* 48 * Function: be_rename 49 * Description: Renames the BE from the original name to the new name 50 * passed in through be_attrs. Also the entries in vfstab and 51 * menu.lst are updated with the new name. 52 * Parameters: 53 * be_attrs - pointer to nvlist_t of attributes being passed in. 54 * The following attribute values are used by 55 * this function: 56 * 57 * BE_ATTR_ORIG_BE_NAME *required 58 * BE_ATTR_NEW_BE_NAME *required 59 * Return: 60 * BE_SUCCESS - Success 61 * be_errno_t - Failure 62 * Scope: 63 * Public 64 */ 65 66 int 67 be_rename(nvlist_t *be_attrs) 68 { 69 be_transaction_data_t bt = { 0 }; 70 be_transaction_data_t cbt = { 0 }; 71 be_fs_list_data_t fld = { 0 }; 72 zfs_handle_t *zhp = NULL; 73 char root_ds[MAXPATHLEN]; 74 char be_root_container[MAXPATHLEN]; 75 char *mp = NULL; 76 int zret = 0, ret = BE_SUCCESS; 77 78 /* Initialize libzfs handle */ 79 if (!be_zfs_init()) 80 return (BE_ERR_INIT); 81 82 /* Get original BE name to rename from */ 83 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name) 84 != 0) { 85 be_print_err(gettext("be_rename: failed to " 86 "lookup BE_ATTR_ORIG_BE_NAME attribute\n")); 87 be_zfs_fini(); 88 return (BE_ERR_INVAL); 89 } 90 91 /* Get new BE name to rename to */ 92 if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name) 93 != 0) { 94 be_print_err(gettext("be_rename: failed to " 95 "lookup BE_ATTR_NEW_BE_NAME attribute\n")); 96 be_zfs_fini(); 97 return (BE_ERR_INVAL); 98 } 99 100 /* 101 * Get the currently active BE and check to see if this 102 * is an attempt to rename the currently active BE. 103 */ 104 if (be_find_current_be(&cbt) != BE_SUCCESS) { 105 be_print_err(gettext("be_rename: failed to find the currently " 106 "active BE\n")); 107 be_zfs_fini(); 108 return (BE_ERR_CURR_BE_NOT_FOUND); 109 } 110 111 if (strncmp(bt.obe_name, cbt.obe_name, 112 MAX(strlen(bt.obe_name), strlen(cbt.obe_name))) == 0) { 113 be_print_err(gettext("be_rename: This is an attempt to rename " 114 "the currently active BE, which is not supported\n")); 115 be_zfs_fini(); 116 free(cbt.obe_name); 117 return (BE_ERR_RENAME_ACTIVE); 118 } 119 120 /* Validate original BE name */ 121 if (!be_valid_be_name(bt.obe_name)) { 122 be_print_err(gettext("be_rename: " 123 "invalid BE name %s\n"), bt.obe_name); 124 be_zfs_fini(); 125 return (BE_ERR_INVAL); 126 } 127 128 /* Validate new BE name */ 129 if (!be_valid_be_name(bt.nbe_name)) { 130 be_print_err(gettext("be_rename: invalid BE name %s\n"), 131 bt.nbe_name); 132 be_zfs_fini(); 133 return (BE_ERR_INVAL); 134 } 135 136 /* Find which zpool the BE is in */ 137 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 138 be_print_err(gettext("be_rename: failed to " 139 "find zpool for BE (%s)\n"), bt.obe_name); 140 be_zfs_fini(); 141 return (BE_ERR_BE_NOENT); 142 } else if (zret < 0) { 143 be_print_err(gettext("be_rename: zpool_iter failed: %s\n"), 144 libzfs_error_description(g_zfs)); 145 ret = zfs_err_to_be_err(g_zfs); 146 be_zfs_fini(); 147 return (ret); 148 } 149 150 /* New BE will reside in the same zpool as orig BE */ 151 bt.nbe_zpool = bt.obe_zpool; 152 153 be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds, sizeof (root_ds)); 154 bt.obe_root_ds = strdup(root_ds); 155 be_make_root_ds(bt.nbe_zpool, bt.nbe_name, root_ds, sizeof (root_ds)); 156 bt.nbe_root_ds = strdup(root_ds); 157 158 /* 159 * Generate a list of file systems from the BE that are legacy 160 * mounted before renaming. We use this list to determine which 161 * entries in the vfstab we need to update after we've renamed the BE. 162 */ 163 if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL, 164 &fld)) != BE_SUCCESS) { 165 be_print_err(gettext("be_rename: failed to " 166 "get legacy mounted file system list for %s\n"), 167 bt.obe_name); 168 goto done; 169 } 170 171 /* Get handle to BE's root dataset */ 172 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) 173 == NULL) { 174 be_print_err(gettext("be_rename: failed to " 175 "open BE root dataset (%s): %s\n"), 176 bt.obe_root_ds, libzfs_error_description(g_zfs)); 177 ret = zfs_err_to_be_err(g_zfs); 178 goto done; 179 } 180 181 /* Rename of BE's root dataset. */ 182 if (zfs_rename(zhp, bt.nbe_root_ds, B_FALSE, B_FALSE) != 0) { 183 be_print_err(gettext("be_rename: failed to " 184 "rename dataset (%s): %s\n"), bt.obe_root_ds, 185 libzfs_error_description(g_zfs)); 186 ret = zfs_err_to_be_err(g_zfs); 187 goto done; 188 } 189 190 /* Change the nextboot property on the pool if necessary */ 191 if (getzoneid() == GLOBAL_ZONEID) { 192 char *nextboot = NULL; 193 int rv = 0; 194 195 if (lzbe_get_boot_device(bt.obe_zpool, &nextboot) == 0 && 196 nextboot != NULL && strcmp(nextboot, bt.obe_root_ds) == 0) { 197 if ((rv = lzbe_set_boot_device(bt.obe_zpool, 198 lzbe_add, "")) != 0) { 199 be_print_err(gettext("be_rename: failed to " 200 "remove temporary activation for " 201 "dataset %s on pool %s\n"), 202 bt.obe_root_ds, bt.obe_zpool); 203 } 204 if (rv == 0 && (rv = lzbe_set_boot_device(bt.nbe_zpool, 205 lzbe_add, bt.nbe_root_ds)) != 0) { 206 be_print_err(gettext("be_rename: failed to " 207 "enable temporary activation for " 208 "dataset %s on pool %s\n"), 209 bt.nbe_root_ds, bt.nbe_zpool); 210 } 211 } 212 free(nextboot); 213 switch (rv) { 214 case 0: 215 break; 216 case ENOMEM: 217 ret = BE_ERR_NOMEM; 218 goto done; 219 default: 220 ret = BE_ERR_UNKNOWN; 221 goto done; 222 } 223 } 224 225 /* Refresh handle to BE's root dataset after the rename */ 226 ZFS_CLOSE(zhp); 227 if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds, ZFS_TYPE_FILESYSTEM)) 228 == NULL) { 229 be_print_err(gettext("be_rename: failed to " 230 "open BE root dataset (%s): %s\n"), 231 bt.nbe_root_ds, libzfs_error_description(g_zfs)); 232 ret = zfs_err_to_be_err(g_zfs); 233 goto done; 234 } 235 236 /* If BE is already mounted, get its mountpoint */ 237 if (zfs_is_mounted(zhp, &mp) && mp == NULL) { 238 be_print_err(gettext("be_rename: failed to " 239 "get altroot of mounted BE %s: %s\n"), 240 bt.nbe_name, libzfs_error_description(g_zfs)); 241 ret = zfs_err_to_be_err(g_zfs); 242 goto done; 243 } 244 245 /* Update BE's vfstab */ 246 247 /* 248 * Since the new and old BEs reside in the same pool (see above), 249 * the same variable can be used for the container for both. 250 */ 251 be_make_root_container_ds(bt.obe_zpool, be_root_container, 252 sizeof (be_root_container)); 253 254 if ((ret = be_update_vfstab(bt.nbe_name, be_root_container, 255 be_root_container, &fld, mp)) != BE_SUCCESS) { 256 be_print_err(gettext("be_rename: " 257 "failed to update new BE's vfstab (%s)\n"), bt.nbe_name); 258 goto done; 259 } 260 261 /* Update this BE's GRUB menu entry */ 262 if (getzoneid() == GLOBAL_ZONEID && (ret = be_update_menu(bt.obe_name, 263 bt.nbe_name, bt.obe_zpool, NULL)) != BE_SUCCESS) { 264 be_print_err(gettext("be_rename: " 265 "failed to update grub menu entry from %s to %s\n"), 266 bt.obe_name, bt.nbe_name); 267 } 268 269 done: 270 be_free_fs_list(&fld); 271 272 ZFS_CLOSE(zhp); 273 274 be_zfs_fini(); 275 276 free(bt.obe_root_ds); 277 free(bt.nbe_root_ds); 278 return (ret); 279 } 280