1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 1999, 2001, 2003  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#ifdef FSYS_EXT2FS
21
22#include "shared.h"
23#include "filesys.h"
24
25static int mapblock1, mapblock2;
26
27/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */
28#define DEV_BSIZE 512
29
30/* include/linux/fs.h */
31#define BLOCK_SIZE 1024		/* initial block size for superblock read */
32/* made up, defaults to 1 but can be passed via mount_opts */
33#define WHICH_SUPER 1
34/* kind of from fs/ext2/super.c */
35#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE)	/* = 2 */
36
37/* include/asm-i386/types.h */
38typedef __signed__ char __s8;
39typedef unsigned char __u8;
40typedef __signed__ short __s16;
41typedef unsigned short __u16;
42typedef __signed__ int __s32;
43typedef unsigned int __u32;
44
45/*
46 * Constants relative to the data blocks, from ext2_fs.h
47 */
48#define EXT2_NDIR_BLOCKS                12
49#define EXT2_IND_BLOCK                  EXT2_NDIR_BLOCKS
50#define EXT2_DIND_BLOCK                 (EXT2_IND_BLOCK + 1)
51#define EXT2_TIND_BLOCK                 (EXT2_DIND_BLOCK + 1)
52#define EXT2_N_BLOCKS                   (EXT2_TIND_BLOCK + 1)
53
54/* include/linux/ext2_fs.h */
55struct ext2_super_block
56  {
57    __u32 s_inodes_count;	/* Inodes count */
58    __u32 s_blocks_count;	/* Blocks count */
59    __u32 s_r_blocks_count;	/* Reserved blocks count */
60    __u32 s_free_blocks_count;	/* Free blocks count */
61    __u32 s_free_inodes_count;	/* Free inodes count */
62    __u32 s_first_data_block;	/* First Data Block */
63    __u32 s_log_block_size;	/* Block size */
64    __s32 s_log_frag_size;	/* Fragment size */
65    __u32 s_blocks_per_group;	/* # Blocks per group */
66    __u32 s_frags_per_group;	/* # Fragments per group */
67    __u32 s_inodes_per_group;	/* # Inodes per group */
68    __u32 s_mtime;		/* Mount time */
69    __u32 s_wtime;		/* Write time */
70    __u16 s_mnt_count;		/* Mount count */
71    __s16 s_max_mnt_count;	/* Maximal mount count */
72    __u16 s_magic;		/* Magic signature */
73    __u16 s_state;		/* File system state */
74    __u16 s_errors;		/* Behaviour when detecting errors */
75    __u16 s_pad;
76    __u32 s_lastcheck;		/* time of last check */
77    __u32 s_checkinterval;	/* max. time between checks */
78    __u32 s_creator_os;		/* OS */
79    __u32 s_rev_level;		/* Revision level */
80    __u16 s_def_resuid;		/* Default uid for reserved blocks */
81    __u16 s_def_resgid;		/* Default gid for reserved blocks */
82    __u32 s_reserved[235];	/* Padding to the end of the block */
83  };
84
85struct ext2_group_desc
86  {
87    __u32 bg_block_bitmap;	/* Blocks bitmap block */
88    __u32 bg_inode_bitmap;	/* Inodes bitmap block */
89    __u32 bg_inode_table;	/* Inodes table block */
90    __u16 bg_free_blocks_count;	/* Free blocks count */
91    __u16 bg_free_inodes_count;	/* Free inodes count */
92    __u16 bg_used_dirs_count;	/* Directories count */
93    __u16 bg_pad;
94    __u32 bg_reserved[3];
95  };
96
97struct ext2_inode
98  {
99    __u16 i_mode;		/* File mode */
100    __u16 i_uid;		/* Owner Uid */
101    __u32 i_size;		/* 4: Size in bytes */
102    __u32 i_atime;		/* Access time */
103    __u32 i_ctime;		/* 12: Creation time */
104    __u32 i_mtime;		/* Modification time */
105    __u32 i_dtime;		/* 20: Deletion Time */
106    __u16 i_gid;		/* Group Id */
107    __u16 i_links_count;	/* 24: Links count */
108    __u32 i_blocks;		/* Blocks count */
109    __u32 i_flags;		/* 32: File flags */
110    union
111      {
112	struct
113	  {
114	    __u32 l_i_reserved1;
115	  }
116	linux1;
117	struct
118	  {
119	    __u32 h_i_translator;
120	  }
121	hurd1;
122	struct
123	  {
124	    __u32 m_i_reserved1;
125	  }
126	masix1;
127      }
128    osd1;			/* OS dependent 1 */
129    __u32 i_block[EXT2_N_BLOCKS];	/* 40: Pointers to blocks */
130    __u32 i_version;		/* File version (for NFS) */
131    __u32 i_file_acl;		/* File ACL */
132    __u32 i_dir_acl;		/* Directory ACL */
133    __u32 i_faddr;		/* Fragment address */
134    union
135      {
136	struct
137	  {
138	    __u8 l_i_frag;	/* Fragment number */
139	    __u8 l_i_fsize;	/* Fragment size */
140	    __u16 i_pad1;
141	    __u32 l_i_reserved2[2];
142	  }
143	linux2;
144	struct
145	  {
146	    __u8 h_i_frag;	/* Fragment number */
147	    __u8 h_i_fsize;	/* Fragment size */
148	    __u16 h_i_mode_high;
149	    __u16 h_i_uid_high;
150	    __u16 h_i_gid_high;
151	    __u32 h_i_author;
152	  }
153	hurd2;
154	struct
155	  {
156	    __u8 m_i_frag;	/* Fragment number */
157	    __u8 m_i_fsize;	/* Fragment size */
158	    __u16 m_pad1;
159	    __u32 m_i_reserved2[2];
160	  }
161	masix2;
162      }
163    osd2;			/* OS dependent 2 */
164  };
165
166/* linux/limits.h */
167#define NAME_MAX         255	/* # chars in a file name */
168
169/* linux/posix_type.h */
170typedef long linux_off_t;
171
172/* linux/ext2fs.h */
173#define EXT2_NAME_LEN 255
174struct ext2_dir_entry
175  {
176    __u32 inode;		/* Inode number */
177    __u16 rec_len;		/* Directory entry length */
178    __u8 name_len;		/* Name length */
179    __u8 file_type;
180    char name[EXT2_NAME_LEN];	/* File name */
181  };
182
183/* linux/ext2fs.h */
184/*
185 * EXT2_DIR_PAD defines the directory entries boundaries
186 *
187 * NOTE: It must be a multiple of 4
188 */
189#define EXT2_DIR_PAD                    4
190#define EXT2_DIR_ROUND                  (EXT2_DIR_PAD - 1)
191#define EXT2_DIR_REC_LEN(name_len)      (((name_len) + 8 + EXT2_DIR_ROUND) & \
192                                         ~EXT2_DIR_ROUND)
193
194
195/* ext2/super.c */
196#define log2(n) ffz(~(n))
197
198#define EXT2_SUPER_MAGIC      0xEF53	/* include/linux/ext2_fs.h */
199#define EXT2_ROOT_INO              2	/* include/linux/ext2_fs.h */
200#define PATH_MAX                1024	/* include/linux/limits.h */
201#define MAX_LINK_COUNT             5	/* number of symbolic links to follow */
202
203/* made up, these are pointers into FSYS_BUF */
204/* read once, always stays there: */
205#define SUPERBLOCK \
206    ((struct ext2_super_block *)(FSYS_BUF))
207#define GROUP_DESC \
208    ((struct ext2_group_desc *) \
209     ((int)SUPERBLOCK + sizeof(struct ext2_super_block)))
210#define INODE \
211    ((struct ext2_inode *)((int)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK)))
212#define DATABLOCK1 \
213    ((int)((int)INODE + sizeof(struct ext2_inode)))
214#define DATABLOCK2 \
215    ((int)((int)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK)))
216
217/* linux/ext2_fs.h */
218#define EXT2_ADDR_PER_BLOCK(s)          (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
219#define EXT2_ADDR_PER_BLOCK_BITS(s)		(log2(EXT2_ADDR_PER_BLOCK(s)))
220
221/* linux/ext2_fs.h */
222#define EXT2_BLOCK_SIZE_BITS(s)        ((s)->s_log_block_size + 10)
223/* kind of from ext2/super.c */
224#define EXT2_BLOCK_SIZE(s)	(1 << EXT2_BLOCK_SIZE_BITS(s))
225/* linux/ext2fs.h */
226#define EXT2_DESC_PER_BLOCK(s) \
227     (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
228/* linux/stat.h */
229#define S_IFMT  00170000
230#define S_IFLNK  0120000
231#define S_IFREG  0100000
232#define S_IFDIR  0040000
233#define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK)
234#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
235#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
236
237/* include/asm-i386/bitops.h */
238/*
239 * ffz = Find First Zero in word. Undefined if no zero exists,
240 * so code should check against ~0UL first..
241 */
242static __inline__ unsigned long
243ffz (unsigned long word)
244{
245  __asm__ ("bsfl %1,%0"
246:	   "=r" (word)
247:	   "r" (~word));
248  return word;
249}
250
251/* check filesystem types and read superblock into memory buffer */
252int
253ext2fs_mount (void)
254{
255  int retval = 1;
256
257  if ((((current_drive & 0x80) || (current_slice != 0))
258       && (current_slice != PC_SLICE_TYPE_EXT2FS)
259       && (current_slice != PC_SLICE_TYPE_LINUX_RAID)
260       && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS))
261       && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER)))
262      || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE))
263      || !devread (SBLOCK, 0, sizeof (struct ext2_super_block),
264		   (char *) SUPERBLOCK)
265      || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC)
266      retval = 0;
267
268  return retval;
269}
270
271/* Takes a file system block number and reads it into BUFFER. */
272static int
273ext2_rdfsb (int fsblock, int buffer)
274{
275#ifdef E2DEBUG
276  printf ("fsblock %d buffer %d\n", fsblock, buffer);
277#endif /* E2DEBUG */
278  return devread (fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0,
279		  EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer);
280}
281
282/* from
283  ext2/inode.c:ext2_bmap()
284*/
285/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into
286   a physical block (the location in the file system) via an inode. */
287static int
288ext2fs_block_map (int logical_block)
289{
290
291#ifdef E2DEBUG
292  unsigned char *i;
293  for (i = (unsigned char *) INODE;
294       i < ((unsigned char *) INODE + sizeof (struct ext2_inode));
295       i++)
296    {
297      printf ("%c", "0123456789abcdef"[*i >> 4]);
298      printf ("%c", "0123456789abcdef"[*i % 16]);
299      if (!((i + 1 - (unsigned char *) INODE) % 16))
300	{
301	  printf ("\n");
302	}
303      else
304	{
305	  printf (" ");
306	}
307    }
308  printf ("logical block %d\n", logical_block);
309#endif /* E2DEBUG */
310
311  /* if it is directly pointed to by the inode, return that physical addr */
312  if (logical_block < EXT2_NDIR_BLOCKS)
313    {
314#ifdef E2DEBUG
315      printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block]));
316      printf ("returning %d\n", INODE->i_block[logical_block]);
317#endif /* E2DEBUG */
318      return INODE->i_block[logical_block];
319    }
320  /* else */
321  logical_block -= EXT2_NDIR_BLOCKS;
322  /* try the indirect block */
323  if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK))
324    {
325      if (mapblock1 != 1
326	  && !ext2_rdfsb (INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1))
327	{
328	  errnum = ERR_FSYS_CORRUPT;
329	  return -1;
330	}
331      mapblock1 = 1;
332      return ((__u32 *) DATABLOCK1)[logical_block];
333    }
334  /* else */
335  logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK);
336  /* now try the double indirect block */
337  if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)))
338    {
339      int bnum;
340      if (mapblock1 != 2
341	  && !ext2_rdfsb (INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1))
342	{
343	  errnum = ERR_FSYS_CORRUPT;
344	  return -1;
345	}
346      mapblock1 = 2;
347      if ((bnum = (((__u32 *) DATABLOCK1)
348		   [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)]))
349	  != mapblock2
350	  && !ext2_rdfsb (bnum, DATABLOCK2))
351	{
352	  errnum = ERR_FSYS_CORRUPT;
353	  return -1;
354	}
355      mapblock2 = bnum;
356      return ((__u32 *) DATABLOCK2)
357	[logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)];
358    }
359  /* else */
360  mapblock2 = -1;
361  logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2));
362  if (mapblock1 != 3
363      && !ext2_rdfsb (INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1))
364    {
365      errnum = ERR_FSYS_CORRUPT;
366      return -1;
367    }
368  mapblock1 = 3;
369  if (!ext2_rdfsb (((__u32 *) DATABLOCK1)
370		   [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)
371				      * 2)],
372		   DATABLOCK2))
373    {
374      errnum = ERR_FSYS_CORRUPT;
375      return -1;
376    }
377  if (!ext2_rdfsb (((__u32 *) DATABLOCK2)
378		   [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK))
379		    & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)],
380		   DATABLOCK2))
381    {
382      errnum = ERR_FSYS_CORRUPT;
383      return -1;
384    }
385  return ((__u32 *) DATABLOCK2)
386    [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)];
387}
388
389/* preconditions: all preconds of ext2fs_block_map */
390int
391ext2fs_read (char *buf, int len)
392{
393  int logical_block;
394  int offset;
395  int map;
396  int ret = 0;
397  int size = 0;
398
399#ifdef E2DEBUG
400  static char hexdigit[] = "0123456789abcdef";
401  unsigned char *i;
402  for (i = (unsigned char *) INODE;
403       i < ((unsigned char *) INODE + sizeof (struct ext2_inode));
404       i++)
405    {
406      printf ("%c", hexdigit[*i >> 4]);
407      printf ("%c", hexdigit[*i % 16]);
408      if (!((i + 1 - (unsigned char *) INODE) % 16))
409	{
410	  printf ("\n");
411	}
412      else
413	{
414	  printf (" ");
415	}
416    }
417#endif /* E2DEBUG */
418  while (len > 0)
419    {
420      /* find the (logical) block component of our location */
421      logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK);
422      offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1);
423      map = ext2fs_block_map (logical_block);
424#ifdef E2DEBUG
425      printf ("map=%d\n", map);
426#endif /* E2DEBUG */
427      if (map < 0)
428	break;
429
430      size = EXT2_BLOCK_SIZE (SUPERBLOCK);
431      size -= offset;
432      if (size > len)
433	size = len;
434
435      if (map == 0) {
436        memset ((char *) buf, 0, size);
437      } else {
438        disk_read_func = disk_read_hook;
439
440        devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE),
441	         offset, size, buf);
442
443        disk_read_func = NULL;
444      }
445
446      buf += size;
447      len -= size;
448      filepos += size;
449      ret += size;
450    }
451
452  if (errnum)
453    ret = 0;
454
455  return ret;
456}
457
458
459/* Based on:
460   def_blk_fops points to
461   blkdev_open, which calls (I think):
462   sys_open()
463   do_open()
464   open_namei()
465   dir_namei() which accesses current->fs->root
466     fs->root was set during original mount:
467     (something)... which calls (I think):
468     ext2_read_super()
469     iget()
470     __iget()
471     read_inode()
472     ext2_read_inode()
473       uses desc_per_block_bits, which is set in ext2_read_super()
474       also uses group descriptors loaded during ext2_read_super()
475   lookup()
476   ext2_lookup()
477   ext2_find_entry()
478   ext2_getblk()
479
480*/
481
482static inline
483int ext2_is_fast_symlink (void)
484{
485  int ea_blocks;
486  ea_blocks = INODE->i_file_acl ? EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE : 0;
487  return INODE->i_blocks == ea_blocks;
488}
489
490/* preconditions: ext2fs_mount already executed, therefore supblk in buffer
491 *   known as SUPERBLOCK
492 * returns: 0 if error, nonzero iff we were able to find the file successfully
493 * postconditions: on a nonzero return, buffer known as INODE contains the
494 *   inode of the file we were trying to look up
495 * side effects: messes up GROUP_DESC buffer area
496 */
497int
498ext2fs_dir (char *dirname)
499{
500  int current_ino = EXT2_ROOT_INO;	/* start at the root */
501  int updir_ino = current_ino;	/* the parent of the current directory */
502  int group_id;			/* which group the inode is in */
503  int group_desc;		/* fs pointer to that group */
504  int desc;			/* index within that group */
505  int ino_blk;			/* fs pointer of the inode's information */
506  int str_chk = 0;		/* used to hold the results of a string compare */
507  struct ext2_group_desc *gdp;
508  struct ext2_inode *raw_inode;	/* inode info corresponding to current_ino */
509
510  char linkbuf[PATH_MAX];	/* buffer for following symbolic links */
511  int link_count = 0;
512
513  char *rest;
514  char ch;			/* temp char holder */
515
516  int off;			/* offset within block of directory entry (off mod blocksize) */
517  int loc;			/* location within a directory */
518  int blk;			/* which data blk within dir entry (off div blocksize) */
519  long map;			/* fs pointer of a particular block from dir entry */
520  struct ext2_dir_entry *dp;	/* pointer to directory entry */
521#ifdef E2DEBUG
522  unsigned char *i;
523#endif	/* E2DEBUG */
524
525  /* loop invariants:
526     current_ino = inode to lookup
527     dirname = pointer to filename component we are cur looking up within
528     the directory known pointed to by current_ino (if any)
529   */
530
531  while (1)
532    {
533#ifdef E2DEBUG
534      printf ("inode %d\n", current_ino);
535      printf ("dirname=%s\n", dirname);
536#endif /* E2DEBUG */
537
538      /* look up an inode */
539      group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group);
540      group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK));
541      desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1);
542#ifdef E2DEBUG
543      printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group,
544	      EXT2_DESC_PER_BLOCK (SUPERBLOCK));
545      printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc);
546#endif /* E2DEBUG */
547      if (!ext2_rdfsb (
548			(WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block),
549			(int) GROUP_DESC))
550	{
551	  return 0;
552	}
553      gdp = GROUP_DESC;
554      ino_blk = gdp[desc].bg_inode_table +
555	(((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group))
556	 >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)));
557#ifdef E2DEBUG
558      printf ("inode table fsblock=%d\n", ino_blk);
559#endif /* E2DEBUG */
560      if (!ext2_rdfsb (ino_blk, (int) INODE))
561	{
562	  return 0;
563	}
564
565      /* reset indirect blocks! */
566      mapblock2 = mapblock1 = -1;
567
568      raw_inode = INODE +
569	((current_ino - 1)
570	 & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1));
571#ifdef E2DEBUG
572      printf ("ipb=%d, sizeof(inode)=%d\n",
573	      (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)),
574	      sizeof (struct ext2_inode));
575      printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode);
576      printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE);
577      for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode;
578	   i++)
579	{
580	  printf ("%c", "0123456789abcdef"[*i >> 4]);
581	  printf ("%c", "0123456789abcdef"[*i % 16]);
582	  if (!((i + 1 - (unsigned char *) INODE) % 16))
583	    {
584	      printf ("\n");
585	    }
586	  else
587	    {
588	      printf (" ");
589	    }
590	}
591      printf ("first word=%x\n", *((int *) raw_inode));
592#endif /* E2DEBUG */
593
594      /* copy inode to fixed location */
595      memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode));
596
597#ifdef E2DEBUG
598      printf ("first word=%x\n", *((int *) INODE));
599#endif /* E2DEBUG */
600
601      /* If we've got a symbolic link, then chase it. */
602      if (S_ISLNK (INODE->i_mode))
603	{
604	  int len;
605	  if (++link_count > MAX_LINK_COUNT)
606	    {
607	      errnum = ERR_SYMLINK_LOOP;
608	      return 0;
609	    }
610
611	  /* Find out how long our remaining name is. */
612	  len = 0;
613	  while (dirname[len] && !isspace (dirname[len]))
614	    len++;
615
616	  /* Get the symlink size. */
617	  filemax = (INODE->i_size);
618	  if (filemax + len > sizeof (linkbuf) - 2)
619	    {
620	      errnum = ERR_FILELENGTH;
621	      return 0;
622	    }
623
624	  if (len)
625	    {
626	      /* Copy the remaining name to the end of the symlink data.
627	         Note that DIRNAME and LINKBUF may overlap! */
628	      memmove (linkbuf + filemax, dirname, len);
629	    }
630	  linkbuf[filemax + len] = '\0';
631
632	  /* Read the symlink data. */
633	  if (! ext2_is_fast_symlink ())
634	    {
635	      /* Read the necessary blocks, and reset the file pointer. */
636	      len = grub_read (linkbuf, filemax);
637	      filepos = 0;
638	      if (!len)
639		return 0;
640	    }
641	  else
642	    {
643	      /* Copy the data directly from the inode. */
644	      len = filemax;
645	      memmove (linkbuf, (char *) INODE->i_block, len);
646	    }
647
648#ifdef E2DEBUG
649	  printf ("symlink=%s\n", linkbuf);
650#endif
651
652	  dirname = linkbuf;
653	  if (*dirname == '/')
654	    {
655	      /* It's an absolute link, so look it up in root. */
656	      current_ino = EXT2_ROOT_INO;
657	      updir_ino = current_ino;
658	    }
659	  else
660	    {
661	      /* Relative, so look it up in our parent directory. */
662	      current_ino = updir_ino;
663	    }
664
665	  /* Try again using the new name. */
666	  continue;
667	}
668
669      /* if end of filename, INODE points to the file's inode */
670      if (!*dirname || isspace (*dirname))
671	{
672	  if (!S_ISREG (INODE->i_mode))
673	    {
674	      errnum = ERR_BAD_FILETYPE;
675	      return 0;
676	    }
677
678	  filemax = (INODE->i_size);
679	  return 1;
680	}
681
682      /* else we have to traverse a directory */
683      updir_ino = current_ino;
684
685      /* skip over slashes */
686      while (*dirname == '/')
687	dirname++;
688
689      /* if this isn't a directory of sufficient size to hold our file, abort */
690      if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode))
691	{
692	  errnum = ERR_BAD_FILETYPE;
693	  return 0;
694	}
695
696      /* skip to next slash or end of filename (space) */
697      for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/';
698	   rest++);
699
700      /* look through this directory and find the next filename component */
701      /* invariant: rest points to slash after the next filename component */
702      *rest = 0;
703      loc = 0;
704
705      do
706	{
707
708#ifdef E2DEBUG
709	  printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc);
710#endif /* E2DEBUG */
711
712	  /* if our location/byte offset into the directory exceeds the size,
713	     give up */
714	  if (loc >= INODE->i_size)
715	    {
716	      if (print_possibilities < 0)
717		{
718# if 0
719		  putchar ('\n');
720# endif
721		}
722	      else
723		{
724		  errnum = ERR_FILE_NOT_FOUND;
725		  *rest = ch;
726		}
727	      return (print_possibilities < 0);
728	    }
729
730	  /* else, find the (logical) block component of our location */
731	  blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK);
732
733	  /* we know which logical block of the directory entry we are looking
734	     for, now we have to translate that to the physical (fs) block on
735	     the disk */
736	  map = ext2fs_block_map (blk);
737#ifdef E2DEBUG
738	  printf ("fs block=%d\n", map);
739#endif /* E2DEBUG */
740	  mapblock2 = -1;
741	  if ((map < 0) || !ext2_rdfsb (map, DATABLOCK2))
742	    {
743	      errnum = ERR_FSYS_CORRUPT;
744	      *rest = ch;
745	      return 0;
746	    }
747	  off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1);
748	  dp = (struct ext2_dir_entry *) (DATABLOCK2 + off);
749	  /* advance loc prematurely to next on-disk directory entry  */
750	  loc += dp->rec_len;
751
752	  /* NOTE: ext2fs filenames are NOT null-terminated */
753
754#ifdef E2DEBUG
755	  printf ("directory entry ino=%d\n", dp->inode);
756	  if (dp->inode)
757	    printf ("entry=%s\n", dp->name);
758#endif /* E2DEBUG */
759
760	  if (dp->inode)
761	    {
762	      int saved_c = dp->name[dp->name_len];
763
764	      dp->name[dp->name_len] = 0;
765	      str_chk = substring (dirname, dp->name);
766
767# ifndef STAGE1_5
768	      if (print_possibilities && ch != '/'
769		  && (!*dirname || str_chk <= 0))
770		{
771		  if (print_possibilities > 0)
772		    print_possibilities = -print_possibilities;
773		  print_a_completion (dp->name);
774		}
775# endif
776
777	      dp->name[dp->name_len] = saved_c;
778	    }
779
780	}
781      while (!dp->inode || (str_chk || (print_possibilities && ch != '/')));
782
783      current_ino = dp->inode;
784      *(dirname = rest) = ch;
785    }
786  /* never get here */
787}
788
789#endif /* FSYS_EXT2_FS */
790