1105816Sphk/*
2121921Smarcel * Copyright (c) 2003 Marcel Moolenaar
3121921Smarcel * All rights reserved.
4121921Smarcel *
5121921Smarcel * Redistribution and use in source and binary forms, with or without
6121921Smarcel * modification, are permitted provided that the following conditions
7121921Smarcel * are met:
8121921Smarcel *
9121921Smarcel * 1. Redistributions of source code must retain the above copyright
10121921Smarcel *    notice, this list of conditions and the following disclaimer.
11121921Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12121921Smarcel *    notice, this list of conditions and the following disclaimer in the
13121921Smarcel *    documentation and/or other materials provided with the distribution.
14121921Smarcel *
15121921Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16121921Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17121921Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18121921Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19121921Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20121921Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21121921Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22121921Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23121921Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24121921Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25121921Smarcel *
26121921Smarcel * CRC32 code derived from work by Gary S. Brown.
27105816Sphk */
28105816Sphk
29105816Sphk#include <sys/cdefs.h>
30105816Sphk__FBSDID("$FreeBSD$");
31105816Sphk
32105816Sphk#include <sys/types.h>
33105816Sphk#include <sys/disklabel.h>
34105816Sphk#include <sys/diskmbr.h>
35121921Smarcel#include <sys/gpt.h>
36121921Smarcel#include <sys/stat.h>
37121921Smarcel
38121921Smarcel#include <errno.h>
39121921Smarcel#include <fcntl.h>
40105816Sphk#include <paths.h>
41121921Smarcel#include <stddef.h>
42121921Smarcel#include <stdio.h>
43121921Smarcel#include <stdlib.h>
44121921Smarcel#include <string.h>
45121921Smarcel#include <unistd.h>
46121921Smarcel#include <uuid.h>
47121921Smarcel
48105816Sphk#include "libdisk.h"
49105816Sphk
50121921Smarcelstatic uuid_t _efi = GPT_ENT_TYPE_EFI;
51121921Smarcelstatic uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD;
52121921Smarcelstatic uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP;
53121921Smarcelstatic uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS;
54121921Smarcel
55121921Smarcelstatic uint32_t crc32_tab[] = {
56121921Smarcel	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
57121921Smarcel	0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
58121921Smarcel	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
59121921Smarcel	0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
60121921Smarcel	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
61121921Smarcel	0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
62121921Smarcel	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
63121921Smarcel	0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
64121921Smarcel	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
65121921Smarcel	0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
66121921Smarcel	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
67121921Smarcel	0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
68121921Smarcel	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
69121921Smarcel	0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
70121921Smarcel	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
71121921Smarcel	0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
72121921Smarcel	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
73121921Smarcel	0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
74121921Smarcel	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
75121921Smarcel	0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
76121921Smarcel	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
77121921Smarcel	0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
78121921Smarcel	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
79121921Smarcel	0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
80121921Smarcel	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
81121921Smarcel	0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
82121921Smarcel	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
83121921Smarcel	0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
84121921Smarcel	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
85121921Smarcel	0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
86121921Smarcel	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
87121921Smarcel	0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
88121921Smarcel	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
89121921Smarcel	0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
90121921Smarcel	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
91121921Smarcel	0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
92121921Smarcel	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
93121921Smarcel	0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
94121921Smarcel	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
95121921Smarcel	0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
96121921Smarcel	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
97121921Smarcel	0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
98121921Smarcel	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
99121921Smarcel};
100121921Smarcel
101122839Smarcelstatic uint32_t
102121921Smarcelcrc32(const void *buf, size_t size)
103121921Smarcel{
104121921Smarcel	const uint8_t *p;
105121921Smarcel	uint32_t crc;
106121921Smarcel
107121921Smarcel	p = buf;
108121921Smarcel	crc = ~0U;
109121921Smarcel
110121921Smarcel	while (size--)
111121921Smarcel		crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
112121921Smarcel
113121921Smarcel	return (crc ^ ~0U);
114121921Smarcel}
115121921Smarcel
116121921Smarcelstatic int
117121921Smarcelwrite_pmbr(int fd, const struct disk *disk)
118121921Smarcel{
119121921Smarcel	struct dos_partition dp;
120121921Smarcel	char *buffer;
121121921Smarcel	u_long nsects;
122121921Smarcel	int error;
123121921Smarcel
124125124Smarcel	error = 0;
125121921Smarcel	nsects = disk->media_size / disk->sector_size;
126161135Smarcel	nsects--;	/* The GPT starts at LBA 1 */
127161135Smarcel
128121921Smarcel	buffer = calloc(disk->sector_size, 1);
129121921Smarcel	if (buffer == NULL)
130121921Smarcel		return (ENOMEM);
131121921Smarcel	buffer[DOSMAGICOFFSET] = DOSMAGIC & 0xff;
132121921Smarcel	buffer[DOSMAGICOFFSET + 1] = DOSMAGIC >> 8;
133121921Smarcel
134121921Smarcel	dp.dp_flag = 0;
135121921Smarcel	dp.dp_shd = dp.dp_ssect = dp.dp_scyl = 0xff;
136121921Smarcel	dp.dp_typ = DOSPTYP_PMBR;
137121921Smarcel	dp.dp_ehd = dp.dp_esect = dp.dp_ecyl = 0xff;
138121921Smarcel	dp.dp_start = 1;
139121921Smarcel	dp.dp_size = (nsects > 0xffffffffu) ? ~0u : nsects;
140121921Smarcel	memcpy(buffer + DOSPARTOFF, &dp, DOSPARTSIZE);
141121921Smarcel
142121921Smarcel	if (lseek(fd, 0L, SEEK_SET) != 0L ||
143121921Smarcel	    write(fd, buffer, disk->sector_size) != disk->sector_size)
144121921Smarcel		error = (errno) ? errno : EAGAIN;
145121921Smarcel
146121921Smarcel	free(buffer);
147121921Smarcel	return (error);
148121921Smarcel}
149121921Smarcel
150121921Smarcelstatic int
151121921Smarcelread_gpt(int fd, const struct disk *disk, struct gpt_hdr *hdr,
152121921Smarcel    struct gpt_ent *tbl)
153121921Smarcel{
154121921Smarcel	char *buffer;
155121921Smarcel	off_t off;
156121921Smarcel	size_t nsects, sz;
157121921Smarcel	int error, i;
158121921Smarcel
159125124Smarcel	error = 0;
160121921Smarcel	nsects = disk->gpt_size * sizeof(struct gpt_ent) / disk->sector_size;
161121921Smarcel	nsects++;
162121921Smarcel	sz = nsects * disk->sector_size;
163121921Smarcel	buffer = malloc(sz);
164121921Smarcel	if (buffer == NULL)
165121921Smarcel		return (ENOMEM);
166121921Smarcel
167121921Smarcel	if (lseek(fd, disk->sector_size, SEEK_SET) != disk->sector_size ||
168121921Smarcel	    read(fd, buffer, disk->sector_size) != disk->sector_size) {
169121921Smarcel		error = (errno) ? errno : EAGAIN;
170121921Smarcel		goto bail;
171121921Smarcel	}
172121921Smarcel	if (memcmp(buffer, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
173121921Smarcel		/*
174121921Smarcel		 * No GPT on disk. Create one out of thin air.
175121921Smarcel		 */
176121921Smarcel		bzero(&hdr[0], sizeof(struct gpt_hdr));
177121921Smarcel		memcpy(hdr[0].hdr_sig, GPT_HDR_SIG, sizeof(hdr[0].hdr_sig));
178121921Smarcel		hdr[0].hdr_revision = GPT_HDR_REVISION;
179121921Smarcel		hdr[0].hdr_size = offsetof(struct gpt_hdr, padding);
180121921Smarcel		hdr[0].hdr_lba_self = 1;
181121921Smarcel		hdr[0].hdr_lba_alt = disk->media_size / disk->sector_size - 1L;
182121921Smarcel		hdr[0].hdr_lba_start = disk->lba_start;
183121921Smarcel		hdr[0].hdr_lba_end = disk->lba_end;
184121921Smarcel		uuid_create(&hdr[0].hdr_uuid, NULL);
185121921Smarcel		hdr[0].hdr_lba_table = 2;
186121921Smarcel		hdr[0].hdr_entries = disk->gpt_size;
187121921Smarcel		hdr[0].hdr_entsz = sizeof(struct gpt_ent);
188121921Smarcel		hdr[1] = hdr[0];
189121921Smarcel		hdr[1].hdr_lba_self = hdr[0].hdr_lba_alt;
190121921Smarcel		hdr[1].hdr_lba_alt = hdr[0].hdr_lba_self;
191121921Smarcel		hdr[1].hdr_lba_table = disk->lba_end + 1;
192121921Smarcel
193121921Smarcel		for (i = 0; i < disk->gpt_size; i++) {
194121921Smarcel			bzero(&tbl[i], sizeof(struct gpt_ent));
195121921Smarcel			uuid_create(&tbl[i].ent_uuid, NULL);
196121921Smarcel		}
197121921Smarcel
198121921Smarcel		goto bail;
199121921Smarcel	}
200121921Smarcel
201121921Smarcel	/*
202121921Smarcel	 * We have a GPT on disk. Read it.
203121921Smarcel	 */
204121921Smarcel	memcpy(&hdr[0], buffer, sizeof(struct gpt_hdr));
205121921Smarcel	off = hdr->hdr_lba_table * disk->sector_size;
206121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
207121921Smarcel	    read(fd, buffer, sz) != sz) {
208121921Smarcel		error = (errno) ? errno : EAGAIN;
209121921Smarcel		goto bail;
210121921Smarcel	}
211121921Smarcel	memcpy(tbl, buffer, sizeof(struct gpt_ent) * disk->gpt_size);
212121921Smarcel	off = hdr->hdr_lba_alt * disk->sector_size;
213121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
214121921Smarcel	    read(fd, buffer, disk->sector_size) != disk->sector_size) {
215121921Smarcel		error = (errno) ? errno : EAGAIN;
216121921Smarcel		goto bail;
217121921Smarcel	}
218121921Smarcel	memcpy(&hdr[1], buffer, sizeof(struct gpt_hdr));
219121921Smarcel
220121921Smarcelbail:
221121921Smarcel	free(buffer);
222121921Smarcel	return (error);
223121921Smarcel}
224121921Smarcel
225121921Smarcelstatic int
226121921Smarcelupdate_gpt(int fd, const struct disk *disk, struct gpt_hdr *hdr,
227121921Smarcel    struct gpt_ent *tbl)
228121921Smarcel{
229122025Smarcel	struct gpt_ent *save;
230121921Smarcel	char *buffer;
231121921Smarcel	struct chunk *c;
232121921Smarcel	off_t off;
233121921Smarcel	size_t bufsz;
234122025Smarcel	int error, idx, sav;
235121921Smarcel
236125124Smarcel	error = 0;
237125124Smarcel
238122025Smarcel	/*
239122025Smarcel	 * Save the entries of those chunks that have an index. They are
240122025Smarcel	 * the ones that exist on disk already.
241122025Smarcel	 */
242122025Smarcel	sav = 0;
243122025Smarcel	for (c = disk->chunks->part; c != NULL; c = c->next) {
244122025Smarcel		if ((c->flags & CHUNK_HAS_INDEX))
245122025Smarcel			sav++;
246122025Smarcel	}
247122025Smarcel	if (sav > 0) {
248122025Smarcel		save = malloc(sav * sizeof(struct gpt_ent));
249122025Smarcel		if (save == NULL)
250122025Smarcel			abort();
251122025Smarcel		sav = 0;
252122025Smarcel		for (c = disk->chunks->part; c != NULL; c = c->next) {
253122025Smarcel			if ((c->flags & CHUNK_HAS_INDEX)) {
254122025Smarcel				idx = CHUNK_FTOI(c->flags);
255122025Smarcel				save[sav] = tbl[idx];
256122025Smarcel				c->flags ^= CHUNK_ITOF(idx);
257122025Smarcel				c->flags |= CHUNK_ITOF(sav);
258122025Smarcel				sav++;
259122025Smarcel			}
260122025Smarcel		}
261122025Smarcel	} else
262122025Smarcel		save = NULL;
263122025Smarcel
264122025Smarcel	/*
265122025Smarcel	 * Clear the table entries.
266122025Smarcel	 */
267122025Smarcel	for (idx = 0; idx < disk->gpt_size; idx++) {
268122025Smarcel		uuid_create_nil(&tbl[idx].ent_type, NULL);
269122025Smarcel		tbl[idx].ent_lba_start = 0;
270122025Smarcel		tbl[idx].ent_lba_end = 0;
271122025Smarcel		tbl[idx].ent_attr = 0;
272122025Smarcel		bzero(tbl[idx].ent_name, sizeof(tbl[idx].ent_name));
273122025Smarcel	}
274122025Smarcel
275122025Smarcel	/*
276122025Smarcel	 * Repopulate the table from the chunks, possibly using saved
277122025Smarcel	 * information.
278122025Smarcel	 */
279121921Smarcel	idx = 0;
280121921Smarcel	for (c = disk->chunks->part; c != NULL; c = c->next) {
281121921Smarcel		if (!(c->flags & CHUNK_HAS_INDEX)) {
282121921Smarcel			switch (c->type) {
283121921Smarcel			case freebsd:
284121921Smarcel				tbl[idx].ent_type = _fbsd;
285121921Smarcel				break;
286121921Smarcel			case efi:
287121921Smarcel				tbl[idx].ent_type = _efi;
288121921Smarcel				break;
289121921Smarcel			case part:
290121921Smarcel				switch (c->subtype) {
291121921Smarcel				case FS_SWAP:
292121921Smarcel					tbl[idx].ent_type = _swap;
293121921Smarcel					break;
294121921Smarcel				case FS_BSDFFS:
295121921Smarcel					tbl[idx].ent_type = _ufs;
296121921Smarcel					break;
297121921Smarcel				default:
298121921Smarcel					return (EINVAL);
299121921Smarcel				}
300121921Smarcel				break;
301121921Smarcel			default:
302121921Smarcel				return (EINVAL);
303121921Smarcel			}
304122025Smarcel		} else {
305122025Smarcel			sav = CHUNK_FTOI(c->flags);
306122025Smarcel			tbl[idx].ent_type = save[sav].ent_type;
307122025Smarcel			memcpy(tbl[idx].ent_name, save[sav].ent_name,
308122025Smarcel			    sizeof(tbl[idx].ent_name));
309122025Smarcel		}
310121931Smarcel		tbl[idx].ent_lba_start = c->offset;
311121931Smarcel		tbl[idx].ent_lba_end = c->end;
312122025Smarcel
313122025Smarcel		idx++;
314122025Smarcel		if (idx == disk->gpt_size)
315122025Smarcel			return (ENOSPC);
316121921Smarcel 	}
317122025Smarcel	if (save != NULL)
318122025Smarcel		free(save);
319121921Smarcel
320121921Smarcel	hdr[0].hdr_crc_table = crc32(tbl,
321121921Smarcel	    disk->gpt_size * sizeof(struct gpt_ent));
322121921Smarcel	hdr[0].hdr_crc_self = 0;
323121921Smarcel	hdr[0].hdr_crc_self = crc32(&hdr[0], hdr[0].hdr_size);
324121921Smarcel
325121921Smarcel	hdr[1].hdr_crc_table = hdr[0].hdr_crc_table;
326121921Smarcel	hdr[1].hdr_crc_self = 0;
327121921Smarcel	hdr[1].hdr_crc_self = crc32(&hdr[1], hdr[1].hdr_size);
328121921Smarcel
329121921Smarcel	/*
330121921Smarcel	 * Write the new GPT back to the disk.
331121921Smarcel	 */
332121921Smarcel	bufsz = disk->gpt_size * sizeof(struct gpt_ent);
333121921Smarcel	if (bufsz == 0 || bufsz % disk->sector_size)
334121921Smarcel		bufsz += disk->sector_size;
335121921Smarcel	bufsz = (bufsz / disk->sector_size) * disk->sector_size;
336121921Smarcel	buffer = calloc(1, bufsz);
337121921Smarcel
338121921Smarcel	memcpy(buffer, &hdr[0], sizeof(struct gpt_hdr));
339121921Smarcel	off = hdr[0].hdr_lba_self * disk->sector_size;
340121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
341121921Smarcel	    write(fd, buffer, disk->sector_size) != disk->sector_size) {
342121921Smarcel		error = (errno) ? errno : EAGAIN;
343121921Smarcel		goto bail;
344121921Smarcel	}
345121921Smarcel	memcpy(buffer, &hdr[1], sizeof(struct gpt_hdr));
346121921Smarcel	off = hdr[1].hdr_lba_self * disk->sector_size;
347121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
348121921Smarcel	    write(fd, buffer, disk->sector_size) != disk->sector_size) {
349121921Smarcel		error = (errno) ? errno : EAGAIN;
350121921Smarcel		goto bail;
351121921Smarcel	}
352121921Smarcel	memcpy(buffer, tbl, disk->gpt_size * sizeof(struct gpt_ent));
353121921Smarcel	off = hdr[0].hdr_lba_table * disk->sector_size;
354121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
355121921Smarcel	    write(fd, buffer, bufsz) != bufsz) {
356121921Smarcel		error = (errno) ? errno : EAGAIN;
357121921Smarcel		goto bail;
358121921Smarcel	}
359121921Smarcel	off = hdr[1].hdr_lba_table * disk->sector_size;
360121921Smarcel	if (lseek(fd, off, SEEK_SET) != off ||
361121921Smarcel	    write(fd, buffer, bufsz) != bufsz) {
362121921Smarcel		error = (errno) ? errno : EAGAIN;
363121921Smarcel		goto bail;
364121921Smarcel	}
365121921Smarcel
366121921Smarcelbail:
367121921Smarcel	free(buffer);
368121921Smarcel	return (error);
369121921Smarcel}
370121921Smarcel
371105816Sphkint
372121921SmarcelWrite_Disk(const struct disk *disk)
373105816Sphk{
374121921Smarcel	char devname[64];
375121921Smarcel	struct gpt_hdr *hdr;
376121921Smarcel	struct gpt_ent *tbl;
377121921Smarcel	int error, fd;
378121921Smarcel
379121921Smarcel	hdr = malloc(sizeof(struct gpt_hdr) * 2);
380121921Smarcel	if (hdr == NULL)
381121921Smarcel		return (ENOMEM);
382121921Smarcel	tbl = malloc(sizeof(struct gpt_ent) * disk->gpt_size);
383121921Smarcel	if (tbl == NULL) {
384121921Smarcel		free(hdr);
385121921Smarcel		return (ENOMEM);
386121921Smarcel	}
387121921Smarcel
388121921Smarcel	snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, disk->name);
389121921Smarcel	fd = open(devname, O_RDWR);
390121921Smarcel	if (fd == -1) {
391121921Smarcel		free(tbl);
392121921Smarcel		free(hdr);
393121921Smarcel		return (errno);
394121921Smarcel	}
395121921Smarcel
396121921Smarcel	/*
397121921Smarcel	 * We can always write the PMBR, because we reject disks that do not
398121921Smarcel	 * have a PMBR and are not virgin.
399121921Smarcel	 */
400121921Smarcel	error = write_pmbr(fd, disk);
401121921Smarcel	if (error)
402121921Smarcel		goto bail;
403121921Smarcel
404121921Smarcel	/*
405121921Smarcel	 * Read the existing GPT from disk or otherwise create one out of
406121921Smarcel	 * thin air. This way we can preserve the UUIDs and the entry names
407121921Smarcel	 * when updating it.
408121921Smarcel	 */
409121921Smarcel	error = read_gpt(fd, disk, hdr, tbl);
410121921Smarcel	if (error)
411121921Smarcel		goto bail;
412121921Smarcel
413121921Smarcel	/*
414121921Smarcel	 * Update and write the in-memory copy of the GPT.
415121921Smarcel	 */
416121921Smarcel	error = update_gpt(fd, disk, hdr, tbl);
417121921Smarcel
418121921Smarcelbail:
419121921Smarcel	close(fd);
420121921Smarcel	free(tbl);
421121921Smarcel	free(hdr);
422121921Smarcel	return (error);
423105816Sphk}
424