Merge from vendor branch DIFFUTILS:
[dragonfly.git] / sys / i386 / boot / cdboot / cdrom.c
1 /*
2  * Copyright © 1997 Pluto Technologies International, Inc.  Boulder CO
3  * Copyright © 1997 interface business GmbH, Dresden.
4  *      All rights reserved.
5  *
6  * This code was written by Jörg Wunsch, Dresden.
7  * Direct comments to <joerg_wunsch@interface-business.de>.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: src/sys/i386/boot/cdboot/cdrom.c,v 1.4 1999/08/28 00:43:17 peter Exp $
31  * $DragonFly: src/sys/i386/boot/cdboot/Attic/cdrom.c,v 1.2 2003/06/17 04:28:34 dillon Exp $
32  */
33
34
35 #include "boot.h"
36
37 #include <isofs/cd9660/iso.h>
38
39 #define BLKSIZE 2048            /* CD-ROM data block size */
40 #define BIOSSEC 512             /* BIOS sector size */
41
42 #define CD2LBA(rba)     ((rba) << 2) /* CD-ROM relative block to BIOS LBA */
43
44 u_int32_t sessionstart;
45
46 static struct iso_primary_descriptor pdesc;
47
48 static char *rootdirbuf;
49 static size_t rootdirsize;
50 static char xbuf[BLKSIZE];
51 static u_int32_t curblk, startblk, filesize, offset;
52
53 static int bread(u_int32_t rba, size_t nblks, void *buf);
54 static void badread(const char *msg, u_int32_t blkno);
55 static struct iso_directory_record *find(const char *path, int list_only);
56 static char *get_rr_name(struct iso_directory_record *dirp, size_t *len_ret);
57 static int iread(u_char *buf, size_t len,
58                  void (*copyfun)(const void *src, void *dst, size_t size));
59
60 static struct daddrpacket dpkt = { 0x10 };
61
62 int
63 devopen(u_int32_t session)
64 {
65         int rv;
66         u_int32_t rootdirblk;
67         struct iso_directory_record *rootdirp;
68
69         if ((rv = bread(session + 16, 1, &pdesc)) != 0) {
70                 printf("Error reading primary ISO descriptor: %d\n", rv);
71                 return -1;
72         }
73         rootdirp = (struct iso_directory_record *)pdesc.root_directory_record;
74         rootdirblk = isonum_733(rootdirp->extent);
75         rootdirsize = isonum_733(rootdirp->size);
76
77         /* just in case, round up */
78         rootdirsize = (rootdirsize + BLKSIZE - 1) & ~(BLKSIZE - 1);
79
80         if (rootdirbuf != NULL)
81                 free(rootdirbuf);
82         if ((rootdirbuf = malloc(rootdirsize)) == 0) {
83                 printf("Cannot allocate memory for the root "
84                        "directory buffer.\n");
85                 return -1;
86         }
87         if ((rv = bread(rootdirblk, rootdirsize / BLKSIZE, rootdirbuf))
88             != 0) {
89                 printf("Error reading root directory: %d\n", rv);
90                 return -1;
91         }
92
93         DPRINTF(("Root directory is 0x%x bytes @ %d\n",
94                  rootdirsize, rootdirblk));
95
96         return 0;
97 }
98
99 static int
100 bread(u_int32_t rba, size_t nblks, void *buf)
101 {
102         int i, rv;
103
104         for (i = 0, rv = -1; rv != 0 && i < 3; i++) {
105                 dpkt.nblocks = nblks * (BLKSIZE / BIOSSEC);
106                 dpkt.boffs = (u_int16_t)((int)buf & 0xffff);
107                 dpkt.bseg = BOOTSEG;
108                 dpkt.lba = CD2LBA(rba);
109
110 #ifdef DEBUG_VERBOSE
111                 DPRINTF(("Calling biosreadlba(%d blocks, lba %d) = ",
112                          dpkt.nblocks, dpkt.lba));
113 #endif
114
115                 rv = biosreadlba(&dpkt);
116
117 #ifdef DEBUG_VERBOSE
118                 DPRINTF(("%d\n", rv));
119 #endif
120         }
121         return rv;
122 }
123
124
125 void
126 seek(u_int32_t offs)
127 {
128         offset = offs;
129 }
130
131 static void
132 badread(const char *msg, u_int32_t blkno)
133 {
134         printf("Error reading block %d from CD-ROM: %s\n",
135                blkno, msg);
136 }
137
138 static __inline size_t
139 minlen(size_t a, size_t b)
140 {
141         return a < b? a: b;
142 }
143
144 /*
145  * Internal form of read()/xread().
146  */
147 static int
148 iread(u_char *buf, size_t len,
149       void (*copyfun)(const void *src, void *dst, size_t size))
150 {
151         u_int32_t newblk, ptr;
152         size_t bsize;
153
154         newblk = offset / BLKSIZE + startblk;
155
156         if (newblk != curblk) {
157                 if (offset + len >= filesize) {
158                         badread("access beyond file limit", newblk);
159                         return -1;
160                 }
161                 if (bread(newblk, 1, xbuf)) {
162                         badread("BIOS read error", newblk);
163                         return -1;
164                 }
165                 curblk = newblk;
166         }
167         ptr = offset & (BLKSIZE - 1);
168         if (ptr > 0) {
169                 /* initial short transfer */
170                 bsize = minlen(BLKSIZE - ptr, len);
171                 copyfun(xbuf + ptr, buf, bsize);
172                 buf += bsize;
173                 len -= bsize;
174                 offset += bsize;
175         }
176         for (; len > 0; len -= bsize) {
177                 bsize = minlen(len, BLKSIZE);
178                 newblk = offset / BLKSIZE + startblk;
179
180                 if (newblk != curblk) {
181                         if (offset + bsize > filesize) {
182                                 badread("access beyond file limit", newblk);
183                                 return -1;
184                         }
185                         if (bread(newblk, 1, xbuf)) {
186                                 badread("BIOS read error", newblk);
187                                 return -1;
188                         }
189                         curblk = newblk;
190                 }
191                 copyfun(xbuf, buf, bsize);
192                 buf += bsize;
193                 offset += bsize;
194         }
195         return 0;
196 }
197
198 int
199 read(u_char *buf, size_t len)
200 {
201         DPRINTF(("read(0x%x, %d)\n", (int)buf, len));
202         return iread(buf, len, bcopy);
203 }
204
205 int
206 xread(u_char *buf, size_t len)
207 {
208         DPRINTF(("xread(0x%x, %d)\n", (int)buf, len));
209         return iread(buf, len, pcpy);
210 }
211
212 static char *
213 get_rr_name(struct iso_directory_record *dirp, size_t *len_ret)
214 {
215         struct rr_header {
216                 char    type[2];
217                 u_char  len;
218                 u_char  version;
219         } *rrp;
220         struct rr_nm_header {
221                 struct rr_header rrh;
222                 u_char  flags;
223                 char    name[0]; /* XXX -- using gcc extension */
224         } *rrnmp;
225         char *cp;
226
227         cp = dirp->name + (u_char)dirp->name_len[0];
228         /* round up to 16-bit boundary; ugly */
229         cp = (char *)(((int)cp + 1) & ~1);
230         rrp = (struct rr_header *)cp;
231
232         if (rrp->type[0] != 'R' || rrp->type[1] != 'R') {
233                 DPRINTF(("no RR, "));
234                 return 0;
235         }
236
237         DPRINTF(("RR attribs: "));
238         cp += rrp->len;
239         while (cp - (char *)dirp <= (u_char)dirp->length[0]) {
240                 rrp = (struct rr_header *)cp;
241                 DPRINTF(("%c%c ", rrp->type[0], rrp->type[1]));
242                 if (rrp->type[0] == 'N' && rrp->type[1] == 'M') {
243                         rrnmp = (struct rr_nm_header *)rrp;
244                         *len_ret = rrp->len - sizeof(struct rr_nm_header);
245                         return rrnmp->name;
246                 }
247                 cp += rrp->len;
248         }
249
250         return 0;
251 }
252
253 static struct iso_directory_record *
254 find(const char *path, int list_only)
255 {
256         struct iso_directory_record *dirp;
257         char *ptr, *rrname;
258         size_t len, entrylen;
259         char namebuf[256];
260         int i;
261         int (*comp)(const char *, const char *);
262
263         while (*path && *path == '/')
264                 path++;
265
266         for (ptr = rootdirbuf, i = 1;
267              ptr < rootdirbuf + rootdirsize;
268              ptr += entrylen, i++) {
269                 dirp = (struct iso_directory_record *)ptr;
270                 entrylen = (u_char)dirp->length[0];
271                 len = (u_char)dirp->name_len[0];
272
273                 DPRINTF(("# %d: offset 0x%x, length 0x%x = %d, ",
274                          i, (int)(ptr - rootdirbuf), entrylen, entrylen));
275
276                 if (entrylen == 0) {
277                         /*
278                          * Dir entry of length 0.  That's the last
279                          * entry in this block, advance to the next
280                          * block (if any).  In case we get beyond the
281                          * end of the directory, we'll fall off the
282                          * loop due to the rootdirsize condition in
283                          * the `for' statement.
284                          */
285                         DPRINTF(("entrylen 0\n"));
286                         entrylen = (~((ptr - rootdirbuf) + BLKSIZE - 1))
287                                 & (BLKSIZE - 1);
288                         continue;
289                 }
290                 if (len == 0) {
291                         DPRINTF(("name_len 0\n"));
292                         continue;
293                 }
294                 if (len == 1 &&
295                     (dirp->name[0] == '\0' || dirp->name[1] == '\1')) {
296                         DPRINTF(("dot/dot-dot entry\n"));
297                         continue;
298                 }
299                 /* don't consider directories */
300                 if (dirp->flags[0] & 2) {
301                         DPRINTF(("directory\n"));
302                         continue;
303                 }
304                 rrname = get_rr_name(dirp, &len);
305                 comp = rrname? strcmp: strcasecmp;
306
307                 bcopy(rrname? rrname: dirp->name, namebuf, len);
308                 namebuf[len] = 0;
309                 DPRINTF(("name `%s'\n", namebuf));
310
311                 if (list_only) {
312 #ifndef DEBUG
313                         printf("%s ", namebuf);
314 #endif
315                 } else if (comp(path, namebuf) == 0)
316                         return dirp;
317         }
318 #ifndef DEBUG
319         if (list_only)
320                 printf("\n");
321 #endif
322         return 0;
323 }
324
325 int
326 openrd(char *name)
327 {
328         char *cp;
329         const char *fname;
330         u_int32_t oldsession;
331         int session, list_only;
332         struct iso_directory_record *dirp;
333
334         session = 0;
335         fname = name;
336
337         /*
338          * We accept the following boot string:
339          *
340          * [@sessionstart] name
341          */
342         for (cp = name; *cp; cp++)
343                 switch (*cp) {
344                 /* we don't support filenames with spaces */
345                 case ' ':       case '\t':
346                         break;
347
348                 case '@':
349                         if (session) {
350                                 printf("Syntax error\n");
351                                 return -1;
352                         }
353                         session++;
354                         oldsession = sessionstart;
355                         sessionstart = 0;
356                         break;
357
358                 case '0':       case '1':       case '2':
359                 case '3':       case '4':       case '5':
360                 case '6':       case '7':       case '8':
361                 case '9':
362                         if (session == 1) {
363                                 sessionstart *= 10;
364                                 sessionstart += *cp - '0';
365                         }
366                         break;
367
368                 default:
369                         if (session == 1) {
370                                 session++;
371                                 fname = cp;
372                         }
373                 }
374
375         if (session && devopen(sessionstart) == -1) {
376                 (void)devopen(oldsession);
377                 sessionstart = oldsession;
378         }
379         if (session == 1)
380                 /* XXX no filename, only session arg */
381                 return -1;
382
383         list_only = fname[0] == '?' && fname[1] == 0;
384
385         DPRINTF(("Calling find(%s, %d):\n", fname, list_only));
386         dirp = find(fname, list_only);
387         DPRINTF(("find() returned 0x%x\n", (int)dirp));
388
389         if (list_only)
390                 return -1;
391         if (dirp == 0)
392                 return 1;
393
394         startblk = isonum_733(dirp->extent);
395         filesize = isonum_733(dirp->size);
396
397         DPRINTF(("startblk = %d, filesize = %d\n", startblk, filesize));
398
399         curblk = 0;     /* force a re-read, 0 is impossible file start */
400         seek(0);
401
402         return 0;
403 }