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#include <sys/cdefs.h>
28#ifdef __FBSDID
29__FBSDID("$FreeBSD: src/sbin/gpt/add.c,v 1.14 2006/06/22 22:05:28 marcel Exp $");
30#endif
31#ifdef __RCSID
32__RCSID("$NetBSD: add.c,v 1.10 2011/01/06 17:51:28 riz Exp $");
33#endif
34
35#include <sys/types.h>
36
37#include <err.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <inttypes.h>
44
45#include "map.h"
46#include "gpt.h"
47
48static uuid_t type;
49static off_t block, size;
50static unsigned int entry;
51
52const char addmsg[] = "add [-b lba] [-i index] [-s lba] [-t type] "
53	"device ...";
54
55__dead static void
56usage_add(void)
57{
58
59	fprintf(stderr,
60	    "usage: %s %s\n",
61	    getprogname(), addmsg);
62	exit(1);
63}
64
65static void
66add(int fd)
67{
68	map_t *gpt, *tpg;
69	map_t *tbl, *lbt;
70	map_t *map;
71	struct gpt_hdr *hdr;
72	struct gpt_ent *ent;
73	unsigned int i;
74
75	gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
76	ent = NULL;
77	if (gpt == NULL) {
78		warnx("%s: error: no primary GPT header; run create or recover",
79		    device_name);
80		return;
81	}
82
83	tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
84	if (tpg == NULL) {
85		warnx("%s: error: no secondary GPT header; run recover",
86		    device_name);
87		return;
88	}
89
90	tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
91	lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
92	if (tbl == NULL || lbt == NULL) {
93		warnx("%s: error: run recover -- trust me", device_name);
94		return;
95	}
96
97	hdr = gpt->map_data;
98	if (entry > le32toh(hdr->hdr_entries)) {
99		warnx("%s: error: index %u out of range (%u max)", device_name,
100		    entry, le32toh(hdr->hdr_entries));
101		return;
102	}
103
104	if (entry > 0) {
105		i = entry - 1;
106		ent = (void*)((char*)tbl->map_data + i *
107		    le32toh(hdr->hdr_entsz));
108		if (!uuid_is_nil((uuid_t *)&ent->ent_type, NULL)) {
109			warnx("%s: error: entry at index %u is not free",
110			    device_name, entry);
111			return;
112		}
113	} else {
114		/* Find empty slot in GPT table. */
115		for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
116			ent = (void*)((char*)tbl->map_data + i *
117			    le32toh(hdr->hdr_entsz));
118			if (uuid_is_nil((uuid_t *)&ent->ent_type, NULL))
119				break;
120		}
121		if (i == le32toh(hdr->hdr_entries)) {
122			warnx("%s: error: no available table entries",
123			    device_name);
124			return;
125		}
126	}
127
128	map = map_alloc(block, size);
129	if (map == NULL) {
130		warnx("%s: error: no space available on device", device_name);
131		return;
132	}
133
134	le_uuid_enc((uuid_t *)&ent->ent_type, &type);
135	ent->ent_lba_start = htole64(map->map_start);
136	ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL);
137
138	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
139	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
140	hdr->hdr_crc_self = 0;
141	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
142
143	gpt_write(fd, gpt);
144	gpt_write(fd, tbl);
145
146	hdr = tpg->map_data;
147	ent = (void*)((char*)lbt->map_data + i * le32toh(hdr->hdr_entsz));
148
149	le_uuid_enc(&ent->ent_type, &type);
150	ent->ent_lba_start = htole64(map->map_start);
151	ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL);
152
153	hdr->hdr_crc_table = htole32(crc32(lbt->map_data,
154	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
155	hdr->hdr_crc_self = 0;
156	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
157
158	gpt_write(fd, lbt);
159	gpt_write(fd, tpg);
160
161#ifdef __FreeBSD__
162	printf("%sp%u added\n", device_name, i + 1);
163#endif
164#ifdef __NetBSD__
165	printf("Partition added, use:\n");
166	printf("\tdkctl %s addwedge <wedgename> %" PRIu64 " %" PRIu64
167	    " <type>\n", device_arg, map->map_start, map->map_size);
168	printf("to create a wedge for it\n");
169#endif
170}
171
172int
173cmd_add(int argc, char *argv[])
174{
175	char *p;
176	int ch, fd;
177
178	/* Get the migrate options */
179	while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) {
180		switch(ch) {
181		case 'b':
182			if (block > 0)
183				usage_add();
184			block = strtoll(optarg, &p, 10);
185			if (*p != 0 || block < 1)
186				usage_add();
187			break;
188		case 'i':
189			if (entry > 0)
190				usage_add();
191			entry = strtoul(optarg, &p, 10);
192			if (*p != 0 || entry < 1)
193				usage_add();
194			break;
195		case 's':
196			if (size > 0)
197				usage_add();
198			size = strtoll(optarg, &p, 10);
199			if (*p != 0 || size < 1)
200				usage_add();
201			break;
202		case 't':
203			if (!uuid_is_nil(&type, NULL))
204				usage_add();
205			if (parse_uuid(optarg, &type) != 0)
206				usage_add();
207			break;
208		default:
209			usage_add();
210		}
211	}
212
213	if (argc == optind)
214		usage_add();
215
216	/* Create NetBSD FFS partitions by default. */
217	if (uuid_is_nil(&type, NULL)) {
218		uuid_t nb_ffs = GPT_ENT_TYPE_NETBSD_FFS;
219		type = nb_ffs;
220	}
221
222	while (optind < argc) {
223		fd = gpt_open(argv[optind++]);
224		if (fd == -1) {
225			warn("unable to open device '%s'", device_name);
226			continue;
227		}
228
229		add(fd);
230
231		gpt_close(fd);
232	}
233
234	return (0);
235}
236