xref: /titanic_52/usr/src/grub/grub-0.97/stage2/fsys_minix.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2000,2001,2002  Free Software Foundation, Inc.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /* Restrictions:
21    This is MINIX V1 only (yet)
22    Disk creation is like:
23    mkfs.minix -c DEVICE
24 */
25 
26 #ifdef FSYS_MINIX
27 
28 #include "shared.h"
29 #include "filesys.h"
30 
31 /* #define DEBUG_MINIX */
32 
33 /* indirect blocks */
34 static int mapblock1, mapblock2, namelen;
35 
36 /* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */
37 #define DEV_BSIZE 512
38 
39 /* include/linux/fs.h */
40 #define BLOCK_SIZE_BITS 10
41 #define BLOCK_SIZE 	(1<<BLOCK_SIZE_BITS)
42 
43 /* made up, defaults to 1 but can be passed via mount_opts */
44 #define WHICH_SUPER 1
45 /* kind of from fs/ext2/super.c (is OK for minix) */
46 #define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE)	/* = 2 */
47 
48 /* include/asm-i386/type.h */
49 typedef __signed__ char __s8;
50 typedef unsigned char __u8;
51 typedef __signed__ short __s16;
52 typedef unsigned short __u16;
53 typedef __signed__ int __s32;
54 typedef unsigned int __u32;
55 
56 /* include/linux/minix_fs.h */
57 #define MINIX_ROOT_INO 1
58 
59 /* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */
60 #define MINIX_LINK_MAX  250
61 #define MINIX2_LINK_MAX 65530
62 
63 #define MINIX_I_MAP_SLOTS       8
64 #define MINIX_Z_MAP_SLOTS       64
65 #define MINIX_SUPER_MAGIC       0x137F          /* original minix fs */
66 #define MINIX_SUPER_MAGIC2      0x138F          /* minix fs, 30 char names */
67 #define MINIX2_SUPER_MAGIC      0x2468          /* minix V2 fs */
68 #define MINIX2_SUPER_MAGIC2     0x2478          /* minix V2 fs, 30 char names */
69 #define MINIX_VALID_FS          0x0001          /* Clean fs. */
70 #define MINIX_ERROR_FS          0x0002          /* fs has errors. */
71 
72 #define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
73 #define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
74 
75 #define MINIX_V1                0x0001          /* original minix fs */
76 #define MINIX_V2                0x0002          /* minix V2 fs */
77 
78 /* originally this is :
79 #define INODE_VERSION(inode)    inode->i_sb->u.minix_sb.s_version
80    here we have */
81 #define INODE_VERSION(inode)	(SUPERBLOCK->s_version)
82 
83 /*
84  * This is the original minix inode layout on disk.
85  * Note the 8-bit gid and atime and ctime.
86  */
87 struct minix_inode {
88 	__u16 i_mode;
89 	__u16 i_uid;
90 	__u32 i_size;
91 	__u32 i_time;
92 	__u8  i_gid;
93 	__u8  i_nlinks;
94 	__u16 i_zone[9];
95 };
96 
97 /*
98  * The new minix inode has all the time entries, as well as
99  * long block numbers and a third indirect block (7+1+1+1
100  * instead of 7+1+1). Also, some previously 8-bit values are
101  * now 16-bit. The inode is now 64 bytes instead of 32.
102  */
103 struct minix2_inode {
104 	__u16 i_mode;
105 	__u16 i_nlinks;
106 	__u16 i_uid;
107 	__u16 i_gid;
108 	__u32 i_size;
109 	__u32 i_atime;
110 	__u32 i_mtime;
111 	__u32 i_ctime;
112 	__u32 i_zone[10];
113 };
114 
115 /*
116  * minix super-block data on disk
117  */
118 struct minix_super_block {
119         __u16 s_ninodes;
120         __u16 s_nzones;
121         __u16 s_imap_blocks;
122         __u16 s_zmap_blocks;
123         __u16 s_firstdatazone;
124         __u16 s_log_zone_size;
125         __u32 s_max_size;
126         __u16 s_magic;
127         __u16 s_state;
128         __u32 s_zones;
129 };
130 
131 struct minix_dir_entry {
132         __u16 inode;
133         char name[0];
134 };
135 
136 /* made up, these are pointers into FSYS_BUF */
137 /* read once, always stays there: */
138 #define SUPERBLOCK \
139     ((struct minix_super_block *)(FSYS_BUF))
140 #define INODE \
141     ((struct minix_inode *)((int) SUPERBLOCK + BLOCK_SIZE))
142 #define DATABLOCK1 \
143     ((int)((int)INODE + sizeof(struct minix_inode)))
144 #define DATABLOCK2 \
145     ((int)((int)DATABLOCK1 + BLOCK_SIZE))
146 
147 /* linux/stat.h */
148 #define S_IFMT  00170000
149 #define S_IFLNK  0120000
150 #define S_IFREG  0100000
151 #define S_IFDIR  0040000
152 #define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
153 #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
154 #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
155 
156 #define PATH_MAX                1024	/* include/linux/limits.h */
157 #define MAX_LINK_COUNT             5	/* number of symbolic links to follow */
158 
159 /* check filesystem types and read superblock into memory buffer */
160 int
161 minix_mount (void)
162 {
163   if (((current_drive & 0x80) || current_slice != 0)
164       && ! IS_PC_SLICE_TYPE_MINIX (current_slice)
165       && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))
166     return 0;			/* The partition is not of MINIX type */
167 
168   if (part_length < (SBLOCK +
169 		     (sizeof (struct minix_super_block) / DEV_BSIZE)))
170     return 0;			/* The partition is too short */
171 
172   if (!devread (SBLOCK, 0, sizeof (struct minix_super_block),
173 		(char *) SUPERBLOCK))
174     return 0;			/* Cannot read superblock */
175 
176   switch (SUPERBLOCK->s_magic)
177     {
178     case MINIX_SUPER_MAGIC:
179       namelen = 14;
180       break;
181     case MINIX_SUPER_MAGIC2:
182       namelen = 30;
183       break;
184     default:
185       return 0;			/* Unsupported type */
186     }
187 
188   return 1;
189 }
190 
191 /* Takes a file system block number and reads it into BUFFER. */
192 static int
193 minix_rdfsb (int fsblock, int buffer)
194 {
195   return devread (fsblock * (BLOCK_SIZE / DEV_BSIZE), 0,
196 		  BLOCK_SIZE, (char *) buffer);
197 }
198 
199 /* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into
200    a physical block (the location in the file system) via an inode. */
201 static int
202 minix_block_map (int logical_block)
203 {
204   int i;
205 
206   if (logical_block < 7)
207     return INODE->i_zone[logical_block];
208 
209   logical_block -= 7;
210   if (logical_block < 512)
211     {
212       i = INODE->i_zone[7];
213 
214       if (!i || ((mapblock1 != 1)
215 		 && !minix_rdfsb (i, DATABLOCK1)))
216 	{
217 	  errnum = ERR_FSYS_CORRUPT;
218 	  return -1;
219 	}
220       mapblock1 = 1;
221       return ((__u16 *) DATABLOCK1) [logical_block];
222     }
223 
224   logical_block -= 512;
225   i = INODE->i_zone[8];
226   if (!i || ((mapblock1 != 2)
227 	     && !minix_rdfsb (i, DATABLOCK1)))
228     {
229       errnum = ERR_FSYS_CORRUPT;
230       return -1;
231     }
232   mapblock1 = 2;
233   i = ((__u16 *) DATABLOCK1)[logical_block >> 9];
234   if (!i || ((mapblock2 != i)
235 	     && !minix_rdfsb (i, DATABLOCK2)))
236     {
237       errnum = ERR_FSYS_CORRUPT;
238       return -1;
239     }
240   mapblock2 = i;
241   return ((__u16 *) DATABLOCK2)[logical_block & 511];
242 }
243 
244 /* read from INODE into BUF */
245 int
246 minix_read (char *buf, int len)
247 {
248   int logical_block;
249   int offset;
250   int map;
251   int ret = 0;
252   int size = 0;
253 
254   while (len > 0)
255     {
256       /* find the (logical) block component of our location */
257       logical_block = filepos >> BLOCK_SIZE_BITS;
258       offset = filepos & (BLOCK_SIZE - 1);
259       map = minix_block_map (logical_block);
260 #ifdef DEBUG_MINIX
261       printf ("map=%d\n", map);
262 #endif
263       if (map < 0)
264 	break;
265 
266       size = BLOCK_SIZE;
267       size -= offset;
268       if (size > len)
269 	size = len;
270 
271       disk_read_func = disk_read_hook;
272 
273       devread (map * (BLOCK_SIZE / DEV_BSIZE),
274 	       offset, size, buf);
275 
276       disk_read_func = NULL;
277 
278       buf += size;
279       len -= size;
280       filepos += size;
281       ret += size;
282     }
283 
284   if (errnum)
285     ret = 0;
286 
287   return ret;
288 }
289 
290 /* preconditions: minix_mount already executed, therefore supblk in buffer
291      known as SUPERBLOCK
292    returns: 0 if error, nonzero iff we were able to find the file successfully
293    postconditions: on a nonzero return, buffer known as INODE contains the
294      inode of the file we were trying to look up
295    side effects: none yet  */
296 int
297 minix_dir (char *dirname)
298 {
299   int current_ino = MINIX_ROOT_INO;  /* start at the root */
300   int updir_ino = current_ino;	     /* the parent of the current directory */
301   int ino_blk;			     /* fs pointer of the inode's info */
302 
303   int str_chk = 0;		     /* used ot hold the results of a string
304 				        compare */
305 
306   struct minix_inode * raw_inode;    /* inode info for current_ino */
307 
308   char linkbuf[PATH_MAX];	     /* buffer for following sym-links */
309   int link_count = 0;
310 
311   char * rest;
312   char ch;
313 
314   int off;			     /* offset within block of directory
315 					entry */
316   int loc;			     /* location within a directory */
317   int blk;			     /* which data blk within dir entry */
318   long map;			     /* fs pointer of a particular block from
319 					dir entry */
320   struct minix_dir_entry * dp;	     /* pointer to directory entry */
321 
322   /* loop invariants:
323      current_ino = inode to lookup
324      dirname = pointer to filename component we are cur looking up within
325      the directory known pointed to by current_ino (if any) */
326 
327 #ifdef DEBUG_MINIX
328   printf ("\n");
329 #endif
330 
331   while (1)
332     {
333 #ifdef DEBUG_MINIX
334       printf ("inode %d, dirname %s\n", current_ino, dirname);
335 #endif
336 
337       ino_blk = (2 + SUPERBLOCK->s_imap_blocks + SUPERBLOCK->s_zmap_blocks
338 		 + (current_ino - 1) / MINIX_INODES_PER_BLOCK);
339       if (! minix_rdfsb (ino_blk, (int) INODE))
340 	return 0;
341 
342       /* reset indirect blocks! */
343       mapblock2 = mapblock1 = -1;
344 
345       raw_inode = INODE + ((current_ino - 1) % MINIX_INODES_PER_BLOCK);
346 
347       /* copy inode to fixed location */
348       memmove ((void *) INODE, (void *) raw_inode,
349 	       sizeof (struct minix_inode));
350 
351       /* If we've got a symbolic link, then chase it. */
352       if (S_ISLNK (INODE->i_mode))
353 	{
354 	  int len;
355 
356 	  if (++link_count > MAX_LINK_COUNT)
357 	    {
358 	      errnum = ERR_SYMLINK_LOOP;
359 	      return 0;
360 	    }
361 #ifdef DEBUG_MINIX
362 	  printf ("S_ISLNK (%s)\n", dirname);
363 #endif
364 
365 	  /* Find out how long our remaining name is. */
366 	  len = 0;
367 	  while (dirname[len] && !isspace (dirname[len]))
368 	    len++;
369 
370 	  /* Get the symlink size. */
371 	  filemax = (INODE->i_size);
372 	  if (filemax + len > sizeof (linkbuf) - 2)
373 	    {
374 	      errnum = ERR_FILELENGTH;
375 	      return 0;
376 	    }
377 
378 	  if (len)
379 	    {
380 	      /* Copy the remaining name to the end of the symlink data.
381 	         Note that DIRNAME and LINKBUF may overlap! */
382 	      memmove (linkbuf + filemax, dirname, len);
383 	    }
384 	  linkbuf[filemax + len] = '\0';
385 
386 	  /* Read the necessary blocks, and reset the file pointer. */
387 	  len = grub_read (linkbuf, filemax);
388 	  filepos = 0;
389 	  if (!len)
390 	    return 0;
391 
392 #ifdef DEBUG_MINIX
393 	  printf ("symlink=%s\n", linkbuf);
394 #endif
395 
396 	  dirname = linkbuf;
397 	  if (*dirname == '/')
398 	    {
399 	      /* It's an absolute link, so look it up in root. */
400 	      current_ino = MINIX_ROOT_INO;
401 	      updir_ino = current_ino;
402 	    }
403 	  else
404 	    {
405 	      /* Relative, so look it up in our parent directory. */
406 	      current_ino = updir_ino;
407 	    }
408 
409 	  /* Try again using the new name. */
410 	  continue;
411 	}
412 
413       /* If end of filename, INODE points to the file's inode */
414       if (!*dirname || isspace (*dirname))
415 	{
416 	  if (!S_ISREG (INODE->i_mode))
417 	    {
418 	      errnum = ERR_BAD_FILETYPE;
419 	      return 0;
420 	    }
421 
422 	  filemax = (INODE->i_size);
423 	  return 1;
424 	}
425 
426       /* else we have to traverse a directory */
427       updir_ino = current_ino;
428 
429       /* skip over slashes */
430       while (*dirname == '/')
431 	dirname++;
432 
433       /* if this isn't a directory of sufficient size to hold our file,
434 	 abort */
435       if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode))
436 	{
437 	  errnum = ERR_BAD_FILETYPE;
438 	  return 0;
439 	}
440 
441       /* skip to next slash or end of filename (space) */
442       for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/';
443 	   rest++);
444 
445       /* look through this directory and find the next filename component */
446       /* invariant: rest points to slash after the next filename component */
447       *rest = 0;
448       loc = 0;
449 
450       do
451 	{
452 #ifdef DEBUG_MINIX
453 	  printf ("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc);
454 #endif
455 
456 	  /* if our location/byte offset into the directory exceeds the size,
457 	     give up */
458 	  if (loc >= INODE->i_size)
459 	    {
460 	      if (print_possibilities < 0)
461 		{
462 #if 0
463 		  putchar ('\n');
464 #endif
465 		}
466 	      else
467 		{
468 		  errnum = ERR_FILE_NOT_FOUND;
469 		  *rest = ch;
470 		}
471 	      return (print_possibilities < 0);
472 	    }
473 
474 	  /* else, find the (logical) block component of our location */
475 	  blk = loc >> BLOCK_SIZE_BITS;
476 
477 	  /* we know which logical block of the directory entry we are looking
478 	     for, now we have to translate that to the physical (fs) block on
479 	     the disk */
480 	  map = minix_block_map (blk);
481 #ifdef DEBUG_MINIX
482 	  printf ("fs block=%d\n", map);
483 #endif
484 	  mapblock2 = -1;
485 	  if ((map < 0) || !minix_rdfsb (map, DATABLOCK2))
486 	    {
487 	      errnum = ERR_FSYS_CORRUPT;
488 	      *rest = ch;
489 	      return 0;
490 	    }
491 	  off = loc & (BLOCK_SIZE - 1);
492 	  dp = (struct minix_dir_entry *) (DATABLOCK2 + off);
493 	  /* advance loc prematurely to next on-disk directory entry  */
494 	  loc += sizeof (dp->inode) + namelen;
495 
496 	  /* NOTE: minix filenames are NULL terminated if < NAMELEN
497 	     else exact */
498 
499 #ifdef DEBUG_MINIX
500 	  printf ("directory entry ino=%d\n", dp->inode);
501 	  if (dp->inode)
502 	    printf ("entry=%s\n", dp->name);
503 #endif
504 
505 	  if (dp->inode)
506 	    {
507 	      int saved_c = dp->name[namelen];
508 
509 	      dp->name[namelen] = 0;
510 	      str_chk = substring (dirname, dp->name);
511 
512 # ifndef STAGE1_5
513 	      if (print_possibilities && ch != '/'
514 		  && (!*dirname || str_chk <= 0))
515 		{
516 		  if (print_possibilities > 0)
517 		    print_possibilities = -print_possibilities;
518 		  print_a_completion (dp->name);
519 		}
520 # endif
521 
522 	      dp->name[namelen] = saved_c;
523 	    }
524 
525 	}
526       while (!dp->inode || (str_chk || (print_possibilities && ch != '/')));
527 
528       current_ino = dp->inode;
529       *(dirname = rest) = ch;
530     }
531   /* never get here */
532 }
533 
534 #endif /* FSYS_MINIX */
535