c583c0df141f91912cf2a537634161d78af43067
[rrq/maintain_lilo.git] / src / map.c
1 /* map.c  -  Map file creation */
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
13 #define _GNU_SOURCE
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <errno.h>
20
21 #include "lilo.h"
22 #include "common.h"
23 #if !__MSDOS__
24 #include "geometry.h"
25 #endif /* !__MSDOS__ */
26 #include "map.h"
27
28 #if !__MSDOS__
29 typedef struct _map_entry {
30     SECTOR_ADDR addr;
31     struct _map_entry *next;
32 } MAP_ENTRY;
33
34
35 static MAP_ENTRY *map,*last;
36 static SECTOR_ADDR zero_addr;
37 static int map_file;
38 #endif /* !__MSDOS__ */
39
40 void map_patch_first(char *name,char *str)
41 {
42     DESCR_SECTORS descrs;
43     int fd,size,image;
44     unsigned short magic;
45     char *start,*end;
46
47     if (strlen(str) >= SECTOR_SIZE-2)
48         die("map_patch_first: String is too long");
49     if ((fd = open(name,O_RDWR)) < 0) die("open %s: %s",name,strerror(errno));
50     if (lseek(fd,(off_t)SECTOR_SIZE,SEEK_SET) < 0) die("lseek %s: %s",name,strerror(errno));
51     if (read(fd,(char *) &descrs,sizeof(descrs)) != sizeof(descrs))
52         die("read %s: %s",name,strerror(errno));
53     for (start = str; *start && *start == ' '; start++);
54     if (*start) {
55         for (end = start; *end && *end != ' '; end++);
56         if (*end) *end = 0;
57         else end = NULL;
58         for (image = 0; image < MAX_IMAGES; image++)
59 #ifdef LCF_IGNORECASE
60             if (!strcasecmp(descrs.d.descr[image].name,start)) break;
61 #else
62             if (!strcmp(descrs.d.descr[image].name,start)) break;
63 #endif
64         if (image == MAX_IMAGES) die("No image \"%s\" is defined",start);
65         if (end) *end = ' ';
66     }
67     if (lseek(fd,(off_t)0,SEEK_SET) < 0) die("lseek %s: %s",name,strerror(errno));
68     magic = *str ? DC_MAGIC : 0;
69     if ((size = write(fd,(char *) &magic,2)) < 0)
70         die("write %s: %s",name,strerror(errno));
71     if (size != 2) die("map_patch_first: Bad write ?!?");
72     if ((size = write(fd,str,strlen(str)+1)) < 0)
73         die("write %s: %s",name,strerror(errno));
74     if (size != strlen(str)+1) die("map_patch_first: Bad write ?!?");
75     if (close(fd) < 0) die("close %s: %s",name,strerror(errno));
76 }
77
78 #if !__MSDOS__
79
80 static GEOMETRY map_geo;
81
82 void map_create(char *name)
83 {
84     char buffer[SECTOR_SIZE];
85     int fd, i;
86
87     if ((fd = creat(name,0600)) < 0) die("creat %s: %s",name,strerror(errno));
88     (void) close(fd);
89     memset(buffer,0,SECTOR_SIZE);
90     *(unsigned short *) buffer = DC_MGOFF;
91     map_file = geo_open(&map_geo,name,O_RDWR);
92     bios_map = map_geo.device;  /* set device code of map file */
93     if (do_md_install) {
94         struct stat st;
95         if(fstat(map_file,&st)) die("map_create: cannot fstat map file");
96         if (verbose >= 2)
97             printf("map_create:  boot=%04X  map=%04X\n", 
98                                         boot_dev_nr, (int)st.st_dev);
99         if (boot_dev_nr != st.st_dev  &&  extra != X_MBR_ONLY) {
100             die("map file must be on the boot RAID partition");
101         }
102     }
103
104 /* write default command line, descriptor table, zero sector */
105
106     for (i=0; i<MAX_DESCR_SECS+2; i++) {
107         if (write(map_file,buffer,SECTOR_SIZE) != SECTOR_SIZE)
108             die("write %s: %s",name,strerror(errno));
109         *(unsigned short *) buffer = 0;
110     }
111     if (!geo_comp_addr(&map_geo,SECTOR_SIZE*(MAX_DESCR_SECS+1),&zero_addr))
112         die("Hole found in map file (zero sector)");
113 }
114
115
116 void map_descrs(DESCR_SECTORS *descr, SECTOR_ADDR* addr, SECTOR_ADDR* dflcmd)
117 {
118     struct stat st;
119     int i, pos;
120     off_t last;
121
122     descr->l.checksum =
123                 crc32(descr->sector, sizeof((*descr).l.sector), CRC_POLY1);
124
125     last = lseek(map_file, 0L, SEEK_END);       /* save final position */
126     pos = SECTOR_SIZE;
127     if (lseek(map_file, pos, SEEK_SET) < 0) pdie("lseek map file");
128     if (write(map_file,(char *) descr, SECTOR_SIZE*MAX_DESCR_SECS) != SECTOR_SIZE*MAX_DESCR_SECS)
129         pdie("write map file");
130
131     for (i=0; i<MAX_DESCR_SECS; i++) {
132         if (!geo_comp_addr(&map_geo,pos,addr))
133             die("Hole found in map file (descr. sector %d)", i);
134         addr++;
135         pos += SECTOR_SIZE;
136     }
137
138     if (!geo_comp_addr(&map_geo,0,dflcmd))
139         die("Hole found in map file (default command line)");
140
141     if (verbose >= 2) {
142         if (fstat(map_file,&st) < 0) pdie("fstat map file");
143         printf("Map file size: %d bytes.\n",(int) st.st_size);
144     }
145     if (last!=lseek(map_file, last, SEEK_SET)) pdie("lseek map file to end");
146 }
147
148
149 void map_close(BOOT_PARAMS_2 *param2, off_t here)
150 {
151     if (param2) {
152         if (lseek(map_file, here, SEEK_SET) != here)
153             die("map_close: lseek");
154         if (write(map_file,(void*)param2,sizeof(BOOT_PARAMS_2))!=sizeof(BOOT_PARAMS_2))
155             die("map_close: write");
156     }
157     geo_close(&map_geo);
158 }
159
160
161 void map_register(SECTOR_ADDR *addr)
162 {
163     MAP_ENTRY *new;
164
165     new = alloc_t(MAP_ENTRY);
166     new->addr = *addr;
167     new->next = NULL;
168     if (last) last->next = new;
169     else map = new;
170     last = new;
171 }
172
173
174 void map_add_sector(void *sector)
175 {
176     int here;
177     SECTOR_ADDR addr;
178
179     if ((here = lseek(map_file,0L,SEEK_CUR)) < 0) pdie("lseek map file");
180     if (write(map_file,sector,SECTOR_SIZE) != SECTOR_SIZE)
181         pdie("write map file");
182     if (!geo_comp_addr(&map_geo,here,&addr))
183         die("Hole found in map file (app. sector)");
184     map_register(&addr);
185 }
186
187
188 void map_begin_section(void)
189 {
190     map = last = NULL;
191 }
192
193
194 void map_add(GEOMETRY *geo,int from,int num_sect)
195 {
196     int count;
197     SECTOR_ADDR addr;
198
199     for (count = 0; count < num_sect; count++) {
200         if (geo_comp_addr(geo,SECTOR_SIZE*(count+from),&addr))
201             map_register(&addr);
202         else {
203             map_register(&zero_addr);
204             if (verbose > 3) printf("Covering hole at sector %d.\n",count);
205         }
206     }
207 }
208
209
210 void map_add_zero(void)
211 {
212     map_register(&zero_addr);
213 }
214
215
216 static void map_compact(int dont_compact)
217 {
218     MAP_ENTRY *walk,*next;
219     int count, removed, offset, adj, hinib, noffset, maxcount;
220
221     removed = 0;
222     hinib = 257;
223     maxcount = lba32 ? 127 : 128;  /* JRC: max LBA transfer is 127 sectors,
224               per the EDD spec, v1.1, not 128 (unfortunately) */
225 /* JRC: for testing the hinib save: */
226 #ifdef DEBUG
227         maxcount = lba32 ? 3 : maxcount;
228 #endif
229     walk = map;
230     for (count = 0; walk && count < dont_compact; count++) walk = walk->next;
231     offset = 0;
232     noffset = 0;
233     while (walk && walk->next) {
234         adj = ((walk->addr.device ^ walk->next->addr.device) & ~LBA32_NOCOUNT) == 0;
235         if (adj && (walk->addr.device & LBA32_FLAG)) {
236             if ((walk->addr.device & LBA32_NOCOUNT)==0) {
237                 if ( (adj = (hinib==walk->next->addr.num_sect)) ) {
238                     walk->next->addr.num_sect = 1;
239                     walk->next->addr.device &= ~LBA32_NOCOUNT;
240                 }
241             }
242             else {
243                 adj = 0;
244                 hinib = walk->addr.num_sect;
245                 if ((walk->next->addr.device&LBA32_NOCOUNT) &&
246                     (walk->next->addr.num_sect == hinib)) {
247                    walk->next->addr.num_sect = 1;
248                    walk->next->addr.device &= ~LBA32_NOCOUNT;
249                 }
250             }
251         }
252         if (adj && walk->addr.device & (LINEAR_FLAG|LBA32_FLAG))
253             adj = ((walk->addr.head << 16) | (walk->addr.track << 8) |
254               walk->addr.sector)+walk->addr.num_sect == ((walk->next->addr.head
255               << 16) | (walk->next->addr.track << 8) | walk->next->addr.sector);
256         else adj = adj && walk->addr.track == walk->next->addr.track &&
257               walk->addr.head == walk->next->addr.head &&
258               walk->addr.sector+walk->addr.num_sect == walk->next->addr.sector;
259         noffset += SECTOR_SIZE;
260         adj = adj && (offset>>16 == noffset>>16) &&
261                      (walk->addr.num_sect < maxcount);    
262         if (!adj) {
263             offset = noffset;
264             walk = walk->next;
265         }
266         else {
267             walk->addr.num_sect++;
268             next = walk->next->next;
269             free(walk->next);
270             removed++;
271             walk->next = next;
272         }
273     }
274     if (verbose > 1)
275         printf("Compaction removed %d BIOS call%s.\n",removed,removed == 1 ?
276           "" : "s");
277 }
278
279
280 static void map_alloc_page(int offset,SECTOR_ADDR *addr)
281 {
282     int here;
283
284     if ((here = lseek(map_file,offset,SEEK_CUR)) < 0) pdie("lseek map file");
285     if (write(map_file,"",1) != 1) pdie("write map file");
286     if (fdatasync(map_file)) pdie("fdatasync map file");
287     if (!geo_comp_addr(&map_geo,here,addr))
288         die("Hole found in map file (alloc_page)");
289     if (lseek(map_file,-offset-1,SEEK_CUR) < 0) pdie("lseek map file");
290 }
291
292
293 int map_end_section(SECTOR_ADDR *addr,int dont_compact)
294 {
295     int first,offset,sectors;
296     char buffer[SECTOR_SIZE];
297     MAP_ENTRY *walk,*next;
298     int hinib;
299
300     first = 1;
301     memset(buffer,0,SECTOR_SIZE);
302     offset = sectors = 0;
303     if (compact) map_compact(dont_compact);
304     if (!map) die("Empty map section");
305     hinib = 0;
306     for (walk = map; walk; walk = next) {
307         next = walk->next;
308         if (verbose > 3) {
309             if ((walk->addr.device&LBA32_FLAG) && (walk->addr.device&LBA32_NOCOUNT)) hinib = walk->addr.num_sect;
310             printf("  Mapped AL=0x%02x CX=0x%04x DX=0x%04x",walk->addr.num_sect,
311               (walk->addr.track << 8) | walk->addr.sector,(walk->addr.head << 8)
312               | walk->addr.device);
313             if (linear||lba32)
314                 printf(", %s=%d",
315                   lba32 ? "LBA" : "linear",
316                   (walk->addr.head << 16) | (walk->addr.track << 8) | walk->addr.sector | hinib<<24);
317             printf("\n");
318         }
319         if (first) {
320             first = 0;
321             map_alloc_page(0,addr);
322         }
323         if (offset+sizeof(SECTOR_ADDR)*2 > SECTOR_SIZE) {
324             map_alloc_page(SECTOR_SIZE,(SECTOR_ADDR *) (buffer+offset));
325             if (write(map_file,buffer,SECTOR_SIZE) != SECTOR_SIZE)
326                 pdie("write map file");
327             memset(buffer,0,SECTOR_SIZE);
328             offset = 0;
329         }
330         memcpy(buffer+offset,&walk->addr,sizeof(SECTOR_ADDR));
331         offset += sizeof(SECTOR_ADDR);
332         sectors += (walk->addr.device&LBA32_FLAG) && (walk->addr.device&LBA32_NOCOUNT)
333                 ? 1 : walk->addr.num_sect;
334         free(walk);
335     }
336     if (offset)
337         if (write(map_file,buffer,SECTOR_SIZE) != SECTOR_SIZE)
338             pdie("write map file");
339     return sectors;
340 }
341
342 #ifdef LCF_FIRST6
343 static int sa6_from_sa(SECTOR_ADDR6 *sa6, SECTOR_ADDR *sa)
344 {
345     static unsigned char hinib = 0;
346     int count;
347     int flags;
348     int sector;
349     
350     sa6->device = sa->device & DEV_MASK;
351     flags = sa6->flags = sa->device & ~DEV_MASK;
352     count = sa->num_sect;
353     
354     if ((flags & (LBA32_FLAG|LINEAR_FLAG)) == 0) {
355 /* pure geometric addressing */
356         sector = *(unsigned int*)&(sa->sector);
357     }
358     else {
359         if (flags & LBA32_FLAG) {
360 /* pure lba32 addressing */
361             if (flags & LBA32_NOCOUNT) {
362                 hinib = count;
363                 count = 1;
364             }
365         sector = hinib;
366         } /* linear addressing */
367         else  sector = 0;
368
369         sector = ((sector<<8 | sa->head)<<8 | sa->track)<<8 | sa->sector;
370     }
371     sa6->sector = sector;
372     
373     return sector;
374 }
375
376 #endif
377
378
379 #ifdef LCF_FIRST6
380 int map_write(SECTOR_ADDR *list,int max_len,int terminate,int sa6)
381 #else
382 int map_write(SECTOR_ADDR *list,int max_len,int terminate)
383 #endif
384 {
385     MAP_ENTRY *walk,*next;
386     int sectors;
387 #ifdef LCF_FIRST6
388     SECTOR_ADDR6 sa6tem, *list6 = (void*)list;
389     unsigned int *list4 = (void*)list;
390 #endif
391
392     sectors = 0;
393     for (walk = map; walk; walk = next) {
394         next = walk->next;
395         if (--max_len < (terminate ? 1 : 0)) die("Map segment is too big.");
396 #ifdef LCF_FIRST6
397         if (sa6) {
398             (void)sa6_from_sa(&sa6tem, &(walk->addr));
399             if (sa6==2) *list4++ = sa6tem.sector;
400             else *list6++ = sa6tem;
401         }
402         else
403 #endif
404         *list++ = walk->addr;
405
406         free(walk);
407         sectors++;
408     }
409     
410     if (terminate) {
411 #ifdef LCF_FIRST6
412         if (sa6==2) *list4 = 0;
413         else if (sa6) memset(list6, 0, sizeof(SECTOR_ADDR6));
414         else
415 #endif
416         memset(list,0,sizeof(SECTOR_ADDR));
417     }   
418
419     return sectors;
420 }
421
422
423 off_t map_insert_file(GEOMETRY *geo, int skip, int sectors)
424 {
425     off_t here;
426     int count, i;
427     char buff[SECTOR_SIZE];
428     
429     if (verbose>0) printf("Calling map_insert_file\n");
430     if (lseek(geo->fd, (off_t)skip*SECTOR_SIZE, SEEK_SET)<0)
431         pdie("map_insert_file: file seek");
432     here = lseek(map_file, 0, SEEK_CUR);
433
434     for (i=0; i<sectors; i++) {
435         count = read(geo->fd, buff, SECTOR_SIZE);
436         if (count<0) pdie("map_insert_file: file read");
437         if (count<SECTOR_SIZE) memset(buff+count, 0, SECTOR_SIZE-count);
438         count = write(map_file, buff, SECTOR_SIZE);
439         if (count<=0) pdie("map_insert_file: map write");
440     }
441
442     if ((here % SECTOR_SIZE) != 0) die("Map file positioning error");
443     map_add(&map_geo, here/SECTOR_SIZE, sectors);
444     
445     return here;
446 }
447
448 off_t map_insert_data(unsigned char *data, int size)
449 {
450     off_t here;
451     int count, sectors = 0;
452     char buff[SECTOR_SIZE];
453     
454     if (verbose>0) printf("Calling map_insert_data\n");
455     here = lseek(map_file, 0, SEEK_CUR);
456
457     while (size) {
458         if (size>SECTOR_SIZE) count=SECTOR_SIZE;
459         else count=size;
460         
461         memcpy(buff, data, count);
462         data += count;
463         size -= count;
464         sectors++;
465         
466         if (count<SECTOR_SIZE) memset(buff+count, 0, SECTOR_SIZE-count);
467         
468         count = write(map_file, buff, SECTOR_SIZE);
469         if (count<=0) pdie("map_insert_data: map write");
470     }
471
472     if ((here % SECTOR_SIZE) != 0) die("Map file positioning error");
473     map_add(&map_geo, here/SECTOR_SIZE, sectors);
474     
475     return here;
476 }
477
478 #endif /* !__MSDOS__ */
479