sunlabel.c revision 141956
1110130Sjake/*- 2110130Sjake * Copyright (c) 2003 Jake Burkholder. 3129965Sjoerg * Copyright (c) 2004 Joerg Wunsch. 4110130Sjake * All rights reserved. 5110130Sjake * 6110130Sjake * Redistribution and use in source and binary forms, with or without 7110130Sjake * modification, are permitted provided that the following conditions 8110130Sjake * are met: 9110130Sjake * 1. Redistributions of source code must retain the above copyright 10110130Sjake * notice, this list of conditions and the following disclaimer. 11110130Sjake * 2. Redistributions in binary form must reproduce the above copyright 12110130Sjake * notice, this list of conditions and the following disclaimer in the 13110130Sjake * documentation and/or other materials provided with the distribution. 14110130Sjake * 15110130Sjake * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16110130Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17110130Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18110130Sjake * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19110130Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20110130Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21110130Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22110130Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23110130Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24110130Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25110130Sjake * SUCH DAMAGE. 26110130Sjake */ 27110130Sjake/* 28110130Sjake * Copyright (c) 1994, 1995 Gordon W. Ross 29110130Sjake * Copyright (c) 1994 Theo de Raadt 30110130Sjake * All rights reserved. 31110130Sjake * Copyright (c) 1987, 1993 32110130Sjake * The Regents of the University of California. All rights reserved. 33110130Sjake * 34110130Sjake * This code is derived from software contributed to Berkeley by 35110130Sjake * Symmetric Computer Systems. 36110130Sjake * 37110130Sjake * Redistribution and use in source and binary forms, with or without 38110130Sjake * modification, are permitted provided that the following conditions 39110130Sjake * are met: 40110130Sjake * 1. Redistributions of source code must retain the above copyright 41110130Sjake * notice, this list of conditions and the following disclaimer. 42110130Sjake * 2. Redistributions in binary form must reproduce the above copyright 43110130Sjake * notice, this list of conditions and the following disclaimer in the 44110130Sjake * documentation and/or other materials provided with the distribution. 45110130Sjake * 3. All advertising materials mentioning features or use of this software 46110130Sjake * must display the following acknowledgement: 47110130Sjake * This product includes software developed by the University of 48110130Sjake * California, Berkeley and its contributors. 49110130Sjake * This product includes software developed by Theo de Raadt. 50110130Sjake * 4. Neither the name of the University nor the names of its contributors 51110130Sjake * may be used to endorse or promote products derived from this software 52110130Sjake * without specific prior written permission. 53110130Sjake * 54110130Sjake * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55110130Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56110130Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57110130Sjake * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58110130Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59110130Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60110130Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61110130Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62110130Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63110130Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64110130Sjake * SUCH DAMAGE. 65110130Sjake * 66110130Sjake * from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $ 67110130Sjake */ 68110130Sjake 69110130Sjake#include <sys/cdefs.h> 70110130Sjake__FBSDID("$FreeBSD: head/sbin/sunlabel/sunlabel.c 141956 2005-02-15 22:31:05Z obrien $"); 71110130Sjake 72110130Sjake#include <sys/types.h> 73110130Sjake#include <sys/param.h> 74113538Sjake#include <sys/disk.h> 75113538Sjake#include <sys/ioctl.h> 76110130Sjake#include <sys/sun_disklabel.h> 77110130Sjake#include <sys/wait.h> 78110130Sjake 79128916Sjoerg#include <ctype.h> 80110130Sjake#include <err.h> 81110130Sjake#include <fcntl.h> 82110130Sjake#include <inttypes.h> 83113896Sphk#include <libgeom.h> 84110130Sjake#include <paths.h> 85110130Sjake#include <stdio.h> 86110130Sjake#include <stdlib.h> 87110130Sjake#include <string.h> 88110130Sjake#include <unistd.h> 89110130Sjake 90110130Sjake#define _PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX" 91110130Sjake#define _PATH_BOOT "/boot/boot1" 92110130Sjake 93110130Sjakestatic int bflag; 94110130Sjakestatic int Bflag; 95129965Sjoergstatic int cflag; 96110130Sjakestatic int eflag; 97129965Sjoergstatic int hflag; 98110130Sjakestatic int nflag; 99110130Sjakestatic int Rflag; 100110130Sjakestatic int wflag; 101110130Sjake 102129965Sjoergstatic off_t mediasize; 103129965Sjoergstatic uint32_t sectorsize; 104129965Sjoerg 105129965Sjoergstruct tags { 106129965Sjoerg const char *name; 107129965Sjoerg unsigned int id; 108129965Sjoerg}; 109129965Sjoerg 110110130Sjakestatic int check_label(struct sun_disklabel *sl); 111110130Sjakestatic void read_label(struct sun_disklabel *sl, const char *disk); 112110130Sjakestatic void write_label(struct sun_disklabel *sl, const char *disk, 113110130Sjake const char *bootpath); 114129965Sjoergstatic void edit_label(struct sun_disklabel *sl, const char *disk, 115110130Sjake const char *bootpath); 116110130Sjakestatic int parse_label(struct sun_disklabel *sl, const char *file); 117110130Sjakestatic void print_label(struct sun_disklabel *sl, const char *disk, FILE *out); 118110130Sjake 119110130Sjakestatic int parse_size(struct sun_disklabel *sl, int part, char *size); 120110130Sjakestatic int parse_offset(struct sun_disklabel *sl, int part, char *offset); 121110130Sjake 122129965Sjoergstatic const char *flagname(unsigned int tag); 123129965Sjoergstatic const char *tagname(unsigned int tag); 124129965Sjoergstatic unsigned int parse_flag(struct sun_disklabel *sl, int part, 125129965Sjoerg const char *flag); 126129965Sjoergstatic unsigned int parse_tag(struct sun_disklabel *sl, int part, 127129965Sjoerg const char *tag); 128129965Sjoergstatic const char *make_h_number(uintmax_t u); 129129965Sjoerg 130110130Sjakestatic void usage(void); 131110130Sjake 132110130Sjakeextern char *__progname; 133110130Sjake 134129965Sjoergstatic struct tags knowntags[] = { 135129965Sjoerg { "unassigned", VTOC_UNASSIGNED }, 136129965Sjoerg { "boot", VTOC_BOOT }, 137129965Sjoerg { "root", VTOC_ROOT }, 138129965Sjoerg { "swap", VTOC_SWAP }, 139129965Sjoerg { "usr", VTOC_USR }, 140129965Sjoerg { "backup", VTOC_BACKUP }, 141129965Sjoerg { "stand", VTOC_STAND }, 142129965Sjoerg { "var", VTOC_VAR }, 143129965Sjoerg { "home", VTOC_HOME }, 144129965Sjoerg { "altsctr", VTOC_ALTSCTR }, 145129965Sjoerg { "cache", VTOC_CACHE }, 146129965Sjoerg { "VxVM_pub", VTOC_VXVM_PUB }, 147129965Sjoerg { "VxVM_priv", VTOC_VXVM_PRIV }, 148129965Sjoerg}; 149129965Sjoerg 150129965Sjoergstatic struct tags knownflags[] = { 151129965Sjoerg { "wm", 0 }, 152129965Sjoerg { "wu", VTOC_UNMNT }, 153129965Sjoerg { "rm", VTOC_RONLY }, 154129965Sjoerg { "ru", VTOC_UNMNT | VTOC_RONLY }, 155129965Sjoerg}; 156129965Sjoerg 157110130Sjake/* 158110130Sjake * Disk label editor for sun disklabels. 159110130Sjake */ 160110130Sjakeint 161110130Sjakemain(int ac, char **av) 162110130Sjake{ 163110130Sjake struct sun_disklabel sl; 164110130Sjake const char *bootpath; 165110130Sjake const char *proto; 166110130Sjake const char *disk; 167110130Sjake int ch; 168110130Sjake 169110130Sjake bootpath = _PATH_BOOT; 170129965Sjoerg while ((ch = getopt(ac, av, "b:BcehnrRw")) != -1) 171110130Sjake switch (ch) { 172110130Sjake case 'b': 173110130Sjake bflag = 1; 174110130Sjake bootpath = optarg; 175110130Sjake break; 176110130Sjake case 'B': 177110130Sjake Bflag = 1; 178110130Sjake break; 179129965Sjoerg case 'c': 180129965Sjoerg cflag = 1; 181129965Sjoerg break; 182110130Sjake case 'e': 183110130Sjake eflag = 1; 184110130Sjake break; 185129965Sjoerg case 'h': 186129965Sjoerg hflag = 1; 187129965Sjoerg break; 188110130Sjake case 'n': 189110130Sjake nflag = 1; 190110130Sjake break; 191110130Sjake case 'r': 192113896Sphk fprintf(stderr, "Obsolete -r flag ignored\n"); 193110130Sjake break; 194110130Sjake case 'R': 195110130Sjake Rflag = 1; 196110130Sjake break; 197110130Sjake case 'w': 198110130Sjake wflag = 1; 199110130Sjake break; 200110130Sjake default: 201110130Sjake usage(); 202110130Sjake break; 203110130Sjake } 204110130Sjake if (bflag && !Bflag) 205110130Sjake usage(); 206110130Sjake if (nflag && !(Bflag || eflag || Rflag || wflag)) 207110130Sjake usage(); 208110130Sjake if (eflag && (Rflag || wflag)) 209110130Sjake usage(); 210129965Sjoerg if (eflag) 211129965Sjoerg hflag = 0; 212110130Sjake ac -= optind; 213110130Sjake av += optind; 214110130Sjake if (ac == 0) 215110130Sjake usage(); 216110130Sjake bzero(&sl, sizeof(sl)); 217110130Sjake disk = av[0]; 218110130Sjake if (wflag) { 219110130Sjake if (ac != 2 || strcmp(av[1], "auto") != 0) 220110130Sjake usage(); 221110130Sjake read_label(&sl, disk); 222110130Sjake bzero(sl.sl_part, sizeof(sl.sl_part)); 223113824Sphk sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 224113824Sphk sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders * 225110130Sjake sl.sl_ntracks * sl.sl_nsectors; 226110130Sjake write_label(&sl, disk, bootpath); 227110130Sjake } else if (eflag) { 228110130Sjake if (ac != 1) 229110130Sjake usage(); 230110130Sjake read_label(&sl, disk); 231110130Sjake if (sl.sl_magic != SUN_DKMAGIC) 232110130Sjake errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 233129965Sjoerg edit_label(&sl, disk, bootpath); 234110130Sjake } else if (Rflag) { 235110130Sjake if (ac != 2) 236110130Sjake usage(); 237110130Sjake proto = av[1]; 238110130Sjake read_label(&sl, disk); 239113538Sjake if (parse_label(&sl, proto) != 0) 240113538Sjake errx(1, "%s: invalid label", proto); 241110130Sjake write_label(&sl, disk, bootpath); 242110130Sjake } else if (Bflag) { 243110130Sjake read_label(&sl, disk); 244110130Sjake if (sl.sl_magic != SUN_DKMAGIC) 245110130Sjake errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 246110130Sjake write_label(&sl, disk, bootpath); 247110130Sjake } else { 248110130Sjake read_label(&sl, disk); 249110130Sjake if (sl.sl_magic != SUN_DKMAGIC) 250110130Sjake errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 251110130Sjake print_label(&sl, disk, stdout); 252110130Sjake } 253110130Sjake return (0); 254110130Sjake} 255110130Sjake 256110130Sjakestatic int 257110130Sjakecheck_label(struct sun_disklabel *sl) 258110130Sjake{ 259110130Sjake uint64_t nsectors; 260110130Sjake uint64_t ostart; 261110130Sjake uint64_t start; 262110130Sjake uint64_t oend; 263110130Sjake uint64_t end; 264129965Sjoerg int havevtoc; 265129965Sjoerg int warnonly; 266110130Sjake int i; 267110130Sjake int j; 268110130Sjake 269129965Sjoerg havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE; 270129965Sjoerg 271110130Sjake nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors; 272113824Sphk if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 || 273113824Sphk sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) { 274113538Sjake warnx("partition c is incorrect, must start at 0 and cover " 275113538Sjake "whole disk"); 276113538Sjake return (1); 277113538Sjake } 278129965Sjoerg if (havevtoc && sl->sl_vtoc_map[2].svtoc_tag != VTOC_BACKUP) { 279129965Sjoerg warnx("partition c must have tag \"backup\""); 280129965Sjoerg return (1); 281129965Sjoerg } 282113824Sphk for (i = 0; i < SUN_NPART; i++) { 283110130Sjake if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0) 284110130Sjake continue; 285110130Sjake start = (uint64_t)sl->sl_part[i].sdkp_cyloffset * 286110130Sjake sl->sl_ntracks * sl->sl_nsectors; 287110130Sjake end = start + sl->sl_part[i].sdkp_nsectors; 288110130Sjake if (end > nsectors) { 289110130Sjake warnx("partition %c extends past end of disk", 290110130Sjake 'a' + i); 291110130Sjake return (1); 292110130Sjake } 293129965Sjoerg if (havevtoc) { 294129965Sjoerg if (sl->sl_vtoc_map[i].svtoc_tag == VTOC_BACKUP) { 295129965Sjoerg warnx("only partition c is allowed to have " 296130692Sjoerg "tag \"backup\""); 297129965Sjoerg return (1); 298129965Sjoerg } 299129965Sjoerg } 300113824Sphk for (j = 0; j < SUN_NPART; j++) { 301129965Sjoerg /* 302129965Sjoerg * Overlaps for unmountable partitions are 303129965Sjoerg * non-fatal but will be warned anyway. 304129965Sjoerg */ 305129965Sjoerg warnonly = havevtoc && 306129965Sjoerg ((sl->sl_vtoc_map[i].svtoc_flag & VTOC_UNMNT) != 0 || 307129965Sjoerg (sl->sl_vtoc_map[j].svtoc_flag & VTOC_UNMNT) != 0); 308129965Sjoerg 309110130Sjake if (j == 2 || j == i || 310110130Sjake sl->sl_part[j].sdkp_nsectors == 0) 311110130Sjake continue; 312110130Sjake ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset * 313110130Sjake sl->sl_ntracks * sl->sl_nsectors; 314110130Sjake oend = ostart + sl->sl_part[j].sdkp_nsectors; 315110130Sjake if ((start <= ostart && end >= oend) || 316110130Sjake (start > ostart && start < oend) || 317110130Sjake (end > ostart && end < oend)) { 318110130Sjake warnx("partition %c overlaps partition %c", 319110130Sjake 'a' + i, 'a' + j); 320129965Sjoerg if (!warnonly) 321129965Sjoerg return (1); 322110130Sjake } 323110130Sjake } 324110130Sjake } 325110130Sjake return (0); 326110130Sjake} 327110130Sjake 328110130Sjakestatic void 329110130Sjakeread_label(struct sun_disklabel *sl, const char *disk) 330110130Sjake{ 331113538Sjake char path[MAXPATHLEN]; 332113538Sjake uint32_t fwsectors; 333113538Sjake uint32_t fwheads; 334113824Sphk char buf[SUN_SIZE]; 335113824Sphk int fd, error; 336110130Sjake 337113538Sjake snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 338113538Sjake if ((fd = open(path, O_RDONLY)) < 0) 339113538Sjake err(1, "open %s", path); 340113824Sphk if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 341113538Sjake err(1, "read"); 342113824Sphk error = sunlabel_dec(buf, sl); 343129965Sjoerg if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0) 344129965Sjoerg if (error) 345129965Sjoerg err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk); 346129965Sjoerg if (ioctl(fd, DIOCGSECTORSIZE, §orsize) != 0) { 347129965Sjoerg if (error) 348129965Sjoerg err(1, "%s: DIOCGSECTORSIZE failed", disk); 349129965Sjoerg else 350129965Sjoerg sectorsize = 512; 351129965Sjoerg } 352113824Sphk if (error) { 353113538Sjake bzero(sl, sizeof(*sl)); 354113538Sjake if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0) 355113538Sjake fwsectors = 63; 356113538Sjake if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) { 357113538Sjake if (mediasize <= 63 * 1024 * sectorsize) 358113538Sjake fwheads = 1; 359113538Sjake else if (mediasize <= 63 * 16 * 1024 * sectorsize) 360113538Sjake fwheads = 16; 361113538Sjake else 362113538Sjake fwheads = 255; 363110130Sjake } 364113538Sjake sl->sl_rpm = 3600; 365113538Sjake sl->sl_pcylinders = mediasize / (fwsectors * fwheads * 366113538Sjake sectorsize); 367113538Sjake sl->sl_sparespercyl = 0; 368113538Sjake sl->sl_interleave = 1; 369113538Sjake sl->sl_ncylinders = sl->sl_pcylinders - 2; 370113538Sjake sl->sl_acylinders = 2; 371113538Sjake sl->sl_nsectors = fwsectors; 372113538Sjake sl->sl_ntracks = fwheads; 373113824Sphk sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 374113824Sphk sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders * 375113538Sjake sl->sl_ntracks * sl->sl_nsectors; 376113688Sphk if (mediasize > (off_t)4999L * 1024L * 1024L) { 377113538Sjake sprintf(sl->sl_text, 378113688Sphk "FreeBSD%jdG cyl %u alt %u hd %u sec %u", 379113688Sphk (intmax_t)(mediasize + 512 * 1024 * 1024) / 380113538Sjake (1024 * 1024 * 1024), 381113538Sjake sl->sl_ncylinders, sl->sl_acylinders, 382113538Sjake sl->sl_ntracks, sl->sl_nsectors); 383113538Sjake } else { 384113538Sjake sprintf(sl->sl_text, 385113688Sphk "FreeBSD%jdM cyl %u alt %u hd %u sec %u", 386113688Sphk (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024), 387113538Sjake sl->sl_ncylinders, sl->sl_acylinders, 388113538Sjake sl->sl_ntracks, sl->sl_nsectors); 389110130Sjake } 390110130Sjake } 391113538Sjake close(fd); 392110130Sjake} 393110130Sjake 394110130Sjakestatic void 395110130Sjakewrite_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 396110130Sjake{ 397110130Sjake char path[MAXPATHLEN]; 398113896Sphk char boot[SUN_BOOTSIZE]; 399113824Sphk char buf[SUN_SIZE]; 400113896Sphk const char *errstr; 401110130Sjake off_t off; 402110130Sjake int bfd; 403110130Sjake int fd; 404110130Sjake int i; 405113896Sphk struct gctl_req *grq; 406110130Sjake 407110130Sjake sl->sl_magic = SUN_DKMAGIC; 408110130Sjake 409110130Sjake if (check_label(sl) != 0) 410110130Sjake errx(1, "invalid label"); 411110130Sjake 412113896Sphk bzero(buf, sizeof(buf)); 413113896Sphk sunlabel_enc(buf, sl); 414113896Sphk 415110130Sjake if (nflag) { 416110130Sjake print_label(sl, disk, stdout); 417113896Sphk return; 418113896Sphk } 419113896Sphk if (Bflag) { 420113896Sphk if ((bfd = open(bootpath, O_RDONLY)) < 0) 421113896Sphk err(1, "open %s", bootpath); 422113896Sphk i = read(bfd, boot, sizeof(boot)); 423113896Sphk if (i < 0) 424113896Sphk err(1, "read"); 425113896Sphk else if (i != sizeof (boot)) 426113896Sphk errx(1, "read wrong size boot code (%d)", i); 427113896Sphk close(bfd); 428113896Sphk } 429113896Sphk snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 430113896Sphk fd = open(path, O_RDWR); 431113896Sphk if (fd < 0) { 432115624Sphk grq = gctl_get_handle(); 433115624Sphk gctl_ro_param(grq, "verb", -1, "write label"); 434113896Sphk gctl_ro_param(grq, "class", -1, "SUN"); 435113896Sphk gctl_ro_param(grq, "geom", -1, disk); 436113896Sphk gctl_ro_param(grq, "label", sizeof buf, buf); 437113896Sphk errstr = gctl_issue(grq); 438113896Sphk if (errstr != NULL) 439113896Sphk errx(1, "%s", errstr); 440113896Sphk gctl_free(grq); 441110130Sjake if (Bflag) { 442115624Sphk grq = gctl_get_handle(); 443115624Sphk gctl_ro_param(grq, "verb", -1, "write bootcode"); 444113896Sphk gctl_ro_param(grq, "class", -1, "SUN"); 445113896Sphk gctl_ro_param(grq, "geom", -1, disk); 446113896Sphk gctl_ro_param(grq, "bootcode", sizeof boot, boot); 447113896Sphk errstr = gctl_issue(grq); 448113896Sphk if (errstr != NULL) 449113896Sphk errx(1, "%s", errstr); 450113896Sphk gctl_free(grq); 451113896Sphk } 452113896Sphk } else { 453113896Sphk if (lseek(fd, 0, SEEK_SET) < 0) 454113896Sphk err(1, "lseek"); 455113896Sphk if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 456113896Sphk err (1, "write"); 457113896Sphk if (Bflag) { 458113824Sphk for (i = 0; i < SUN_NPART; i++) { 459110130Sjake if (sl->sl_part[i].sdkp_nsectors == 0) 460110130Sjake continue; 461110130Sjake off = sl->sl_part[i].sdkp_cyloffset * 462110130Sjake sl->sl_ntracks * sl->sl_nsectors * 512; 463113896Sphk /* 464113896Sphk * Ignore first SUN_SIZE bytes of boot code to 465113896Sphk * avoid overwriting the label. 466113896Sphk */ 467113896Sphk if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0) 468110130Sjake err(1, "lseek"); 469113896Sphk if (write(fd, boot + SUN_SIZE, 470113896Sphk sizeof(boot) - SUN_SIZE) != 471113896Sphk sizeof(boot) - SUN_SIZE) 472110130Sjake err(1, "write"); 473110130Sjake } 474110130Sjake } 475110130Sjake close(fd); 476113896Sphk } 477113896Sphk exit(0); 478110130Sjake} 479110130Sjake 480129965Sjoergstatic void 481110130Sjakeedit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 482110130Sjake{ 483110130Sjake char tmpfil[] = _PATH_TMPFILE; 484110130Sjake const char *editor; 485110130Sjake int status; 486110130Sjake FILE *fp; 487110130Sjake pid_t pid; 488110130Sjake pid_t r; 489110130Sjake int fd; 490110130Sjake int c; 491110130Sjake 492110130Sjake if ((fd = mkstemp(tmpfil)) < 0) 493110130Sjake err(1, "mkstemp"); 494110130Sjake if ((fp = fdopen(fd, "w")) == NULL) 495110130Sjake err(1, "fdopen"); 496110130Sjake print_label(sl, disk, fp); 497110130Sjake fflush(fp); 498129965Sjoerg for (;;) { 499129965Sjoerg if ((pid = fork()) < 0) 500129965Sjoerg err(1, "fork"); 501129965Sjoerg if (pid == 0) { 502129965Sjoerg if ((editor = getenv("EDITOR")) == NULL) 503129965Sjoerg editor = _PATH_VI; 504129965Sjoerg execlp(editor, editor, tmpfil, (char *)NULL); 505129965Sjoerg err(1, "execlp %s", editor); 506110130Sjake } 507129965Sjoerg status = 0; 508129965Sjoerg while ((r = wait(&status)) > 0 && r != pid) 509129965Sjoerg ; 510129965Sjoerg if (WIFEXITED(status)) { 511129965Sjoerg if (parse_label(sl, tmpfil) == 0) { 512129965Sjoerg fclose(fp); 513129965Sjoerg unlink(tmpfil); 514129965Sjoerg write_label(sl, disk, bootpath); 515129965Sjoerg return; 516129965Sjoerg } 517129965Sjoerg printf("re-edit the label? [y]: "); 518129965Sjoerg fflush(stdout); 519129965Sjoerg c = getchar(); 520129965Sjoerg if (c != EOF && c != '\n') 521129965Sjoerg while (getchar() != '\n') 522129965Sjoerg ; 523129965Sjoerg if (c == 'n') { 524129965Sjoerg fclose(fp); 525129965Sjoerg unlink(tmpfil); 526129965Sjoerg return; 527129965Sjoerg } 528110130Sjake } 529110130Sjake } 530110130Sjake fclose(fp); 531110130Sjake unlink(tmpfil); 532129965Sjoerg return; 533110130Sjake} 534110130Sjake 535110130Sjakestatic int 536110130Sjakeparse_label(struct sun_disklabel *sl, const char *file) 537110130Sjake{ 538110130Sjake char offset[32]; 539110130Sjake char size[32]; 540129965Sjoerg char flag[32]; 541129965Sjoerg char tag[32]; 542110130Sjake char buf[128]; 543129965Sjoerg char text[128]; 544129965Sjoerg struct sun_disklabel sl1; 545128916Sjoerg char *bp; 546129965Sjoerg const char *what; 547110130Sjake uint8_t part; 548110130Sjake FILE *fp; 549110130Sjake int line; 550129965Sjoerg int rv; 551129965Sjoerg int wantvtoc; 552129965Sjoerg unsigned alt, cyl, hd, nr, sec; 553110130Sjake 554129965Sjoerg line = wantvtoc = 0; 555110130Sjake if ((fp = fopen(file, "r")) == NULL) 556110130Sjake err(1, "fopen"); 557129965Sjoerg sl1 = *sl; 558129965Sjoerg bzero(&sl1.sl_part, sizeof(sl1.sl_part)); 559110130Sjake while (fgets(buf, sizeof(buf), fp) != NULL) { 560128916Sjoerg /* 561128916Sjoerg * In order to recognize a partition entry, we search 562128916Sjoerg * for lines starting with a single letter followed by 563128916Sjoerg * a colon as their first non-white characters. We 564128916Sjoerg * silently ignore any other lines, so any comment etc. 565128916Sjoerg * lines in the label template will be ignored. 566128916Sjoerg * 567128916Sjoerg * XXX We should probably also recognize the geometry 568128916Sjoerg * fields on top, and allow changing the geometry 569128916Sjoerg * emulated by this disk. 570128916Sjoerg */ 571128916Sjoerg for (bp = buf; isspace(*bp); bp++) 572128916Sjoerg ; 573129965Sjoerg if (strncmp(bp, "text:", strlen("text:")) == 0) { 574129965Sjoerg bp += strlen("text:"); 575129965Sjoerg rv = sscanf(bp, 576130692Sjoerg " %s cyl %u alt %u hd %u sec %u", 577130692Sjoerg text, &cyl, &alt, &hd, &sec); 578129965Sjoerg if (rv != 5) { 579129965Sjoerg warnx("%s, line %d: text label does not " 580130692Sjoerg "contain required fields", 581130692Sjoerg file, line + 1); 582129965Sjoerg fclose(fp); 583129965Sjoerg return (1); 584129965Sjoerg } 585129965Sjoerg if (alt != 2) { 586129965Sjoerg warnx("%s, line %d: # alt must be equal 2", 587130692Sjoerg file, line + 1); 588129965Sjoerg fclose(fp); 589129965Sjoerg return (1); 590129965Sjoerg } 591129965Sjoerg if (cyl == 0 || cyl > USHRT_MAX) { 592129965Sjoerg what = "cyl"; 593129965Sjoerg nr = cyl; 594129965Sjoerg unreasonable: 595129965Sjoerg warnx("%s, line %d: # %s %d unreasonable", 596130692Sjoerg file, line + 1, what, nr); 597129965Sjoerg fclose(fp); 598129965Sjoerg return (1); 599129965Sjoerg } 600129965Sjoerg if (hd == 0 || hd > USHRT_MAX) { 601129965Sjoerg what = "hd"; 602129965Sjoerg nr = hd; 603129965Sjoerg goto unreasonable; 604129965Sjoerg } 605129965Sjoerg if (sec == 0 || sec > USHRT_MAX) { 606129965Sjoerg what = "sec"; 607129965Sjoerg nr = sec; 608129965Sjoerg goto unreasonable; 609129965Sjoerg } 610129965Sjoerg if (mediasize == 0) 611129965Sjoerg warnx("unit size unknown, no sector count " 612130692Sjoerg "check could be done"); 613129965Sjoerg else if ((uintmax_t)(cyl + alt) * sec * hd > 614129965Sjoerg (uintmax_t)mediasize / sectorsize) { 615129965Sjoerg warnx("%s, line %d: sector count %ju exceeds " 616130692Sjoerg "unit size %ju", 617130692Sjoerg file, line + 1, 618130692Sjoerg (uintmax_t)(cyl + alt) * sec * hd, 619130692Sjoerg (uintmax_t)mediasize / sectorsize); 620129965Sjoerg fclose(fp); 621129965Sjoerg return (1); 622129965Sjoerg } 623129965Sjoerg sl1.sl_pcylinders = cyl + alt; 624129965Sjoerg sl1.sl_ncylinders = cyl; 625129965Sjoerg sl1.sl_acylinders = alt; 626129965Sjoerg sl1.sl_nsectors = sec; 627129965Sjoerg sl1.sl_ntracks = hd; 628129965Sjoerg memset(sl1.sl_text, 0, sizeof(sl1.sl_text)); 629129965Sjoerg snprintf(sl1.sl_text, sizeof(sl1.sl_text), 630130692Sjoerg "%s cyl %u alt %u hd %u sec %u", 631130692Sjoerg text, cyl, alt, hd, sec); 632129965Sjoerg continue; 633129965Sjoerg } 634128916Sjoerg if (strlen(bp) < 2 || bp[1] != ':') { 635128916Sjoerg line++; 636110130Sjake continue; 637128916Sjoerg } 638129965Sjoerg rv = sscanf(bp, "%c: %30s %30s %30s %30s", 639130692Sjoerg &part, size, offset, tag, flag); 640129965Sjoerg if (rv < 3) { 641129965Sjoerg syntaxerr: 642128916Sjoerg warnx("%s: syntax error on line %d", 643128916Sjoerg file, line + 1); 644110130Sjake fclose(fp); 645110130Sjake return (1); 646110130Sjake } 647129965Sjoerg if (parse_size(&sl1, part - 'a', size) || 648129965Sjoerg parse_offset(&sl1, part - 'a', offset)) 649129965Sjoerg goto syntaxerr; 650129965Sjoerg if (rv > 3) { 651129965Sjoerg wantvtoc = 1; 652129965Sjoerg if (rv == 5 && parse_flag(&sl1, part - 'a', flag)) 653129965Sjoerg goto syntaxerr; 654129965Sjoerg if (parse_tag(&sl1, part - 'a', tag)) 655129965Sjoerg goto syntaxerr; 656129965Sjoerg } 657110130Sjake line++; 658110130Sjake } 659110130Sjake fclose(fp); 660129965Sjoerg if (wantvtoc) { 661129965Sjoerg sl1.sl_vtoc_sane = SUN_VTOC_SANE; 662129965Sjoerg sl1.sl_vtoc_vers = SUN_VTOC_VERSION; 663129965Sjoerg sl1.sl_vtoc_nparts = SUN_NPART; 664129965Sjoerg } else { 665129965Sjoerg sl1.sl_vtoc_sane = 0; 666129965Sjoerg sl1.sl_vtoc_vers = 0; 667129965Sjoerg sl1.sl_vtoc_nparts = 0; 668129965Sjoerg bzero(&sl1.sl_vtoc_map, sizeof(sl1.sl_vtoc_map)); 669129965Sjoerg } 670129965Sjoerg *sl = sl1; 671110130Sjake return (check_label(sl)); 672110130Sjake} 673110130Sjake 674110130Sjakestatic int 675110130Sjakeparse_size(struct sun_disklabel *sl, int part, char *size) 676110130Sjake{ 677110130Sjake uintmax_t nsectors; 678110130Sjake uintmax_t total; 679110130Sjake uintmax_t n; 680110130Sjake char *p; 681110130Sjake int i; 682110130Sjake 683110130Sjake nsectors = 0; 684110130Sjake n = strtoumax(size, &p, 10); 685110130Sjake if (*p != '\0') { 686110130Sjake if (strcmp(size, "*") == 0) { 687110130Sjake total = sl->sl_ncylinders * sl->sl_ntracks * 688110130Sjake sl->sl_nsectors; 689110130Sjake for (i = 0; i < part; i++) { 690110130Sjake if (i == 2) 691110130Sjake continue; 692110130Sjake nsectors += sl->sl_part[i].sdkp_nsectors; 693110130Sjake } 694110130Sjake n = total - nsectors; 695129965Sjoerg } else if (p[1] == '\0' && (p[0] == 'C' || p[0] == 'c')) { 696129965Sjoerg n = n * sl->sl_ntracks * sl->sl_nsectors; 697110130Sjake } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) { 698110130Sjake n = roundup((n * 1024) / 512, 699110130Sjake sl->sl_ntracks * sl->sl_nsectors); 700110130Sjake } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) { 701110130Sjake n = roundup((n * 1024 * 1024) / 512, 702110130Sjake sl->sl_ntracks * sl->sl_nsectors); 703129965Sjoerg } else if (p[1] == '\0' && (p[0] == 'S' || p[0] == 's')) { 704129965Sjoerg /* size in sectors, no action neded */ 705110130Sjake } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) { 706110130Sjake n = roundup((n * 1024 * 1024 * 1024) / 512, 707110130Sjake sl->sl_ntracks * sl->sl_nsectors); 708110130Sjake } else 709110130Sjake return (-1); 710129965Sjoerg } else if (cflag) { 711129965Sjoerg n = n * sl->sl_ntracks * sl->sl_nsectors; 712110130Sjake } 713110130Sjake sl->sl_part[part].sdkp_nsectors = n; 714110130Sjake return (0); 715110130Sjake} 716110130Sjake 717110130Sjakestatic int 718110130Sjakeparse_offset(struct sun_disklabel *sl, int part, char *offset) 719110130Sjake{ 720110130Sjake uintmax_t nsectors; 721110130Sjake uintmax_t n; 722110130Sjake char *p; 723110130Sjake int i; 724110130Sjake 725110130Sjake nsectors = 0; 726110130Sjake n = strtoumax(offset, &p, 10); 727110130Sjake if (*p != '\0') { 728110130Sjake if (strcmp(offset, "*") == 0) { 729110130Sjake for (i = 0; i < part; i++) { 730110130Sjake if (i == 2) 731110130Sjake continue; 732110130Sjake nsectors += sl->sl_part[i].sdkp_nsectors; 733110130Sjake } 734110130Sjake n = nsectors / (sl->sl_nsectors * sl->sl_ntracks); 735110130Sjake } else 736110130Sjake return (-1); 737110130Sjake } 738110130Sjake sl->sl_part[part].sdkp_cyloffset = n; 739110130Sjake return (0); 740110130Sjake} 741110130Sjake 742110130Sjakestatic void 743110130Sjakeprint_label(struct sun_disklabel *sl, const char *disk, FILE *out) 744110130Sjake{ 745110130Sjake int i; 746129965Sjoerg int havevtoc; 747129965Sjoerg uintmax_t secpercyl; 748110130Sjake 749129965Sjoerg havevtoc = sl->sl_vtoc_sane == SUN_VTOC_SANE; 750129965Sjoerg secpercyl = sl->sl_nsectors * sl->sl_ntracks; 751129965Sjoerg 752110130Sjake fprintf(out, 753110130Sjake"# /dev/%s:\n" 754110130Sjake"text: %s\n" 755141956Sobrien"bytes/sector: %d\n" 756129965Sjoerg"sectors/cylinder: %ju\n", 757129965Sjoerg disk, 758129965Sjoerg sl->sl_text, 759129965Sjoerg sectorsize, 760129965Sjoerg secpercyl); 761129965Sjoerg if (eflag) 762129965Sjoerg fprintf(out, 763130692Sjoerg "# max sectors/unit (including alt cylinders): %ju\n", 764130692Sjoerg (uintmax_t)mediasize / sectorsize); 765129965Sjoerg fprintf(out, 766129965Sjoerg"sectors/unit: %ju\n" 767110130Sjake"\n" 768113824Sphk"%d partitions:\n" 769129965Sjoerg"#\n", 770129965Sjoerg secpercyl * sl->sl_ncylinders, 771129965Sjoerg SUN_NPART); 772129965Sjoerg if (!hflag) { 773129965Sjoerg fprintf(out, "# Size is in %s.", cflag? "cylinders": "sectors"); 774129965Sjoerg if (eflag) 775129965Sjoerg fprintf(out, 776129965Sjoerg" Use %%d%c, %%dK, %%dM or %%dG to specify in %s,\n" 777129965Sjoerg"# kilobytes, megabytes or gigabytes respectively, or '*' to specify rest of\n" 778129965Sjoerg"# disk.\n", 779130692Sjoerg cflag? 's': 'c', 780130692Sjoerg cflag? "sectors": "cylinders"); 781129965Sjoerg else 782129965Sjoerg putc('\n', out); 783129965Sjoerg fprintf(out, "# Offset is in cylinders."); 784129965Sjoerg if (eflag) 785129965Sjoerg fprintf(out, 786129965Sjoerg" Use '*' to calculate offsets automatically.\n" 787129965Sjoerg"#\n"); 788129965Sjoerg else 789129965Sjoerg putc('\n', out); 790129965Sjoerg } 791129965Sjoerg if (havevtoc) 792129965Sjoerg fprintf(out, 793129965Sjoerg"# size offset tag flag\n" 794129965Sjoerg"# ---------- ---------- ---------- ----\n" 795129965Sjoerg ); 796129965Sjoerg else 797129965Sjoerg fprintf(out, 798110130Sjake"# size offset\n" 799129965Sjoerg"# ---------- ----------\n" 800129965Sjoerg ); 801129965Sjoerg 802113824Sphk for (i = 0; i < SUN_NPART; i++) { 803110130Sjake if (sl->sl_part[i].sdkp_nsectors == 0) 804110130Sjake continue; 805129965Sjoerg if (hflag) { 806129965Sjoerg fprintf(out, " %c: %10s", 807130692Sjoerg 'a' + i, 808130692Sjoerg make_h_number((uintmax_t) 809130692Sjoerg sl->sl_part[i].sdkp_nsectors * 512)); 810129965Sjoerg fprintf(out, " %10s", 811130692Sjoerg make_h_number((uintmax_t) 812130692Sjoerg sl->sl_part[i].sdkp_cyloffset * 512 813130692Sjoerg * secpercyl)); 814129965Sjoerg } else { 815129965Sjoerg fprintf(out, " %c: %10ju %10u", 816130692Sjoerg 'a' + i, 817130692Sjoerg sl->sl_part[i].sdkp_nsectors / (cflag? secpercyl: 1), 818130692Sjoerg sl->sl_part[i].sdkp_cyloffset); 819129965Sjoerg } 820129965Sjoerg if (havevtoc) 821129965Sjoerg fprintf(out, " %11s %5s", 822130692Sjoerg tagname(sl->sl_vtoc_map[i].svtoc_tag), 823130692Sjoerg flagname(sl->sl_vtoc_map[i].svtoc_flag)); 824129965Sjoerg putc('\n', out); 825110130Sjake } 826110130Sjake} 827110130Sjake 828110130Sjakestatic void 829110130Sjakeusage(void) 830110130Sjake{ 831110130Sjake 832110130Sjake fprintf(stderr, "usage:" 833129965Sjoerg"\t%s [-r] [-c | -h] disk\n" 834110130Sjake"\t\t(to read label)\n" 835110130Sjake"\t%s -B [-b boot1] [-n] disk\n" 836110130Sjake"\t\t(to install boot program only)\n" 837129965Sjoerg"\t%s -R [-B [-b boot1]] [-r] [-n] [-c] disk protofile\n" 838110130Sjake"\t\t(to restore label)\n" 839129965Sjoerg"\t%s -e [-B [-b boot1]] [-r] [-n] [-c] disk\n" 840110130Sjake"\t\t(to edit label)\n" 841110130Sjake"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n" 842110130Sjake"\t\t(to write default label)\n", 843110130Sjake __progname, 844110130Sjake __progname, 845110130Sjake __progname, 846110130Sjake __progname, 847110130Sjake __progname); 848110130Sjake exit(1); 849110130Sjake} 850129965Sjoerg 851129965Sjoerg/* 852129965Sjoerg * Return VTOC tag and flag names for tag or flag ID, resp. 853129965Sjoerg */ 854129965Sjoergstatic const char * 855129965Sjoergtagname(unsigned int tag) 856129965Sjoerg{ 857129965Sjoerg static char buf[32]; 858129965Sjoerg size_t i; 859129965Sjoerg struct tags *tp; 860129965Sjoerg 861129965Sjoerg for (i = 0, tp = knowntags; 862129965Sjoerg i < sizeof(knowntags) / sizeof(struct tags); 863129965Sjoerg i++, tp++) 864129965Sjoerg if (tp->id == tag) 865129965Sjoerg return (tp->name); 866129965Sjoerg 867129965Sjoerg sprintf(buf, "%u", tag); 868129965Sjoerg 869129965Sjoerg return (buf); 870129965Sjoerg} 871129965Sjoerg 872129965Sjoergstatic const char * 873129965Sjoergflagname(unsigned int flag) 874129965Sjoerg{ 875129965Sjoerg static char buf[32]; 876129965Sjoerg size_t i; 877129965Sjoerg struct tags *tp; 878129965Sjoerg 879129965Sjoerg for (i = 0, tp = knownflags; 880129965Sjoerg i < sizeof(knownflags) / sizeof(struct tags); 881129965Sjoerg i++, tp++) 882129965Sjoerg if (tp->id == flag) 883129965Sjoerg return (tp->name); 884129965Sjoerg 885129965Sjoerg sprintf(buf, "%u", flag); 886129965Sjoerg 887129965Sjoerg return (buf); 888129965Sjoerg} 889129965Sjoerg 890129965Sjoergstatic unsigned int 891129965Sjoergparse_tag(struct sun_disklabel *sl, int part, const char *tag) 892129965Sjoerg{ 893129965Sjoerg struct tags *tp; 894129965Sjoerg char *endp; 895129965Sjoerg size_t i; 896129965Sjoerg unsigned long l; 897129965Sjoerg 898129965Sjoerg for (i = 0, tp = knowntags; 899129965Sjoerg i < sizeof(knowntags) / sizeof(struct tags); 900129965Sjoerg i++, tp++) 901129965Sjoerg if (strcmp(tp->name, tag) == 0) { 902129965Sjoerg sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)tp->id; 903129965Sjoerg return (0); 904129965Sjoerg } 905129965Sjoerg 906129965Sjoerg l = strtoul(tag, &endp, 0); 907129965Sjoerg if (*tag != '\0' && *endp == '\0') { 908129965Sjoerg sl->sl_vtoc_map[part].svtoc_tag = (uint16_t)l; 909129965Sjoerg return (0); 910129965Sjoerg } 911129965Sjoerg 912129965Sjoerg return (-1); 913129965Sjoerg} 914129965Sjoerg 915129965Sjoergstatic unsigned int 916129965Sjoergparse_flag(struct sun_disklabel *sl, int part, const char *flag) 917129965Sjoerg{ 918129965Sjoerg struct tags *tp; 919129965Sjoerg char *endp; 920129965Sjoerg size_t i; 921129965Sjoerg unsigned long l; 922129965Sjoerg 923129965Sjoerg for (i = 0, tp = knownflags; 924129965Sjoerg i < sizeof(knownflags) / sizeof(struct tags); 925129965Sjoerg i++, tp++) 926129965Sjoerg if (strcmp(tp->name, flag) == 0) { 927129965Sjoerg sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)tp->id; 928129965Sjoerg return (0); 929129965Sjoerg } 930129965Sjoerg 931129965Sjoerg l = strtoul(flag, &endp, 0); 932129965Sjoerg if (*flag != '\0' && *endp == '\0') { 933129965Sjoerg sl->sl_vtoc_map[part].svtoc_flag = (uint16_t)l; 934129965Sjoerg return (0); 935129965Sjoerg } 936129965Sjoerg 937129965Sjoerg return (-1); 938129965Sjoerg} 939129965Sjoerg 940129965Sjoerg/* 941129965Sjoerg * Convert argument into `human readable' byte number form. 942129965Sjoerg */ 943129965Sjoergstatic const char * 944129965Sjoergmake_h_number(uintmax_t u) 945129965Sjoerg{ 946129965Sjoerg static char buf[32]; 947129965Sjoerg double d; 948129965Sjoerg 949129965Sjoerg if (u == 0) { 950129965Sjoerg strcpy(buf, "0B"); 951129965Sjoerg } else if (u > 2000000000UL) { 952129965Sjoerg d = (double)u / 1e9; 953129965Sjoerg sprintf(buf, "%.1fG", d); 954129965Sjoerg } else if (u > 2000000UL) { 955129965Sjoerg d = (double)u / 1e6; 956129965Sjoerg sprintf(buf, "%.1fM", d); 957129965Sjoerg } else { 958129965Sjoerg d = (double)u / 1e3; 959129965Sjoerg sprintf(buf, "%.1fK", d); 960129965Sjoerg } 961129965Sjoerg 962129965Sjoerg return (buf); 963129965Sjoerg} 964