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 2022 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 if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, root_ds, 154 sizeof (root_ds))) != BE_SUCCESS) { 155 be_print_err(gettext("%s: failed to get BE container dataset " 156 "for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name); 157 goto done; 158 }; 159 bt.obe_root_ds = strdup(root_ds); 160 if ((ret = be_make_root_ds(bt.nbe_zpool, bt.nbe_name, root_ds, 161 sizeof (root_ds))) != BE_SUCCESS) { 162 be_print_err(gettext("%s: failed to get BE container dataset " 163 "for %s/%s\n"), __func__, bt.nbe_zpool, bt.nbe_name); 164 goto done; 165 } 166 bt.nbe_root_ds = strdup(root_ds); 167 168 /* 169 * Generate a list of file systems from the BE that are legacy 170 * mounted before renaming. We use this list to determine which 171 * entries in the vfstab we need to update after we've renamed the BE. 172 */ 173 if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL, 174 &fld)) != BE_SUCCESS) { 175 be_print_err(gettext("be_rename: failed to " 176 "get legacy mounted file system list for %s\n"), 177 bt.obe_name); 178 goto done; 179 } 180 181 /* Get handle to BE's root dataset */ 182 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) 183 == NULL) { 184 be_print_err(gettext("be_rename: failed to " 185 "open BE root dataset (%s): %s\n"), 186 bt.obe_root_ds, libzfs_error_description(g_zfs)); 187 ret = zfs_err_to_be_err(g_zfs); 188 goto done; 189 } 190 191 /* Rename of BE's root dataset. */ 192 if (zfs_rename(zhp, bt.nbe_root_ds, B_FALSE, B_FALSE) != 0) { 193 be_print_err(gettext("be_rename: failed to " 194 "rename dataset (%s): %s\n"), bt.obe_root_ds, 195 libzfs_error_description(g_zfs)); 196 ret = zfs_err_to_be_err(g_zfs); 197 goto done; 198 } 199 200 /* Change the nextboot property on the pool if necessary */ 201 if (getzoneid() == GLOBAL_ZONEID) { 202 char *nextboot = NULL; 203 int rv = 0; 204 205 if (lzbe_get_boot_device(bt.obe_zpool, &nextboot) == 0 && 206 nextboot != NULL && strcmp(nextboot, bt.obe_root_ds) == 0) { 207 if ((rv = lzbe_set_boot_device(bt.obe_zpool, 208 lzbe_add, "")) != 0) { 209 be_print_err(gettext("be_rename: failed to " 210 "remove temporary activation for " 211 "dataset %s on pool %s\n"), 212 bt.obe_root_ds, bt.obe_zpool); 213 } 214 if (rv == 0 && (rv = lzbe_set_boot_device(bt.nbe_zpool, 215 lzbe_add, bt.nbe_root_ds)) != 0) { 216 be_print_err(gettext("be_rename: failed to " 217 "enable temporary activation for " 218 "dataset %s on pool %s\n"), 219 bt.nbe_root_ds, bt.nbe_zpool); 220 } 221 } 222 free(nextboot); 223 switch (rv) { 224 case 0: 225 break; 226 case ENOMEM: 227 ret = BE_ERR_NOMEM; 228 goto done; 229 default: 230 ret = BE_ERR_UNKNOWN; 231 goto done; 232 } 233 } 234 235 /* Refresh handle to BE's root dataset after the rename */ 236 ZFS_CLOSE(zhp); 237 if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds, ZFS_TYPE_FILESYSTEM)) 238 == NULL) { 239 be_print_err(gettext("be_rename: failed to " 240 "open BE root dataset (%s): %s\n"), 241 bt.nbe_root_ds, libzfs_error_description(g_zfs)); 242 ret = zfs_err_to_be_err(g_zfs); 243 goto done; 244 } 245 246 /* If BE is already mounted, get its mountpoint */ 247 if (zfs_is_mounted(zhp, &mp) && mp == NULL) { 248 be_print_err(gettext("be_rename: failed to " 249 "get altroot of mounted BE %s: %s\n"), 250 bt.nbe_name, libzfs_error_description(g_zfs)); 251 ret = zfs_err_to_be_err(g_zfs); 252 goto done; 253 } 254 255 /* Update BE's vfstab */ 256 257 /* 258 * Since the new and old BEs reside in the same pool (see above), 259 * the same variable can be used for the container for both. 260 */ 261 if ((ret = be_make_root_container_ds(bt.obe_zpool, be_root_container, 262 sizeof (be_root_container))) != BE_SUCCESS) { 263 be_print_err(gettext("%s: failed to get BE container dataset " 264 "for %s\n"), __func__, bt.obe_zpool); 265 goto done; 266 } 267 268 if ((ret = be_update_vfstab(bt.nbe_name, be_root_container, 269 be_root_container, &fld, mp)) != BE_SUCCESS) { 270 be_print_err(gettext("be_rename: " 271 "failed to update new BE's vfstab (%s)\n"), bt.nbe_name); 272 goto done; 273 } 274 275 /* Update this BE's GRUB menu entry */ 276 if (getzoneid() == GLOBAL_ZONEID && (ret = be_update_menu(bt.obe_name, 277 bt.nbe_name, bt.obe_zpool, NULL)) != BE_SUCCESS) { 278 be_print_err(gettext("be_rename: " 279 "failed to update grub menu entry from %s to %s\n"), 280 bt.obe_name, bt.nbe_name); 281 } 282 283 done: 284 be_free_fs_list(&fld); 285 286 ZFS_CLOSE(zhp); 287 288 be_zfs_fini(); 289 290 free(bt.obe_root_ds); 291 free(bt.nbe_root_ds); 292 return (ret); 293 } 294