7dbbc5d99261a2772aa630fedcfa278cfa6c1181
[rrq/maintain_lilo.git] / src / partition.c
1 /* partition.c  -  Partition table handling */
2 /*
3 Copyright 1992-1998 Werner Almesberger.
4 Copyright 1999-2005 John Coffman.
5 All rights reserved.
6
7 Licensed under the terms contained in the file 'COPYING' in the 
8 source directory.
9
10 */
11
12 #define _GNU_SOURCE
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <limits.h>
22 #include <time.h>
23 #include <dirent.h>
24 #include "config.h"
25 #include "lilo.h"
26 #include "common.h"
27 #include "cfg.h"
28 #include "device.h"
29 #include "geometry.h"
30 #include "partition.h"
31 #include "boot.h"
32 #include "loader.h"
33
34 #if __GLIBC__ < 2 || __GLIBC_MINOR__ < 1
35 #if defined(_syscall5) && defined(__NR__llseek)
36        
37        _syscall5(int,  _llseek,  unsigned int,  fd, unsigned int, hi,
38            unsigned int, lo, lloff_t *, res, unsigned int, wh);
39        int _llseek(unsigned int fd,  unsigned  int  offset_high,
40            unsigned  int  offset_low,  lloff_t * result, unsigned int whence);
41
42        lloff_t lseek64(unsigned int fd, lloff_t offs, unsigned int whence)
43        { lloff_t res;
44            return _llseek(fd, offs>>32, offs, &res, whence) < 0  ?
45                          (lloff_t)(-1) : res;
46        }
47
48 #else
49 /* last ditch attempt on small disks, and very old systems */
50 # warning "*****************************************"
51 # warning "***** no 64 bit lseek is available ******"
52 # warning "***** using 23 bit sector addresses *****"
53 # warning "*****************************************"
54 # define lseek64 lseek
55 #endif
56 #endif
57
58 static
59 int anywhere(unsigned char *buf, char *str)
60 {
61     int k, n;
62     char *s;
63     
64     k = strlen(str);
65     n = SECTOR_SIZE-k;
66     s = memchr(buf, *str, n);
67     while(s) {
68         if (!strncmp(s, str, k)) return 1;
69         s++;
70         n = SECTOR_SIZE - k - (int)(s-(char*)buf);
71         s = memchr(s, *str, n);
72     }
73     return 0;
74 }
75
76
77
78 /* identify partitions which would be destroyed if the boot block
79    is overwritten:
80    
81    known problems occur for:
82         XFS
83         NTFS
84         DOS FAT (relocation will fix)
85
86 */
87 int part_nowrite(char* device)
88 {
89     int fd;
90     BOOT_SECTOR bs;
91     
92     int ret=PTW_OKAY;   /* say ok, unless we recognize a problem partition */
93 if ( !(do_md_install && extra==X_MBR_ONLY) ) {
94     if ((fd = open(device, O_RDONLY)) < 0) pdie("part_nowrite check:");
95     if (read(fd, bs.sector, sizeof(bs)) != SECTOR_SIZE) pdie("part_nowrite: read:");
96     
97 /* check for XFS */
98     if (!strncmp("XFSB", (char*)bs.sector, 4)) ret=PTW_XFS;
99     
100 /* check for NTFS */
101     else if (   !strncmp("NTFS", bs.par_d.system, 4)
102                 || anywhere(bs.sector,"NTLDR")  ) ret=PTW_NTFS;
103
104 /* check for HPFS */
105     else if (   !strncmp("OS2", bs.par_d.system, 3)
106                 || anywhere(bs.sector,"OS2LDR")  ) ret=PTW_OS2;
107
108 /* check for DOS FAT */
109     else if (
110         (bs.par_d.bpb.media_descriptor >= 0xF8 || bs.par_d.bpb.media_descriptor == 0xF0)
111         && *(short*)bs.par_d.bpb.bytes_per_sector == SECTOR_SIZE
112         && (bs.par_d.bpb.number_of_FATs==1 || bs.par_d.bpb.number_of_FATs==2)
113     /* sectors_per_cluster is a power of 2, meaning only 1 bit is on */
114         && bs.par_d.bpb.sectors_per_cluster
115         && (bs.par_d.bpb.sectors_per_cluster & (bs.par_d.bpb.sectors_per_cluster-1))==0
116                                 ) {
117                 ret=PTW_DOS;
118 #if 0
119 /* this, it turns out is from Windows 98, so no caution here on NT */
120                 if (anywhere(bs.sector,"WINBOOT SYS")) ret+=PTW_NTFS;
121 #endif
122     }
123     
124 /* check for SWAP -- last check, as 'bs' is overwritten */
125     else if (*(int*)bs.sector == 0xFFFFFFFEU) {
126         if (lseek(fd, (PAGE_SIZE)-SECTOR_SIZE, SEEK_SET) != (PAGE_SIZE)-SECTOR_SIZE)
127             pdie("part_nowrite lseek:");
128         if (SECTOR_SIZE != read(fd, bs.sector, sizeof(bs)) ) pdie("part_nowrite swap check:");
129         if (!strncmp((char*)bs.sector+SECTOR_SIZE-10,"SWAPSPACE2",10)
130             || !strncmp((char*)bs.sector+SECTOR_SIZE-10,"SWAP-SPACE",10) ) ret=PTW_SWAP;
131     }
132
133 /* didn't recognize the superblock type, so assume it is okay */    
134     else ret=PTW_OKAY;
135     
136     close(fd);
137
138 } /* raid install with X_MBR_ONLY in use */
139     if (verbose>=6) printf("part_nowrite: %d\n", ret);    
140     
141     return ret;
142 }
143
144
145 void part_verify(int dev_nr,int type)
146 {
147     GEOMETRY geo;
148     DEVICE dev;
149     char backup_file[PATH_MAX+1];
150     int fd, bck_file, part, size, lin_3d, cyl;
151     unsigned int second, base;
152     struct partition part_table[PART_MAX];
153     int mask, i, pe, Linux, dos, mbr;
154     unsigned short boot_sig;
155     BOOT_PARAMS_1 bs;
156     
157     if (!has_partitions(dev_nr) || !(mask = P_MASK(dev_nr)) || !(dev_nr & mask)
158 #if 0
159      || (dev_nr & mask) > PART_MAX
160 #endif
161         ) return;
162
163     if (verbose >= 4) printf("part_verify:  dev_nr=%04x, type=%d\n", dev_nr, type);
164     geo_get(&geo,dev_nr & ~mask,-1,1);
165     fd = dev_open(&dev,dev_nr & ~mask,cfg_get_flag(cf_options,"fix-table")
166       && !test ? O_RDWR : O_RDONLY);
167     part = (pe = dev_nr & mask)-1;
168 #if 1
169     if (type) {
170         if (lseek(fd, 0L, SEEK_SET) != 0 ||
171             read(fd, &bs, sizeof(bs)) != sizeof(bs) ) pdie("bs read");
172         if (*(int*)bs.signature==EX_MAG_HL) mbr = bs.stage;
173         else mbr = STAGE_MBR;
174     } else mbr = STAGE_MBR;
175 #endif
176     if (lseek(fd, PART_TABLE_OFFSET, SEEK_SET) < 0) pdie("lseek partition table");
177     if (!(size = read(fd,(char *) part_table, sizeof(struct partition)*
178       PART_MAX))) die("Short read on partition table");
179     if (size < 0) pdie("read partition table");
180     if ( read(fd, &boot_sig, sizeof(boot_sig)) != sizeof(boot_sig)  ||
181         boot_sig != BOOT_SIGNATURE ) die("read boot signature failed");
182
183     if (verbose>=5) printf("part_verify:  part#=%d\n", pe);
184
185     second=base=0;
186     for (i=0; i<PART_MAX; i++) {
187         if (is_extd_part(part_table[i].sys_ind)) {
188             if (!base) base = part_table[i].start_sect;
189             else die("invalid partition table: second extended partition found");
190         }
191     }
192     i=5;
193     while (i<=pe && base) {
194         if (lseek64(fd, LLSECTORSIZE*(base+second) + PART_TABLE_OFFSET, SEEK_SET) < 0)
195             die("secondary lseek64 failed");
196         if (read(fd, part_table, sizeof(part_table)) != sizeof(part_table)) die("secondary read pt failed");
197         if ( read(fd, &boot_sig, sizeof(boot_sig)) != sizeof(boot_sig)  ||
198             boot_sig != BOOT_SIGNATURE ) die("read second boot signature failed");
199         if (is_extd_part(part_table[1].sys_ind)) second=part_table[1].start_sect;
200         else base = 0;
201         i++;
202         part=0;
203     }
204 #if 1
205     if (type && pe>0 && pe<=(mbr==STAGE_MBR2?63:PART_MAX)
206              && !(part_table[part].boot_ind&0x80) )
207         warn("Partition %d on %s is not marked Active.",
208                 pe, dev.name);
209 #endif
210     i = part_table[part].sys_ind;
211
212     Linux =   i == PART_LINUX_MINIX ||
213               i == PART_LINUX_NATIVE ||
214               i == PART_LINUX_LVM ||
215               is_extd_part(i);
216
217     i &= ~HIDDEN_OFF;
218     dos =     i == PART_DOS12 ||
219               i == PART_DOS16_SMALL ||
220               i == PART_DOS16_BIG ||
221               i == PART_FAT32 ||
222               i == PART_FAT32_LBA ||
223               i == PART_FAT16_LBA ||
224               i == PART_NTFS ||
225               i == PART_OS2_BOOTMGR ;
226
227     if (type && !Linux) {
228         warn("partition type 0x%02X"" on device 0x%04X is a dangerous place for\n"
229              "    a boot sector.%s",
230                         part_table[part].sys_ind, dev_nr,
231         dos ? "  A DOS/Windows/OS2 system may be rendered unbootable."
232                 "\n  The backup copy of this boot sector should be retained."
233                 : "" );
234 #if 0
235         if (!dos && !cfg_get_flag(cf_options,"ignore-table"))
236             die("You may proceed by using either '-P ignore' or 'ignore-table'");
237 #else
238         if (!yesno("\nProceed? ", 0)) exit(0);
239 #endif
240     }
241     cyl = part_table[part].cyl+((part_table[part].sector >> 6) << 8);
242     lin_3d = (part_table[part].sector & 63)-1+(part_table[part].head+
243       cyl*geo.heads)*geo.sectors;
244     if (pe <= PART_MAX &&
245             (lin_3d > part_table[part].start_sect || (lin_3d <
246             part_table[part].start_sect && cyl != BIOS_MAX_CYLS-1)) ) {
247         warn("Device 0x%04X: Inconsistent partition table, %d%s entry",
248           dev_nr & ~mask,part+1,!part ? "st" : part == 1 ? "nd" : part ==
249           2 ? "rd" : "th");
250         if (!nowarn)
251         fprintf(errstd,"  CHS address in PT:  %d:%d:%d  -->  LBA (%d)\n",
252                 cyl,
253                 part_table[part].head,
254                 part_table[part].sector & 63,
255                 lin_3d);
256         cyl = part_table[part].start_sect/geo.sectors/geo.heads;
257         if (!nowarn)
258         fprintf(errstd,"  LBA address in PT:  %d  -->  CHS (%d:%d:%d)\n",
259                 part_table[part].start_sect,
260                 cyl,
261                 part_table[part].head = (part_table[part].start_sect/geo.sectors) % geo.heads,
262                 part_table[part].sector = (part_table[part].start_sect % geo.sectors)+1
263                 );
264         if (cyl >= BIOS_MAX_CYLS) cyl = BIOS_MAX_CYLS-1;
265         part_table[part].sector |= (cyl >> 8)<<6;
266         part_table[part].cyl = cyl & 0xff;
267         if (!cfg_get_flag(cf_options,"fix-table") && !cfg_get_flag(cf_options,
268           "ignore-table")) die("Either FIX-TABLE or IGNORE-TABLE must be specified\n"
269                         "If not sure, first try IGNORE-TABLE (-P ignore)");
270         if (test || cfg_get_flag(cf_options,"ignore-table")) {
271             warn("The partition table is *NOT* being adjusted.");
272         } else {
273             sprintf(backup_file,BACKUP_DIR "/part.%04X",dev_nr & ~mask);
274             if ((bck_file = creat(backup_file,0644)) < 0)
275                 die("creat %s: %s",backup_file,strerror(errno));
276             if (!(size = write(bck_file,(char *) part_table,
277               sizeof(struct partition)*PART_MAX)))
278                 die("Short write on %s",backup_file);
279             if (size < 0) pdie(backup_file);
280             if (close(bck_file) < 0)
281                 die("close %s: %s",backup_file,strerror(errno));
282             if (verbose > 0)
283                 printf("Backup copy of partition table in %s\n",backup_file);
284             printf("Writing modified partition table to device 0x%04X\n",
285               dev_nr & ~mask);
286             if (lseek(fd,PART_TABLE_OFFSET,SEEK_SET) < 0)
287                 pdie("lseek partition table");
288             if (!(size = write(fd,(char *) part_table,sizeof(struct partition)*
289               PART_MAX))) die("Short write on partition table");
290             if (size < 0) pdie("write partition table");
291         }
292     }
293     dev_close(&dev);
294 }
295
296
297 CHANGE_RULE *change_rules = NULL;
298
299
300 void do_cr_reset(void)
301 {
302     CHANGE_RULE *next;
303
304     while (change_rules) {
305         next = change_rules->next;
306         free((char *) change_rules->type);
307         free(change_rules);
308         change_rules = next;
309     }
310 }
311
312
313 static unsigned char cvt_byte(const char *s)
314 {
315     char *end;
316     unsigned int value;
317
318     value = strtoul(s,&end,0);
319     if (value > 255 || *end) cfg_error("\"%s\" is not a byte value",s);
320     return value;
321 }
322
323
324 static void add_type(const char *type,int normal,int hidden)
325 {
326     CHANGE_RULE *rule;
327
328     for (rule = change_rules; rule; rule = rule->next)
329         if (!strcasecmp(rule->type,type))
330             die("Duplicate type name: \"%s\"",type);
331     rule = alloc_t(CHANGE_RULE);
332     rule->type = stralloc(type);
333     rule->normal = normal == -1 ? hidden ^ HIDDEN_OFF : normal;
334     rule->hidden = hidden == -1 ? normal ^ HIDDEN_OFF : hidden;
335     rule->next = change_rules;
336     change_rules = rule;
337 }
338
339
340 void do_cr_type(void)
341 {
342     const char *normal,*hidden;
343
344     cfg_init(cf_change_rule);
345     (void) cfg_parse(cf_change_rule);
346     normal = cfg_get_strg(cf_change_rule,"normal");
347     hidden = cfg_get_strg(cf_change_rule,"hidden");
348     if (normal)
349         add_type(cfg_get_strg(cf_change_rules,"type"),cvt_byte(normal),
350           hidden ? cvt_byte(hidden) : -1);
351     else {
352         if (!hidden)
353             cfg_error("At least one of NORMAL and HIDDEN must be present");
354         add_type(cfg_get_strg(cf_change_rules,"type"),cvt_byte(hidden),-1);
355     }
356     cfg_unset(cf_change_rules,"type");
357 }
358
359
360 void do_cr(void)
361 {
362     cfg_init(cf_change_rules);
363     (void) cfg_parse(cf_change_rules);
364 }
365
366
367 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
368
369 /*
370  * Rule format:
371  *
372  * +------+------+------+------+
373  * |drive |offset|expect| set  |
374  * +------+------+------+------+
375  *     0      1      2      3
376  */
377
378 static void add_rule(unsigned char bios,unsigned char offset,
379   unsigned char expect,unsigned char set)
380 {
381     int i;
382
383     if (curr_prt_map == PRTMAP_SIZE)
384         cfg_error("Too many change rules (more than %d)",PRTMAP_SIZE);
385     if (verbose >= 3)
386         printf("  Adding rule: disk 0x%02x, offset 0x%x, 0x%02x -> 0x%02x\n",
387             bios,PART_TABLE_OFFSET+offset,expect,set);
388     prt_map[curr_prt_map] = (set << 24) | (expect << 16) | (offset << 8) | bios;
389     for (i = 0; i < curr_prt_map; i++) {
390         if (prt_map[i] == prt_map[curr_prt_map])
391           die("Repeated rule: disk 0x%02x, offset 0x%x, 0x%02x -> 0x%02x",
392             bios,PART_TABLE_OFFSET+offset,expect,set);
393         if ((prt_map[i] & 0xffff) == ((offset << 8) | bios) &&
394           (prt_map[i] >> 24) == expect)
395             die("Redundant rule: disk 0x%02x, offset 0x%x: 0x%02x -> 0x%02x "
396               "-> 0x%02x",bios,PART_TABLE_OFFSET+offset,
397              (prt_map[i] >> 16) & 0xff,expect,set);
398     }
399     curr_prt_map++;
400 }
401
402 #endif
403
404
405 static int has_partition;
406
407 static CHANGE_RULE *may_change(unsigned char sys_ind)
408 {
409     CHANGE_RULE *cr = change_rules;
410     
411     while (cr) {
412         if (cr->normal == sys_ind || cr->hidden == sys_ind) return cr;
413         cr = cr->next;
414     }
415     return NULL;
416 }
417
418
419 void do_cr_auto(void)
420 {
421     GEOMETRY geo;
422     struct stat st;
423     char *table, *table2, *other;
424     int partition, pfd, i, j;
425     struct partition part_table[PART_MAX];
426
427     if (autoauto) has_partition = 0;
428     other = identify ? cfg_get_strg(cf_identify, "other")
429                      : cfg_get_strg(cf_top, "other");
430     if (verbose > 4) printf("do_cr_auto: other=%s has_partition=%d\n",
431         other, has_partition);
432 #if 0
433     i = other[strlen(other)-1] - '0';
434     if (i>PART_MAX || i<1) return;
435 #endif
436     table = cfg_get_strg(cf_other,"table");
437     table2 = boot_mbr(other, 1);        /* get possible default */
438     if (!table) table = table2;
439     
440     if (!table && autoauto) return;
441     if (table && autoauto && !table2) cfg_error("TABLE may not be specified");
442    
443     if (has_partition) cfg_error("AUTOMATIC must be before PARTITION");
444     if (!table) cfg_error("TABLE must be set to use AUTOMATIC");
445     /*    
446      */
447     if (stat(table,&st) < 0) die("stat %s: %s",table,strerror(errno));
448     geo_get(&geo,st.st_rdev & D_MASK(st.st_rdev),-1,1);
449     partition = st.st_rdev & P_MASK(st.st_rdev);
450     if (!S_ISBLK(st.st_mode) || partition)
451         cfg_error("\"%s\" doesn't contain a primary partition table",table);
452     pfd = open(table, O_RDONLY);
453     if (pfd<0) die("Cannot open %s", table);
454     if (lseek(pfd, PART_TABLE_OFFSET, SEEK_SET)!=PART_TABLE_OFFSET)
455         die("Cannot seek to partition table of %s", table);
456     if (read(pfd, part_table, sizeof(part_table))!=sizeof(part_table))
457         die("Cannot read Partition Table of %s", table);
458     close(pfd);
459     partition = other[strlen(other)-1] - '0';
460     if (verbose > 3) printf("partition = %d\n", partition);
461     for (j=i=0; i<PART_MAX; i++)
462         if (may_change(part_table[i].sys_ind)) j++;
463
464     if (j>1)
465 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
466     for (i=0; i<PART_MAX; i++) {
467         CHANGE_RULE *cr;
468         if ((cr=may_change(part_table[i].sys_ind))) {
469             j = i*PARTITION_ENTRY + PART_TYPE_ENT_OFF;
470             if (autoauto) {
471                 warn("CHANGE AUTOMATIC assumed after \"other=%s\"", other);
472                 autoauto = 0;  /* suppress further warnings */
473             }    
474             if (i == partition-1)
475                 add_rule(geo.device, j, cr->hidden, cr->normal);
476             else
477                 add_rule(geo.device, j, cr->normal, cr->hidden);
478         }
479     }
480 #else
481     warn("This LILO is compiled without REWRITE_TABLE;\n"
482        "   unable to generate CHANGE/AUTOMATIC change-rules");
483 #endif
484 }
485
486
487
488 void do_cr_part(void)
489 {
490     GEOMETRY geo;
491     struct stat st;
492     char *tmp;
493     int partition,part_base;
494
495     tmp = cfg_get_strg(cf_change,"partition");
496     if (stat(tmp,&st) < 0) die("stat %s: %s",tmp,strerror(errno));
497     geo_get(&geo,st.st_rdev & D_MASK(st.st_rdev),-1,1);
498     partition = st.st_rdev & P_MASK(st.st_rdev);
499     if (!S_ISBLK(st.st_mode) || !partition || partition > PART_MAX)
500         cfg_error("\"%s\" isn't a primary partition",tmp);
501     part_base = (partition-1)*PARTITION_ENTRY;
502     has_partition = 1;   
503     cfg_init(cf_change_dsc);
504     (void) cfg_parse(cf_change_dsc);
505     tmp = cfg_get_strg(cf_change_dsc,"set");
506     if (tmp) {
507 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
508         CHANGE_RULE *walk;
509         char *here;
510         int hidden;
511
512         here = (void*)NULL;     /* quiet GCC */
513         hidden = 0;             /* quiet GCC */
514         if (strlen(tmp) < 7 || !(here = strrchr(tmp,'_')) ||
515           ((hidden = strcasecmp(here+1,"normal")) &&
516           strcasecmp(here+1,"hidden")))
517             cfg_error("Type name must end with _normal or _hidden");
518         *here = 0;
519         for (walk = change_rules; walk; walk = walk->next)
520             if (!strcasecmp(walk->type,tmp)) break;
521         if (!walk) cfg_error("Unrecognized type name");
522         add_rule(geo.device,part_base+PART_TYPE_ENT_OFF,hidden ? walk->normal :
523           walk->hidden,hidden ? walk->hidden : walk->normal);
524 #else
525         die("This LILO is compiled without REWRITE_TABLE and doesn't support "
526           "the SET option");
527 #endif
528     }
529     if (cfg_get_flag(cf_change_dsc,"activate")) {
530 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
531         add_rule(geo.device,part_base+PART_ACT_ENT_OFF,0x00,0x80);
532         if (cfg_get_flag(cf_change_dsc,"deactivate"))
533             cfg_error("ACTIVATE and DEACTIVATE are incompatible");
534 #else
535         die("This LILO is compiled without REWRITE_TABLE and doesn't support "
536           "the ACTIVATE option");
537 #endif
538     }
539     if (cfg_get_flag(cf_change_dsc,"deactivate"))
540 #if defined(LCF_REWRITE_TABLE) && !defined(LCF_READONLY)
541         add_rule(geo.device,part_base+PART_ACT_ENT_OFF,0x80,0x00);
542 #else
543         die("This LILO is compiled without REWRITE_TABLE and doesn't support "
544           "the DEACTIVATE option");
545 #endif
546     cfg_unset(cf_change,"partition");
547 }
548
549
550 void do_change(void)
551 {
552     cfg_init(cf_change);
553     has_partition = 0;
554     (void) cfg_parse(cf_change);
555 }
556
557
558 void preload_types(void)
559 {
560 #if 0 /* don't know if it makes sense to add these too */
561     add_type("Netware", 0x64, 0x74);
562     add_type("OS2_BM", 0x0a, 0x1a);
563 #endif
564     add_type("OS2_HPFS", 0x07, 0x17);
565
566     add_type("FAT16_lba", PART_FAT16_LBA, -1);
567     add_type("FAT32_lba", PART_FAT32_LBA, -1);
568     add_type("FAT32", PART_FAT32, -1);
569     add_type("NTFS", PART_NTFS, -1);
570     add_type("DOS16_big", PART_DOS16_BIG, -1);
571     add_type("DOS16_small", PART_DOS16_SMALL, -1);
572     add_type("DOS12", PART_DOS12, -1);
573 }
574
575
576
577 #define PART_BEGIN      0x1be
578 #define PART_NUM        4
579 #define PART_SIZE       16
580 #define PART_ACTIVE     0x80
581 #define PART_INACTIVE   0
582
583
584 void do_activate(char *part, char *which)
585 {
586 #if 1
587     int part_max, count, number, fd;
588     struct partition pt [PART_MAX_MAX+1];
589     long long daddr [PART_MAX_MAX+1];
590     int modify=0;
591     
592     part_max = read_partitions(part, extended_pt ? PART_MAX_MAX : 0,
593                                         NULL, pt, daddr);
594 /*    printf("part_max=%d\n", part_max); */
595     if (!which) {       /* one argument: display active partition */
596         for (count=0; count < part_max; count++) {
597             if (pt[count].boot_ind) {
598                 printf("%s%d\n",part,count+1);
599                 exit(0);
600             }
601         }
602         printf("No active partition found on %s\n",part);
603         exit(0);
604     }
605     number = to_number(which);
606     if (number < 0 || number > part_max)
607         die("%s: not a valid partition number (1-%d)",which,part_max);
608
609     if (number && !pt[number-1].sys_ind) die("Cannot activate an empty partition");
610     number--;   /* we are zero-based from here on */
611
612     if ((fd = open(part, O_RDWR)) < 0)
613         die("open %s: %s",part,strerror(errno));
614     for (count=0; count<part_max; count++) {
615         unsigned char flag = count==number ? PART_ACTIVE : PART_INACTIVE;
616         if (pt[count].sys_ind && pt[count].boot_ind != flag) {
617             pt[count].boot_ind = flag;
618             printf("pt[%d] -> %2x\n", count+1, (int)flag);
619             if (lseek64(fd, daddr[count], SEEK_SET) < 0) die("PT lseek64 failed");
620             if (!test)
621             if (write(fd, &pt[count], sizeof(pt[0])) != sizeof(pt[0]) )
622                 die("PT write failure");
623             modify++;
624         }
625     }
626     close(fd);
627     if (modify)
628         printf("The partition table has%s been updated.\n", test ? " *NOT*" : "");
629     else
630         printf("No partition table modifications are needed.\n");
631 #else
632     struct stat st;
633     int fd,number,count;
634     unsigned char flag, ptype;
635
636     if ((fd = open(part, !which ? O_RDONLY : O_RDWR)) < 0)
637         die("open %s: %s",part,strerror(errno));
638     if (fstat(fd,&st) < 0) die("stat %s: %s",part,strerror(errno));
639     if (!S_ISBLK(st.st_mode)) die("%s: not a block device",part);
640     if (verbose >= 1) {
641        printf("st.st_dev = %04X, st.st_rdev = %04X\n",
642                                 (int)st.st_dev, (int)st.st_rdev);
643     }
644     if ((st.st_rdev & has_partitions(st.st_rdev)) != st.st_rdev)
645         die("%s is not a master device with a primary partition table", part);
646     if (!which) {       /* one argument: display active partition */
647         for (count = 1; count <= PART_NUM; count++) {
648             if (lseek(fd,PART_BEGIN+(count-1)*PART_SIZE,SEEK_SET) < 0)
649                 die("lseek: %s",strerror(errno));
650             if (read(fd,&flag,1) != 1) die("read: %s",strerror(errno));
651             if (flag == PART_ACTIVE) {
652                 printf("%s%d\n",part,count);
653                 exit(0);
654             }
655         }
656         die("No active partition found on %s",part);
657     }
658     number = to_number(which);
659     if (number < 0 || number > 4)
660         die("%s: not a valid partition number (1-4)",which);
661     for (count = 1; count <= PART_NUM; count++) {
662         if (lseek(fd,PART_BEGIN+(count-1)*PART_SIZE+4,SEEK_SET) < 0)
663             die("lseek: %s",strerror(errno));
664         if (read(fd,&ptype,1) != 1) die("read: %s",strerror(errno));
665         if (count == number && ptype==0) die("Cannot activate an empty partition");
666     }
667     if (test) {
668         printf("The partition table of  %s  has *NOT* been updated\n",part);
669     }
670     else for (count = 1; count <= PART_NUM; count++) {
671         if (lseek(fd,PART_BEGIN+(count-1)*PART_SIZE,SEEK_SET) < 0)
672             die("lseek: %s",strerror(errno));
673         flag = count == number ? PART_ACTIVE : PART_INACTIVE;
674         if (write(fd,&flag,1) != 1) die("write: %s",strerror(errno));
675     }
676 #endif
677     exit(0);
678 }
679
680
681 void do_install_mbr(char *part, char *what)
682 {
683     int fd, i;
684 #ifndef LCF_BUILTIN
685     int nfd;
686 #endif
687     struct stat st;
688     BOOT_SECTOR buf;
689     char *cp;
690     
691     if (!what) what = DFL_MBR;
692     extended_pt |= !!strchr(what,'x') || !!strchr(what,'X') || !!strchr(what,'2');
693     if ((fd=open(part,O_RDWR)) < 0) die("Cannot open %s: %s", part,strerror(errno));
694     if (fstat(fd,&st) < 0) die("stat: %s : %s", part,strerror(errno));
695     if (!S_ISBLK(st.st_mode) && !force_fs) die("%s not a block device",part);
696     if (st.st_rdev != (st.st_rdev & has_partitions(st.st_rdev)))
697         die("%s is not a master device with a primary parition table",part);
698     if (read(fd,&buf,SECTOR_SIZE) != SECTOR_SIZE) die("read %s: %s",part, strerror(errno));
699
700     cp = cfg_get_strg(cf_options,"force-backup");
701     i = (cp!=NULL);
702     if (!cp) cp = cfg_get_strg(cf_options,"backup");
703     make_backup(cp, i, &buf, st.st_rdev, part);
704     
705 #ifndef LCF_BUILTIN    
706     if ((nfd=open(what,O_RDONLY)) < 0) die("Cannot open %s: %s",what,strerror(errno));
707     if (read(nfd,buf,MAX_BOOT_SIZE) != MAX_BOOT_SIZE) die("read %s: %s",what,strerror(errno));
708 #else
709     memcpy(&buf, extended_pt ? Mbr2.data : Mbr.data, MAX_BOOT_SIZE);
710 #endif    
711     buf.boot.boot_ind = BOOT_SIGNATURE;
712     if (zflag) {
713         buf.boot.mbz =
714         buf.boot.marker =
715         buf.boot.volume_id = 0;
716 #if BETA_TEST || 1
717         if ((cp=cfg_get_strg(cf_options,RAID_EXTRA_BOOT))) {
718             buf.boot.volume_id = strtoul(cp, NULL, 16);
719         }
720 #endif
721     } else if (buf.boot.volume_id == 0) {
722 #if 0
723         i = st.st_rdev;
724         i %= PRIME;             /* modulo a prime number; eg, 2551, 9091 */
725         i += SMALL_PRIME;
726         srand(time(NULL));      /* seed the random number generator */
727         while (i--) rand();
728         *(int*)&buf[PART_TABLE_OFFSET - 6] = rand();  /* insert serial number */
729         if (*(short*)&buf[PART_TABLE_OFFSET - 2] == 0)
730             *(short*)&buf[PART_TABLE_OFFSET - 2] = MAGIC_SERIAL;
731 #else
732         buf.boot.volume_id = new_serial(st.st_rdev);
733         buf.boot.marker = MAGIC_SERIAL;
734 #endif
735     }
736     
737     if (lseek(fd,0,SEEK_SET) != 0) die("seek %s; %s", part, strerror(errno));
738     if (!test) {
739         if (write(fd,&buf,SECTOR_SIZE) != SECTOR_SIZE)
740                 die("write %s: %s",part,strerror(errno));
741     }
742     close(fd);
743 #ifndef LCF_BUILTIN
744     close(nfd);
745 #endif
746     printf("The Master Boot Record of  %s  has %sbeen updated.\n", part, test ? "*NOT* " : "");
747     exit(0);
748 }
749
750
751
752 /* partition table read */
753 int read_partitions(char *part, int max, int *volid,
754                 struct partition *p, long long *where)
755 {
756     int fd, i;
757     unsigned int second, base;
758     unsigned short boot_sig;
759     struct partition pt[PART_MAX];
760     BOOT_PARAMS_1 hdr;
761     struct stat st;
762     long long daddr;
763
764     if ((fd=open(part,O_RDONLY))<0) die("Cannot open '%s'", part);
765     if (fstat(fd,&st)<0) die("Cannot fstat '%s'", part);
766     if (!S_ISBLK(st.st_mode)) die("Not a block device '%s'", part);
767     i = st.st_rdev;
768     if (!has_partitions(i) || (P_MASK(i)&i) )
769         die("Not a device with partitions '%s'", part);
770
771     if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) die("read header");
772     if (!strncmp(hdr.signature, "LILO", 4) && hdr.stage == STAGE_MBR2 &&
773         max == 0) max = PART_MAX_MAX;
774     else if (max == 0) max = PART_MAX;
775     if (lseek(fd, PART_TABLE_OFFSET, SEEK_SET)<0) die("lseek failed");
776     if (read(fd, pt, sizeof(pt)) != sizeof(pt)) die("read pt failed");
777     if ( read(fd, &boot_sig, sizeof(boot_sig)) != sizeof(boot_sig)  ||
778         boot_sig != BOOT_SIGNATURE ) die("read boot signature failed");
779     if (volid) {
780         if (lseek(fd, MAX_BOOT_SIZE+2, SEEK_SET)<0) die("lseek vol-ID failed");
781         if (read(fd, volid, sizeof(*volid)) != sizeof(*volid))
782             die("read vol-ID failed");
783 /*      printf(" vol-ID: %08X\n", second);      */
784     }
785 /*    printf("%s\n", phead); */
786     second=base=0;
787     if (max>=4)
788     for (i=0; i<PART_MAX; i++) {
789 /*      print_pt(i+1, pt[i]); */
790         if (is_extd_part(pt[i].sys_ind)) {
791             if (!base) base = pt[i].start_sect;
792             else die("invalid partition table: second extended partition found");
793         }
794         if (where) *where++ = PART_TABLE_OFFSET + i*sizeof(*p);
795         *p++ = pt[i];
796     }
797
798     max -= (i=4);
799     
800     if (max>0)
801     while (base) {
802         daddr = LLSECTORSIZE*(base+second) + PART_TABLE_OFFSET;
803         if (lseek64(fd, daddr, SEEK_SET) < 0)
804             die("secondary lseek64 failed");
805         if (read(fd, pt, sizeof(pt)) != sizeof(pt)) die("secondary read pt failed");
806         if ( read(fd, &boot_sig, sizeof(boot_sig)) != sizeof(boot_sig)  ||
807             boot_sig != BOOT_SIGNATURE ) die("read second boot signature failed");
808 /*      print_pt(i++, pt[0]); */
809         if (is_extd_part(pt[1].sys_ind)) second=pt[1].start_sect;
810         else base = 0;
811         if (max-- > 0) {
812             *p++ = pt[0];
813             if (where) *where++ = daddr;
814             i++;
815         }
816     }
817     if (max > 0) {
818         p->sys_ind = 0;
819         if (where) *where = 0;
820     }
821         
822     close(fd);
823     
824     return i;
825 }
826