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 */
34static 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 */
49typedef __signed__ char __s8;
50typedef unsigned char __u8;
51typedef __signed__ short __s16;
52typedef unsigned short __u16;
53typedef __signed__ int __s32;
54typedef 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 */
87struct 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 */
103struct 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 */
118struct 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
131struct 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 */
160int
161minix_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. */
192static int
193minix_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. */
201static int
202minix_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 */
245int
246minix_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  */
296int
297minix_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