sgivol.c revision 1.15.46.1 1 /* $NetBSD: sgivol.c,v 1.15.46.1 2008/05/18 12:32:43 yamt Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Michael Hitch and Hubert Feyrer.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39
40 #if HAVE_NBTOOL_CONFIG_H
41 #include "../../../../../sys/sys/bootblock.h"
42 /* Ficticious geometry for cross tool usage against a file image */
43 #define SGIVOL_NBTOOL_NSECS 32
44 #define SGIVOL_NBTOOL_NTRACKS 64
45 #else
46 #include <sys/disklabel.h>
47 #endif
48
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <fcntl.h>
55 #include <util.h>
56 #ifndef HAVE_NBTOOL_CONFIG_H
57 #include <sys/endian.h>
58 #endif
59
60 int fd;
61 int opt_i; /* Initialize volume header */
62 int opt_r; /* Read a file from volume header */
63 int opt_w; /* Write a file to volume header */
64 int opt_d; /* Delete a file from volume header */
65 int opt_p; /* Modify a partition */
66 int opt_q; /* quiet mode */
67 int opt_f; /* Don't ask, just do what you're told */
68 int partno, partfirst, partblocks, parttype;
69 struct sgi_boot_block *volhdr;
70 int32_t checksum;
71 u_int32_t volhdr_size = SGI_BOOT_BLOCK_SIZE_VOLHDR;
72
73 const char *vfilename = "";
74 const char *ufilename = "";
75
76 #if HAVE_NBTOOL_CONFIG_H
77 struct stat st;
78 #else
79 struct disklabel lbl;
80 #endif
81
82 unsigned char buf[512];
83
84 const char *sgi_types[] = {
85 "Volume Header",
86 "Repl Trks",
87 "Repl Secs",
88 "Raw",
89 "BSD4.2",
90 "SysV",
91 "Volume",
92 "EFS",
93 "LVol",
94 "RLVol",
95 "XFS",
96 "XSFLog",
97 "XLV",
98 "XVM"
99 };
100
101 int main(int, char *[]);
102
103 void display_vol(void);
104 void init_volhdr(void);
105 void read_file(void);
106 void write_file(void);
107 void delete_file(void);
108 void modify_partition(void);
109 void write_volhdr(void);
110 int allocate_space(int);
111 void checksum_vol(void);
112 void usage(void);
113
114 int
115 main(int argc, char *argv[])
116 {
117 int ch;
118 while ((ch = getopt(argc, argv, "irwpdqfh:")) != -1) {
119 switch (ch) {
120 /* -i, -r, -w, -d and -p override each other */
121 /* -q implies -f */
122 case 'q':
123 ++opt_q;
124 ++opt_f;
125 break;
126 case 'f':
127 ++opt_f;
128 break;
129 case 'i':
130 ++opt_i;
131 opt_r = opt_w = opt_d = opt_p = 0;
132 break;
133 case 'h':
134 volhdr_size = atoi(optarg);
135 break;
136 case 'r':
137 ++opt_r;
138 opt_i = opt_w = opt_d = opt_p = 0;
139 break;
140 case 'w':
141 ++opt_w;
142 opt_i = opt_r = opt_d = opt_p = 0;
143 break;
144 case 'd':
145 ++opt_d;
146 opt_i = opt_r = opt_w = opt_p = 0;
147 break;
148 case 'p':
149 ++opt_p;
150 opt_i = opt_r = opt_w = opt_d = 0;
151 partno = atoi(argv[0]);
152 partfirst = atoi(argv[1]);
153 partblocks = atoi(argv[2]);
154 parttype = atoi(argv[3]);
155 break;
156 case '?':
157 default:
158 usage();
159 }
160 }
161 argc -= optind;
162 argv += optind;
163
164 if (opt_r || opt_w) {
165 if (argc != 3)
166 usage();
167 vfilename = argv[0];
168 ufilename = argv[1];
169 argc -= 2;
170 argv += 2;
171 }
172 if (opt_d) {
173 if (argc != 2)
174 usage();
175 vfilename = argv[0];
176 argc--;
177 argv++;
178 }
179
180 if (opt_p) {
181 if (argc != 5)
182 usage();
183 partno = atoi(argv[0]);
184 partfirst = atoi(argv[1]);
185 partblocks = atoi(argv[2]);
186 parttype = atoi(argv[3]);
187 argc -= 4;
188 argv += 4;
189 }
190 if (argc != 1)
191 usage();
192
193 fd = open(argv[0], (opt_i | opt_w | opt_d | opt_p) ? O_RDWR : O_RDONLY);
194 if (fd < 0) {
195 #if HAVE_NBTOOL_CONFIG_H
196 perror("File open");
197 exit(1);
198 #else
199 sprintf((char *)buf, "/dev/r%s%c", argv[0], 'a' + getrawpartition());
200 fd = open((char *)buf, (opt_i | opt_w | opt_d | opt_p)
201 ? O_RDWR : O_RDONLY);
202 if (fd < 0) {
203 printf("Error opening device %s: %s\n",
204 argv[0], strerror(errno));
205 exit(1);
206 }
207 #endif
208 }
209 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
210 perror("read volhdr");
211 exit(1);
212 }
213 #if HAVE_NBTOOL_CONFIG_H
214 if (fstat(fd, &st) < 0) {
215 perror("stat error");
216 exit(1);
217 }
218 if (!S_ISREG(st.st_mode)) {
219 printf("Must be regular file\n");
220 exit(1);
221 }
222 if (st.st_size % SGI_BOOT_BLOCK_BLOCKSIZE) {
223 printf("Size must be multiple of %d\n",
224 SGI_BOOT_BLOCK_BLOCKSIZE);
225 exit(1);
226 }
227 if (st.st_size < (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS)) {
228 printf("Minimum size of %d required\n",
229 SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS);
230 exit(1);
231 }
232 #else
233 if (ioctl(fd, DIOCGDINFO, &lbl) < 0) {
234 perror("DIOCGDINFO");
235 exit(1);
236 }
237 #endif
238 volhdr = (struct sgi_boot_block *) buf;
239 if (opt_i) {
240 init_volhdr();
241 exit(0);
242 }
243 if (be32toh(volhdr->magic) != SGI_BOOT_BLOCK_MAGIC) {
244 printf("No Volume Header found, magic=%x. Use -i first.\n",
245 be32toh(volhdr->magic));
246 exit(1);
247 }
248 if (opt_r) {
249 read_file();
250 exit(0);
251 }
252 if (opt_w) {
253 write_file();
254 exit(0);
255 }
256 if (opt_d) {
257 delete_file();
258 exit(0);
259 }
260 if (opt_p) {
261 modify_partition();
262 exit(0);
263 }
264
265 if (!opt_q)
266 display_vol();
267
268 return 0;
269 }
270
271 void
272 display_vol(void)
273 {
274 int32_t *l;
275 int i;
276
277 #if HAVE_NBTOOL_CONFIG_H
278 printf("disklabel shows %d sectors\n",
279 st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
280 #else
281 printf("disklabel shows %d sectors\n", lbl.d_secperunit);
282 #endif
283 l = (int32_t *)buf;
284 checksum = 0;
285 for (i = 0; i < 512 / 4; ++i)
286 checksum += be32toh(l[i]);
287 printf("checksum: %08x%s\n", checksum, checksum == 0 ? "" : " *ERROR*");
288 printf("root part: %d\n", be16toh(volhdr->root));
289 printf("swap part: %d\n", be16toh(volhdr->swap));
290 printf("bootfile: %s\n", volhdr->bootfile);
291 /* volhdr->devparams[0..47] */
292 printf("\nVolume header files:\n");
293 for (i = 0; i < 15; ++i)
294 if (volhdr->voldir[i].name[0])
295 printf("%-8s offset %4d blocks, length %8d bytes "
296 "(%d blocks)\n",
297 volhdr->voldir[i].name,
298 be32toh(volhdr->voldir[i].block),
299 be32toh(volhdr->voldir[i].bytes),
300 (be32toh(volhdr->voldir[i].bytes) + 511) / 512);
301 printf("\nSGI partitions:\n");
302 for (i = 0; i < SGI_BOOT_BLOCK_MAXPARTITIONS; ++i) {
303 if (be32toh(volhdr->partitions[i].blocks)) {
304 printf("%2d:%c blocks %8d first %8d type %2d (%s)\n",
305 i, i + 'a', be32toh(volhdr->partitions[i].blocks),
306 be32toh(volhdr->partitions[i].first),
307 be32toh(volhdr->partitions[i].type),
308 be32toh(volhdr->partitions[i].type) > 13 ? "???" :
309 sgi_types[be32toh(volhdr->partitions[i].type)]);
310 }
311 }
312 }
313
314 void
315 init_volhdr(void)
316 {
317 memset(buf, 0, sizeof(buf));
318 volhdr->magic = htobe32(SGI_BOOT_BLOCK_MAGIC);
319 volhdr->root = htobe16(0);
320 volhdr->swap = htobe16(1);
321 strcpy(volhdr->bootfile, "/netbsd");
322 #if HAVE_NBTOOL_CONFIG_H
323 volhdr->dp.dp_skew = 0;
324 #else
325 volhdr->dp.dp_skew = lbl.d_trackskew;
326 #endif
327 volhdr->dp.dp_gap1 = 1; /* XXX */
328 volhdr->dp.dp_gap2 = 1; /* XXX */
329 #if HAVE_NBTOOL_CONFIG_H
330 volhdr->dp.dp_cyls =
331 htobe16(st.st_size / (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS));
332 #else
333 volhdr->dp.dp_cyls = htobe16(lbl.d_ncylinders);
334 #endif
335 volhdr->dp.dp_shd0 = 0;
336 #if HAVE_NBTOOL_CONFIG_H
337 volhdr->dp.dp_trks0 = htobe16(SGIVOL_NBTOOL_NTRACKS);
338 volhdr->dp.dp_secs = htobe16(SGIVOL_NBTOOL_NSECS);
339 volhdr->dp.dp_secbytes = htobe16(SGI_BOOT_BLOCK_BLOCKSIZE);
340 volhdr->dp.dp_interleave = htobe16(1);
341 #else
342 volhdr->dp.dp_trks0 = htobe16(lbl.d_ntracks);
343 volhdr->dp.dp_secs = htobe16(lbl.d_nsectors);
344 volhdr->dp.dp_secbytes = htobe16(lbl.d_secsize);
345 volhdr->dp.dp_interleave = htobe16(lbl.d_interleave);
346 #endif
347 volhdr->dp.dp_nretries = htobe32(22);
348 #if HAVE_NBTOOL_CONFIG_H
349 volhdr->partitions[10].blocks =
350 htobe32(st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
351 #else
352 volhdr->partitions[10].blocks = htobe32(lbl.d_secperunit);
353 #endif
354 volhdr->partitions[10].first = 0;
355 volhdr->partitions[10].type = htobe32(SGI_PTYPE_VOLUME);
356 volhdr->partitions[8].blocks = htobe32(volhdr_size);
357 volhdr->partitions[8].first = 0;
358 volhdr->partitions[8].type = htobe32(SGI_PTYPE_VOLHDR);
359 #if HAVE_NBTOOL_CONFIG_H
360 volhdr->partitions[0].blocks =
361 htobe32((st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE) - volhdr_size);
362 #else
363 volhdr->partitions[0].blocks = htobe32(lbl.d_secperunit - volhdr_size);
364 #endif
365 volhdr->partitions[0].first = htobe32(volhdr_size);
366 volhdr->partitions[0].type = htobe32(SGI_PTYPE_BSD);
367 write_volhdr();
368 }
369
370 void
371 read_file(void)
372 {
373 FILE *fp;
374 int i;
375
376 if (!opt_q)
377 printf("Reading file %s\n", vfilename);
378 for (i = 0; i < 15; ++i) {
379 if (strncmp(vfilename, volhdr->voldir[i].name,
380 strlen(volhdr->voldir[i].name)) == 0)
381 break;
382 }
383 if (i >= 15) {
384 printf("file %s not found\n", vfilename);
385 exit(1);
386 }
387 /* XXX assumes volume header starts at 0? */
388 lseek(fd, be32toh(volhdr->voldir[i].block) * 512, SEEK_SET);
389 fp = fopen(ufilename, "w");
390 if (fp == NULL) {
391 perror("open write");
392 exit(1);
393 }
394 i = be32toh(volhdr->voldir[i].bytes);
395 while (i > 0) {
396 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
397 perror("read file");
398 exit(1);
399 }
400 fwrite(buf, 1, i > sizeof(buf) ? sizeof(buf) : i, fp);
401 i -= i > sizeof(buf) ? sizeof(buf) : i;
402 }
403 fclose(fp);
404 }
405
406 void
407 write_file(void)
408 {
409 FILE *fp;
410 int slot;
411 size_t namelen;
412 int block, i;
413 struct stat st;
414 char fbuf[512];
415
416 if (!opt_q)
417 printf("Writing file %s\n", ufilename);
418 if (stat(ufilename, &st) < 0) {
419 perror("stat");
420 exit(1);
421 }
422 if (!opt_q)
423 printf("File %s has %lld bytes\n", ufilename, st.st_size);
424 slot = -1;
425 for (i = 0; i < 15; ++i) {
426 if (volhdr->voldir[i].name[0] == '\0' && slot < 0)
427 slot = i;
428 if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
429 slot = i;
430 break;
431 }
432 }
433 if (slot == -1) {
434 printf("No directory space for file %s\n", vfilename);
435 exit(1);
436 }
437 /* -w can overwrite, -a won't overwrite */
438 if (be32toh(volhdr->voldir[slot].block) > 0) {
439 if (!opt_q)
440 printf("File %s exists, removing old file\n",
441 vfilename);
442 volhdr->voldir[slot].name[0] = 0;
443 volhdr->voldir[slot].block = volhdr->voldir[slot].bytes = 0;
444 }
445 if (st.st_size == 0) {
446 printf("bad file size\n");
447 exit(1);
448 }
449 /* XXX assumes volume header starts at 0? */
450 block = allocate_space((int)st.st_size);
451 if (block < 0) {
452 printf("No space for file\n");
453 exit(1);
454 }
455
456 /*
457 * Make sure the name in the volume header is max. 8 chars,
458 * NOT including NUL.
459 */
460 namelen = strlen(vfilename);
461 if (namelen > sizeof(volhdr->voldir[slot].name)) {
462 printf("Warning: '%s' is too long for volume header, ",
463 vfilename);
464 namelen = sizeof(volhdr->voldir[slot].name);
465 printf("truncating to '%-8s'\n", vfilename);
466 }
467
468 /* Populate it w/ NULs */
469 memset(volhdr->voldir[slot].name, 0,
470 sizeof(volhdr->voldir[slot].name));
471 /* Then copy the name */
472 memcpy(volhdr->voldir[slot].name, vfilename, namelen);
473
474 volhdr->voldir[slot].block = htobe32(block);
475 volhdr->voldir[slot].bytes = htobe32(st.st_size);
476
477 write_volhdr();
478
479 /* write the file itself */
480 i = lseek(fd, block * 512, SEEK_SET);
481 if (i < 0) {
482 perror("lseek write");
483 exit(1);
484 }
485 i = st.st_size;
486 fp = fopen(ufilename, "r");
487 while (i > 0) {
488 fread(fbuf, 1, i > 512 ? 512 : i, fp);
489 if (write(fd, fbuf, 512) != 512) {
490 perror("write file");
491 exit(1);
492 }
493 i -= i > 512 ? 512 : i;
494 }
495 }
496
497 void
498 delete_file(void)
499 {
500 int i;
501
502 for (i = 0; i < 15; ++i) {
503 if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
504 break;
505 }
506 }
507 if (i >= 15) {
508 printf("File %s not found\n", vfilename);
509 exit(1);
510 }
511
512 /* XXX: we don't compact the file space, so get fragmentation */
513 volhdr->voldir[i].name[0] = '\0';
514 volhdr->voldir[i].block = volhdr->voldir[i].bytes = 0;
515 write_volhdr();
516 }
517
518 void
519 modify_partition(void)
520 {
521 if (!opt_q)
522 printf("Modify partition %d start %d length %d\n",
523 partno, partfirst, partblocks);
524 if (partno < 0 || partno > 15) {
525 printf("Invalid partition number: %d\n", partno);
526 exit(1);
527 }
528 volhdr->partitions[partno].blocks = htobe32(partblocks);
529 volhdr->partitions[partno].first = htobe32(partfirst);
530 volhdr->partitions[partno].type = htobe32(parttype);
531 write_volhdr();
532 }
533
534 void
535 write_volhdr(void)
536 {
537 int i;
538
539 checksum_vol();
540
541 if (!opt_q)
542 display_vol();
543 if (!opt_f) {
544 printf("\nDo you want to update volume (y/n)? ");
545 i = getchar();
546 if (i != 'Y' && i != 'y')
547 exit(1);
548 }
549 i = lseek(fd, 0, SEEK_SET);
550 if (i < 0) {
551 perror("lseek 0");
552 exit(1);
553 }
554 i = write(fd, buf, 512);
555 if (i < 0)
556 perror("write volhdr");
557 }
558
559 int
560 allocate_space(int size)
561 {
562 int n, blocks;
563 int first;
564
565 blocks = (size + 511) / 512;
566 first = 2;
567 n = 0;
568 while (n < 15) {
569 if (volhdr->voldir[n].name[0]) {
570 if (first < (be32toh(volhdr->voldir[n].block) +
571 (be32toh(volhdr->voldir[n].bytes) + 511) / 512) &&
572 (first + blocks) > be32toh(volhdr->voldir[n].block)) {
573 first = be32toh(volhdr->voldir[n].block) +
574 (be32toh(volhdr->voldir[n].bytes) + 511) / 512;
575 #if 0
576 printf("allocate: n=%d first=%d blocks=%d size=%d\n", n, first, blocks, size);
577 printf("%s %d %d\n", volhdr->voldir[n].name, volhdr->voldir[n].block, volhdr->voldir[n].bytes);
578 printf("first=%d block=%d last=%d end=%d\n", first, volhdr->voldir[n].block,
579 first + blocks - 1, volhdr->voldir[n].block + (volhdr->voldir[n].bytes + 511) / 512);
580 #endif
581 n = 0;
582 continue;
583 }
584 }
585 ++n;
586 }
587 #if HAVE_NBTOOL_CONFIG_H
588 if (first + blocks > (st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE))
589 #else
590 if (first + blocks > lbl.d_secperunit)
591 #endif
592 first = -1;
593 /* XXX assumes volume header is partition 8 */
594 /* XXX assumes volume header starts at 0? */
595 if (first + blocks >= be32toh(volhdr->partitions[8].blocks))
596 first = -1;
597 return (first);
598 }
599
600 void
601 checksum_vol(void)
602 {
603 int32_t *l;
604 int i;
605
606 volhdr->checksum = checksum = 0;
607 l = (int32_t *)buf;
608 for (i = 0; i < 512 / 4; ++i)
609 checksum += be32toh(l[i]);
610 volhdr->checksum = htobe32(-checksum);
611 }
612
613 void
614 usage(void)
615 {
616 printf("Usage: sgivol [-qf] -i [-h vhsize] device\n"
617 " sgivol [-qf] -r vhfilename diskfilename device\n"
618 " sgivol [-qf] -w vhfilename diskfilename device\n"
619 " sgivol [-qf] -d vhfilename device\n"
620 " sgivol [-qf] -p partno partfirst partblocks "
621 "parttype device\n"
622 );
623 exit(0);
624 }
625