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
iso9660_devread(int sector,int byte_offset,int byte_len,char * buf)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
iso9660_mount(void)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
iso9660_dir(char * dirname)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
iso9660_read(char * buf,int len)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 */
343