Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / grubfs / fsys_iso9660.c
1 /*
2  *  ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader)
3  *  including Rock Ridge Extensions support
4  *
5  *  Copyright (C) 1998, 1999  Kousuke Takai  <tak@kmc.kyoto-u.ac.jp>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20  *  MA 02110-1301, USA.
21  */
22 /*
23  *  References:
24  *      linux/fs/isofs/rock.[ch]
25  *      mkisofs-1.11.1/diag/isoinfo.c
26  *      mkisofs-1.11.1/iso9660.h
27  *              (all are written by Eric Youngdale)
28  *
29  *  Modifications by:
30  *      Leonid Lisovskiy   <lly@pisem.net>      2003
31  */
32
33 /*
34  * Modified to make it work with FILO
35  * 2003-10 by SONE Takeshi
36  */
37
38 #ifdef FSYS_ISO9660
39
40 #include "shared.h"
41 #include "filesys.h"
42 #include "iso9660.h"
43 #include "debug.h"
44
45 #if defined(__sparc__) || defined(__PPC__)
46 #define ENDIAN b
47 #else
48 #define ENDIAN l
49 #endif
50
51 struct iso_superblock {
52     unsigned long vol_sector;
53
54     unsigned long file_start;
55 };
56
57 #define ISO_SUPER       ((struct iso_superblock *)(FSYS_BUF))
58 #define PRIMDESC        ((struct iso_primary_descriptor *)(FSYS_BUF + 2048))
59 #define DIRREC          ((struct iso_directory_record *)(FSYS_BUF + 4096))
60 #define RRCONT_BUF      ((unsigned char *)(FSYS_BUF + 6144))
61 #define NAME_BUF        ((unsigned char *)(FSYS_BUF + 8192))
62
63 static int
64 iso9660_devread (int sector, int byte_offset, int byte_len, char *buf)
65 {
66   /* FILO uses 512-byte "soft" sector, and ISO-9660 uses 2048-byte
67    * CD-ROM sector */
68   return devread(sector<<2, byte_offset, byte_len, buf);
69 }
70
71 int
72 iso9660_mount (void)
73 {
74   unsigned int sector;
75
76   /*
77    *  Because there is no defined slice type ID for ISO-9660 filesystem,
78    *  this test will pass only either (1) if entire disk is used, or
79    *  (2) if current partition is BSD style sub-partition whose ID is
80    *  ISO-9660.
81    */
82   /*if ((current_partition != 0xFFFFFF)
83       && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660))
84     return 0;*/
85
86   /*
87    *  Currently, only FIRST session of MultiSession disks are supported !!!
88    */
89   for (sector = 16 ; sector < 32 ; sector++)
90     {
91       if (!iso9660_devread(sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC))
92         break;
93       /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */
94       if (CHECK4(&PRIMDESC->type, ISO_VD_PRIMARY, 'C', 'D', '0')
95           && CHECK2(PRIMDESC->id + 3, '0', '1'))
96         {
97           ISO_SUPER->vol_sector = sector;
98           ISO_SUPER->file_start = 0;
99           fsmax = PRIMDESC->volume_space_size.ENDIAN;
100           return 1;
101         }
102     }
103
104   return 0;
105 }
106
107 int
108 iso9660_dir (char *dirname)
109 {
110   struct iso_directory_record *idr;
111   RR_ptr_t rr_ptr;
112   struct rock_ridge *ce_ptr;
113   unsigned int pathlen;
114   int size;
115   unsigned int extent;
116   unsigned int rr_len;
117   unsigned char file_type;
118   unsigned char rr_flag;
119
120   idr = &PRIMDESC->root_directory_record;
121   ISO_SUPER->file_start = 0;
122
123   do
124   {
125       while (*dirname == '/')   /* skip leading slashes */
126           dirname++;
127       /* pathlen = strcspn(dirname, "/\n\t "); */
128       for (pathlen = 0 ;
129           dirname[pathlen]
130              && !isspace(dirname[pathlen]) && dirname[pathlen] != '/' ;
131           pathlen++)
132         ;
133
134       size = idr->size.ENDIAN;
135       extent = idr->extent.ENDIAN;
136
137       while (size > 0)
138       {
139           if (!iso9660_devread(extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC))
140           {
141               errnum = ERR_FSYS_CORRUPT;
142               return 0;
143           }
144           extent++;
145
146           idr = (struct iso_directory_record *)DIRREC;
147           for (; idr->length.ENDIAN > 0;
148                  idr = (struct iso_directory_record *)((char *)idr + idr->length.ENDIAN) )
149           {
150               const char *name = (char *)idr->name;
151               unsigned int name_len = idr->name_len.ENDIAN;
152
153               file_type = (idr->flags.ENDIAN & 2) ? ISO_DIRECTORY : ISO_REGULAR;
154               if (name_len == 1)
155               {
156                   if ((name[0] == 0) || /* self */
157                       (name[0] == 1))   /* parent */
158                     continue;
159               }
160               if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1'))
161               {
162                   name_len -= 2;        /* truncate trailing file version */
163                   if (name_len > 1 && name[name_len - 1] == '.')
164                     name_len--;         /* truncate trailing dot */
165               }
166
167               /*
168                *  Parse Rock-Ridge extension
169                */
170               rr_len = (idr->length.ENDIAN - idr->name_len.ENDIAN
171                         - (unsigned char)sizeof(struct iso_directory_record)
172                         + (unsigned char)sizeof(idr->name));
173               rr_ptr.ptr = ((char *)idr + idr->name_len.ENDIAN
174                             + sizeof(struct iso_directory_record)
175                             - sizeof(idr->name));
176               if (rr_len & 1)
177                 rr_ptr.ptr++, rr_len--;
178               ce_ptr = NULL;
179               rr_flag = RR_FLAG_NM | RR_FLAG_PX;
180
181               while (rr_len >= 4)
182               {
183                   if (rr_ptr.rr->version != 1)
184                   {
185 #ifndef STAGE1_5
186                     if (debug)
187                       printf(
188                             "Non-supported version (%d) RockRidge chunk "
189                             "`%c%c'\n", rr_ptr.rr->version,
190                             rr_ptr.rr->signature & 0xFF,
191                             rr_ptr.rr->signature >> 8);
192 #endif
193                   }
194                   else if (CHECK2(&rr_ptr.rr->signature, 'R', 'R')
195                            && rr_ptr.rr->len >= 5)
196                       rr_flag &= rr_ptr.rr->u.rr.flags.ENDIAN;
197                   else if (CHECK2(&rr_ptr.rr->signature, 'N', 'M'))
198                   {
199                       name = (char *)rr_ptr.rr->u.nm.name;
200                       name_len = rr_ptr.rr->len - 5;
201                       rr_flag &= ~RR_FLAG_NM;
202                   }
203                   else if (CHECK2(&rr_ptr.rr->signature, 'P', 'X')
204                            && rr_ptr.rr->len >= 36)
205                   {
206                       file_type = ((rr_ptr.rr->u.px.mode.ENDIAN & POSIX_S_IFMT)
207                                    == POSIX_S_IFREG
208                                    ? ISO_REGULAR
209                                    : ((rr_ptr.rr->u.px.mode.ENDIAN & POSIX_S_IFMT)
210                                       == POSIX_S_IFDIR
211                                       ? ISO_DIRECTORY : ISO_OTHER));
212                       rr_flag &= ~RR_FLAG_PX;
213                   }
214                   else if (CHECK2(&rr_ptr.rr->signature, 'C', 'E')
215                            && rr_ptr.rr->len >= 28)
216                     ce_ptr = rr_ptr.rr;
217                   if (!rr_flag)
218                     /*
219                      * There is no more extension we expects...
220                      */
221                     break;
222                   rr_len -= rr_ptr.rr->len;
223                   rr_ptr.ptr += rr_ptr.rr->len;
224                   if (rr_len < 4 && ce_ptr != NULL)
225                   {
226                       /* preserve name before loading new extent. */
227                       if( RRCONT_BUF <= (unsigned char *)name
228                           && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE )
229                       {
230                           memcpy(NAME_BUF, name, name_len);
231                           name = (char *)NAME_BUF;
232                       }
233                       rr_ptr.ptr = (char *)(RRCONT_BUF + ce_ptr->u.ce.offset.ENDIAN);
234                       rr_len = ce_ptr->u.ce.size.ENDIAN;
235                       if (!iso9660_devread(ce_ptr->u.ce.extent.ENDIAN, 0,
236                                            ISO_SECTOR_SIZE, (char *)RRCONT_BUF))
237                       {
238                           errnum = 0;   /* this is not fatal. */
239                           break;
240                       }
241                       ce_ptr = NULL;
242                    }
243               } /* rr_len >= 4 */
244
245               filemax = MAXINT;
246               if (name_len >= pathlen
247                   && !strnicmp(name, dirname, pathlen))
248               {
249                 if (dirname[pathlen] == '/' || !print_possibilities)
250                 {
251                   /*
252                    *  DIRNAME is directory component of pathname,
253                    *  or we are to open a file.
254                    */
255                   if (pathlen == name_len)
256                   {
257                       if (dirname[pathlen] == '/')
258                       {
259                           if (file_type != ISO_DIRECTORY)
260                           {
261                               errnum = ERR_BAD_FILETYPE;
262                               return 0;
263                           }
264                           goto next_dir_level;
265                       }
266                       if (file_type != ISO_REGULAR)
267                       {
268                           errnum = ERR_BAD_FILETYPE;
269                           return 0;
270                       }
271                       ISO_SUPER->file_start = idr->extent.ENDIAN;
272                       filepos = 0;
273                       filemax = idr->size.ENDIAN;
274                       return 1;
275                   }
276                 }
277                 else    /* Completion */
278                 {
279 #ifndef STAGE1_5
280                   if (print_possibilities > 0)
281                       print_possibilities = -print_possibilities;
282                   memcpy(NAME_BUF, name, name_len);
283                   NAME_BUF[name_len] = '\0';
284                   print_a_completion (NAME_BUF);
285 #endif
286                 }
287               }
288           } /* for */
289
290           size -= ISO_SECTOR_SIZE;
291       } /* size>0 */
292
293       if (dirname[pathlen] == '/' || print_possibilities >= 0)
294       {
295           errnum = ERR_FILE_NOT_FOUND;
296           return 0;
297       }
298
299 next_dir_level:
300       dirname += pathlen;
301
302   } while (*dirname == '/');
303
304   return 1;
305 }
306
307 int
308 iso9660_read (char *buf, int len)
309 {
310   int sector, blkoffset, size, ret;
311
312   if (ISO_SUPER->file_start == 0)
313     return 0;
314
315   ret = 0;
316   blkoffset = filepos & (ISO_SECTOR_SIZE - 1);
317   sector = filepos >> ISO_SECTOR_BITS;
318   while (len > 0)
319   {
320     size = ISO_SECTOR_SIZE - blkoffset;
321     if (size > len)
322         size = len;
323
324     disk_read_func = disk_read_hook;
325
326     if (!iso9660_devread(ISO_SUPER->file_start + sector, blkoffset, size, buf))
327         return 0;
328
329     disk_read_func = NULL;
330
331     len -= size;
332     buf += size;
333     ret += size;
334     filepos += size;
335     sector++;
336     blkoffset = 0;
337   }
338
339   return ret;
340 }
341
342 #endif /* FSYS_ISO9660 */