write_ia64_disk.c revision 121921
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: head/lib/libdisk/write_ia64_disk.c 121921 2003-11-03 03:18:34Z marcel $"); 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 101121921Smarceluint32_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 124121921Smarcel nsects = disk->media_size / disk->sector_size; 125121921Smarcel buffer = calloc(disk->sector_size, 1); 126121921Smarcel if (buffer == NULL) 127121921Smarcel return (ENOMEM); 128121921Smarcel buffer[DOSMAGICOFFSET] = DOSMAGIC & 0xff; 129121921Smarcel buffer[DOSMAGICOFFSET + 1] = DOSMAGIC >> 8; 130121921Smarcel 131121921Smarcel dp.dp_flag = 0; 132121921Smarcel dp.dp_shd = dp.dp_ssect = dp.dp_scyl = 0xff; 133121921Smarcel dp.dp_typ = DOSPTYP_PMBR; 134121921Smarcel dp.dp_ehd = dp.dp_esect = dp.dp_ecyl = 0xff; 135121921Smarcel dp.dp_start = 1; 136121921Smarcel dp.dp_size = (nsects > 0xffffffffu) ? ~0u : nsects; 137121921Smarcel memcpy(buffer + DOSPARTOFF, &dp, DOSPARTSIZE); 138121921Smarcel 139121921Smarcel if (lseek(fd, 0L, SEEK_SET) != 0L || 140121921Smarcel write(fd, buffer, disk->sector_size) != disk->sector_size) 141121921Smarcel error = (errno) ? errno : EAGAIN; 142121921Smarcel 143121921Smarcel free(buffer); 144121921Smarcel return (error); 145121921Smarcel} 146121921Smarcel 147121921Smarcelstatic int 148121921Smarcelread_gpt(int fd, const struct disk *disk, struct gpt_hdr *hdr, 149121921Smarcel struct gpt_ent *tbl) 150121921Smarcel{ 151121921Smarcel char *buffer; 152121921Smarcel off_t off; 153121921Smarcel size_t nsects, sz; 154121921Smarcel int error, i; 155121921Smarcel 156121921Smarcel nsects = disk->gpt_size * sizeof(struct gpt_ent) / disk->sector_size; 157121921Smarcel nsects++; 158121921Smarcel sz = nsects * disk->sector_size; 159121921Smarcel buffer = malloc(sz); 160121921Smarcel if (buffer == NULL) 161121921Smarcel return (ENOMEM); 162121921Smarcel 163121921Smarcel if (lseek(fd, disk->sector_size, SEEK_SET) != disk->sector_size || 164121921Smarcel read(fd, buffer, disk->sector_size) != disk->sector_size) { 165121921Smarcel error = (errno) ? errno : EAGAIN; 166121921Smarcel goto bail; 167121921Smarcel } 168121921Smarcel if (memcmp(buffer, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { 169121921Smarcel /* 170121921Smarcel * No GPT on disk. Create one out of thin air. 171121921Smarcel */ 172121921Smarcel bzero(&hdr[0], sizeof(struct gpt_hdr)); 173121921Smarcel memcpy(hdr[0].hdr_sig, GPT_HDR_SIG, sizeof(hdr[0].hdr_sig)); 174121921Smarcel hdr[0].hdr_revision = GPT_HDR_REVISION; 175121921Smarcel hdr[0].hdr_size = offsetof(struct gpt_hdr, padding); 176121921Smarcel hdr[0].hdr_lba_self = 1; 177121921Smarcel hdr[0].hdr_lba_alt = disk->media_size / disk->sector_size - 1L; 178121921Smarcel hdr[0].hdr_lba_start = disk->lba_start; 179121921Smarcel hdr[0].hdr_lba_end = disk->lba_end; 180121921Smarcel uuid_create(&hdr[0].hdr_uuid, NULL); 181121921Smarcel hdr[0].hdr_lba_table = 2; 182121921Smarcel hdr[0].hdr_entries = disk->gpt_size; 183121921Smarcel hdr[0].hdr_entsz = sizeof(struct gpt_ent); 184121921Smarcel hdr[1] = hdr[0]; 185121921Smarcel hdr[1].hdr_lba_self = hdr[0].hdr_lba_alt; 186121921Smarcel hdr[1].hdr_lba_alt = hdr[0].hdr_lba_self; 187121921Smarcel hdr[1].hdr_lba_table = disk->lba_end + 1; 188121921Smarcel 189121921Smarcel for (i = 0; i < disk->gpt_size; i++) { 190121921Smarcel bzero(&tbl[i], sizeof(struct gpt_ent)); 191121921Smarcel uuid_create(&tbl[i].ent_uuid, NULL); 192121921Smarcel } 193121921Smarcel 194121921Smarcel error = 0; 195121921Smarcel goto bail; 196121921Smarcel } 197121921Smarcel 198121921Smarcel /* 199121921Smarcel * We have a GPT on disk. Read it. 200121921Smarcel */ 201121921Smarcel memcpy(&hdr[0], buffer, sizeof(struct gpt_hdr)); 202121921Smarcel off = hdr->hdr_lba_table * disk->sector_size; 203121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 204121921Smarcel read(fd, buffer, sz) != sz) { 205121921Smarcel error = (errno) ? errno : EAGAIN; 206121921Smarcel goto bail; 207121921Smarcel } 208121921Smarcel memcpy(tbl, buffer, sizeof(struct gpt_ent) * disk->gpt_size); 209121921Smarcel off = hdr->hdr_lba_alt * disk->sector_size; 210121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 211121921Smarcel read(fd, buffer, disk->sector_size) != disk->sector_size) { 212121921Smarcel error = (errno) ? errno : EAGAIN; 213121921Smarcel goto bail; 214121921Smarcel } 215121921Smarcel memcpy(&hdr[1], buffer, sizeof(struct gpt_hdr)); 216121921Smarcel error = 0; 217121921Smarcel 218121921Smarcelbail: 219121921Smarcel free(buffer); 220121921Smarcel return (error); 221121921Smarcel} 222121921Smarcel 223121921Smarcelstatic int 224121921Smarcelupdate_gpt(int fd, const struct disk *disk, struct gpt_hdr *hdr, 225121921Smarcel struct gpt_ent *tbl) 226121921Smarcel{ 227121921Smarcel char *buffer; 228121921Smarcel struct chunk *c; 229121921Smarcel off_t off; 230121921Smarcel size_t bufsz; 231121921Smarcel int error, idx; 232121921Smarcel 233121921Smarcel idx = 0; 234121921Smarcel for (c = disk->chunks->part; c != NULL; c = c->next) { 235121921Smarcel if (!(c->flags & CHUNK_HAS_INDEX)) { 236121921Smarcel while (idx < disk->gpt_size && 237121921Smarcel !uuid_is_nil(&tbl[idx].ent_type, NULL)) 238121921Smarcel idx++; 239121921Smarcel if (idx == disk->gpt_size) 240121921Smarcel return (ENOSPC); 241121921Smarcel 242121921Smarcel switch (c->type) { 243121921Smarcel case freebsd: 244121921Smarcel tbl[idx].ent_type = _fbsd; 245121921Smarcel break; 246121921Smarcel case efi: 247121921Smarcel tbl[idx].ent_type = _efi; 248121921Smarcel break; 249121921Smarcel case part: 250121921Smarcel switch (c->subtype) { 251121921Smarcel case FS_SWAP: 252121921Smarcel tbl[idx].ent_type = _swap; 253121921Smarcel break; 254121921Smarcel case FS_BSDFFS: 255121921Smarcel tbl[idx].ent_type = _ufs; 256121921Smarcel break; 257121921Smarcel default: 258121921Smarcel return (EINVAL); 259121921Smarcel } 260121921Smarcel break; 261121921Smarcel default: 262121921Smarcel return (EINVAL); 263121921Smarcel } 264121921Smarcel } else 265121921Smarcel idx = CHUNK_FTOI(c->flags); 266121921Smarcel 267121921Smarcel tbl[idx].ent_lba_start = c->offset / disk->sector_size; 268121921Smarcel tbl[idx].ent_lba_end = c->end / disk->sector_size; 269121921Smarcel } 270121921Smarcel 271121921Smarcel hdr[0].hdr_crc_table = crc32(tbl, 272121921Smarcel disk->gpt_size * sizeof(struct gpt_ent)); 273121921Smarcel hdr[0].hdr_crc_self = 0; 274121921Smarcel hdr[0].hdr_crc_self = crc32(&hdr[0], hdr[0].hdr_size); 275121921Smarcel 276121921Smarcel hdr[1].hdr_crc_table = hdr[0].hdr_crc_table; 277121921Smarcel hdr[1].hdr_crc_self = 0; 278121921Smarcel hdr[1].hdr_crc_self = crc32(&hdr[1], hdr[1].hdr_size); 279121921Smarcel 280121921Smarcel /* 281121921Smarcel * Write the new GPT back to the disk. 282121921Smarcel */ 283121921Smarcel bufsz = disk->gpt_size * sizeof(struct gpt_ent); 284121921Smarcel if (bufsz == 0 || bufsz % disk->sector_size) 285121921Smarcel bufsz += disk->sector_size; 286121921Smarcel bufsz = (bufsz / disk->sector_size) * disk->sector_size; 287121921Smarcel buffer = calloc(1, bufsz); 288121921Smarcel 289121921Smarcel memcpy(buffer, &hdr[0], sizeof(struct gpt_hdr)); 290121921Smarcel off = hdr[0].hdr_lba_self * disk->sector_size; 291121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 292121921Smarcel write(fd, buffer, disk->sector_size) != disk->sector_size) { 293121921Smarcel error = (errno) ? errno : EAGAIN; 294121921Smarcel goto bail; 295121921Smarcel } 296121921Smarcel memcpy(buffer, &hdr[1], sizeof(struct gpt_hdr)); 297121921Smarcel off = hdr[1].hdr_lba_self * disk->sector_size; 298121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 299121921Smarcel write(fd, buffer, disk->sector_size) != disk->sector_size) { 300121921Smarcel error = (errno) ? errno : EAGAIN; 301121921Smarcel goto bail; 302121921Smarcel } 303121921Smarcel memcpy(buffer, tbl, disk->gpt_size * sizeof(struct gpt_ent)); 304121921Smarcel off = hdr[0].hdr_lba_table * disk->sector_size; 305121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 306121921Smarcel write(fd, buffer, bufsz) != bufsz) { 307121921Smarcel error = (errno) ? errno : EAGAIN; 308121921Smarcel goto bail; 309121921Smarcel } 310121921Smarcel off = hdr[1].hdr_lba_table * disk->sector_size; 311121921Smarcel if (lseek(fd, off, SEEK_SET) != off || 312121921Smarcel write(fd, buffer, bufsz) != bufsz) { 313121921Smarcel error = (errno) ? errno : EAGAIN; 314121921Smarcel goto bail; 315121921Smarcel } 316121921Smarcel error = 0; 317121921Smarcel 318121921Smarcelbail: 319121921Smarcel free(buffer); 320121921Smarcel return (error); 321121921Smarcel} 322121921Smarcel 323105816Sphkint 324121921SmarcelWrite_Disk(const struct disk *disk) 325105816Sphk{ 326121921Smarcel char devname[64]; 327121921Smarcel struct gpt_hdr *hdr; 328121921Smarcel struct gpt_ent *tbl; 329121921Smarcel int error, fd; 330121921Smarcel 331121921Smarcel hdr = malloc(sizeof(struct gpt_hdr) * 2); 332121921Smarcel if (hdr == NULL) 333121921Smarcel return (ENOMEM); 334121921Smarcel tbl = malloc(sizeof(struct gpt_ent) * disk->gpt_size); 335121921Smarcel if (tbl == NULL) { 336121921Smarcel free(hdr); 337121921Smarcel return (ENOMEM); 338121921Smarcel } 339121921Smarcel 340121921Smarcel snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, disk->name); 341121921Smarcel fd = open(devname, O_RDWR); 342121921Smarcel if (fd == -1) { 343121921Smarcel free(tbl); 344121921Smarcel free(hdr); 345121921Smarcel return (errno); 346121921Smarcel } 347121921Smarcel 348121921Smarcel /* 349121921Smarcel * We can always write the PMBR, because we reject disks that do not 350121921Smarcel * have a PMBR and are not virgin. 351121921Smarcel */ 352121921Smarcel error = write_pmbr(fd, disk); 353121921Smarcel if (error) 354121921Smarcel goto bail; 355121921Smarcel 356121921Smarcel /* 357121921Smarcel * Read the existing GPT from disk or otherwise create one out of 358121921Smarcel * thin air. This way we can preserve the UUIDs and the entry names 359121921Smarcel * when updating it. 360121921Smarcel */ 361121921Smarcel error = read_gpt(fd, disk, hdr, tbl); 362121921Smarcel if (error) 363121921Smarcel goto bail; 364121921Smarcel 365121921Smarcel /* 366121921Smarcel * Update and write the in-memory copy of the GPT. 367121921Smarcel */ 368121921Smarcel error = update_gpt(fd, disk, hdr, tbl); 369121921Smarcel if (error) 370121921Smarcel goto bail; 371121921Smarcel 372106741Smarcel return (0); 373121921Smarcel 374121921Smarcelbail: 375121921Smarcel close(fd); 376121921Smarcel free(tbl); 377121921Smarcel free(hdr); 378121921Smarcel return (error); 379105816Sphk} 380