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