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