1 /*- 2 * Copyright (c) 2002 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #if HAVE_NBTOOL_CONFIG_H 28 #include "nbtool_config.h" 29 #endif 30 31 #include <sys/cdefs.h> 32 #ifdef __FBSDID 33 __FBSDID("$FreeBSD: src/sbin/gpt/add.c,v 1.14 2006/06/22 22:05:28 marcel Exp $"); 34 #endif 35 #ifdef __RCSID 36 __RCSID("$NetBSD: add.c,v 1.48 2025/12/17 16:18:11 nia Exp $"); 37 #endif 38 39 #include <sys/types.h> 40 #if defined(HAVE_SYS_ENDIAN_H) || ! defined(HAVE_NBTOOL_CONFIG_H) 41 #include <sys/endian.h> 42 #endif 43 #include <sys/stat.h> 44 45 #include <err.h> 46 #include <stddef.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #include "map.h" 53 #include "gpt.h" 54 #include "gpt_private.h" 55 56 static int cmd_add(gpt_t, int, char *[]); 57 58 static const char *addhelp[] = { 59 "[-a alignment] [-b blocknr] [-i index] [-l label]", 60 "[-s size] [-t type]", 61 }; 62 63 const struct gpt_cmd c_add = { 64 "add", 65 cmd_add, 66 addhelp, __arraycount(addhelp), 67 GPT_SYNC, 68 }; 69 70 #define usage() gpt_usage(NULL, &c_add) 71 72 static void 73 ent_set(struct gpt_ent *ent, const map_t map, const gpt_uuid_t xtype, 74 const uint8_t *xname) 75 { 76 gpt_uuid_copy(ent->ent_type, xtype); 77 ent->ent_lba_start = htole64((uint64_t)map->map_start); 78 ent->ent_lba_end = htole64((uint64_t)(map->map_start + 79 map->map_size - 1LL)); 80 if (xname == NULL) 81 return; 82 utf8_to_utf16(xname, ent->ent_name, __arraycount(ent->ent_name)); 83 } 84 85 static int 86 add(gpt_t gpt, off_t alignment, off_t block, off_t sectors, off_t size __unused, 87 u_int entry, uint8_t *name, gpt_uuid_t type) 88 { 89 map_t map; 90 struct gpt_hdr *hdr; 91 struct gpt_ent *ent; 92 unsigned int i; 93 off_t alignsecs; 94 char buf[128]; 95 96 if ((hdr = gpt_hdr(gpt)) == NULL) 97 return -1; 98 99 ent = NULL; 100 101 if (entry > le32toh(hdr->hdr_entries)) { 102 gpt_warnx(gpt, "index %u out of range (%u max)", 103 entry, le32toh(hdr->hdr_entries)); 104 return -1; 105 } 106 107 if (entry > 0) { 108 i = entry - 1; 109 ent = gpt_ent_primary(gpt, i); 110 if (!gpt_uuid_is_nil(ent->ent_type)) { 111 gpt_warnx(gpt, "Entry at index %u is not free", entry); 112 return -1; 113 } 114 } else { 115 /* Find empty slot in GPT table. */ 116 for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 117 ent = gpt_ent_primary(gpt, i); 118 if (gpt_uuid_is_nil(ent->ent_type)) 119 break; 120 } 121 if (i == le32toh(hdr->hdr_entries)) { 122 gpt_warnx(gpt, "No available table entries"); 123 return -1; 124 } 125 } 126 127 if (gpt_uuid_is_nil(ent->ent_guid)) 128 if (gpt_uuid_generate(gpt, ent->ent_guid) == -1) { 129 gpt_warnx(gpt, "Unable to make UUID"); 130 return -1; 131 } 132 133 if (alignment > 0) { 134 alignsecs = alignment / gpt->secsz; 135 map = map_alloc(gpt, block, sectors, alignsecs); 136 if (map == NULL) { 137 gpt_warnx(gpt, "Not enough space available on " 138 "device for an aligned partition"); 139 return -1; 140 } 141 } else { 142 map = map_alloc(gpt, block, sectors, 0); 143 if (map == NULL) { 144 gpt_warnx(gpt, "Not enough space available on device"); 145 return -1; 146 } 147 } 148 149 ent_set(ent, map, type, name); 150 if (gpt_write_primary(gpt) == -1) 151 return -1; 152 153 ent = gpt_ent_backup(gpt, i); 154 ent_set(ent, map, type, name); 155 if (gpt_write_backup(gpt) == -1) 156 return -1; 157 158 gpt_uuid_snprintf(buf, sizeof(buf), "%d", type); 159 gpt_msg(gpt, "Partition %d added: %s %" PRIu64 " %" PRIu64, i + 1, 160 buf, map->map_start, map->map_size); 161 return 0; 162 } 163 164 static int 165 cmd_add(gpt_t gpt, int argc, char *argv[]) 166 { 167 int ch; 168 off_t alignment = 0, block = 0, sectors = 0, size = 0; 169 unsigned int entry = 0; 170 uint8_t *name = NULL; 171 gpt_uuid_t type; 172 173 gpt_uuid_copy(type, gpt_uuid_nil); 174 175 while ((ch = getopt(argc, argv, GPT_AIS "b:l:t:")) != -1) { 176 switch(ch) { 177 case 'b': 178 if (gpt_human_get(gpt, &block) == -1) 179 goto usage; 180 break; 181 case 'l': 182 if (gpt_name_get(gpt, &name) == -1) 183 goto usage; 184 break; 185 case 't': 186 if (gpt_uuid_get(gpt, &type) == -1) 187 goto usage; 188 break; 189 case 'a': 190 case 's': 191 case 'i': 192 if (gpt_add_ais(gpt, &alignment, &entry, &size, ch) 193 == -1) 194 goto usage; 195 break; 196 default: 197 goto usage; 198 } 199 } 200 201 if (argc != optind) 202 return usage(); 203 204 /* Create NetBSD FFS partitions by default. */ 205 if (gpt_uuid_is_nil(type)) 206 gpt_uuid_create(GPT_TYPE_NETBSD_FFS, type, NULL, 0); 207 208 if (optind != argc) 209 goto cleanup; 210 211 if ((sectors = gpt_check_ais(gpt, alignment, ~0U, size)) == -1) 212 goto cleanup; 213 214 return add(gpt, alignment, block, sectors, size, entry, name, type); 215 usage: 216 return usage(); 217 cleanup: 218 free(name); 219 return -1; 220 } 221