diff --git a/BUGS b/BUGS index 435b1c326..fe92f40b5 100644 --- a/BUGS +++ b/BUGS @@ -1,6 +1,8 @@ - Known problems/bugs: + - GRUB cannot boot FreeBSD 3.4 and 4.0 directly. So you must + chain-load them instead. + - GRUB hangs up when accessing a disk via Adaptec AIC-7880 SCSI-controller in LBA mode. It is unknown if this is due to GRUB or the SCSI BIOS. For now, you need to disable the BIOS support for diff --git a/ChangeLog b/ChangeLog index 99024d595..28d1b4850 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,52 @@ +2000-02-17 OKUJI Yoshinori + + * configure.in (--enable-3c90x): Add -DINCLUDE_3C90X=1 instead + of -DINCLUDE_3C90x=1. This was just a typo. Reported by Per + Lundberg. + +2000-02-17 Jochen Hoenicke + + * stage2/fsys_fat.c (fat_read): Forgot to increase BUF. + (fat_dir): Use fat_read instead of grub_read; this makes + setting the FSMAX unnecessary. + (fat_mount): FSMAX is no longer set. + +2000-02-16 Jochen Hoenicke + + * stage2/char_io.c (grub_isspace): Make carriage return a white + space. + + * stage2/fsys_fat.c (fat_dir): Long filename support. + (NAME_BUF): New macro. + * stage2/fat.h (FAT_LONGDIR_ID, FAT_LONGDIR_ALIASCHECKSUM, + FAT_ATTRIB_LONGNAME): New Macros. + + * stage2/fsys_fat.c (fat_create_blocklist): Deleted, instead + fat_read is implemented. + (fat_read): new function. + * stage2/disk_io.c (fsys_table): Use fat_read. + * stage2/filesys.h: Declare fat_read, remove NO_BLOCK_FILES + hack. + * stage2/Makefile.am: Compile fat_stage1_5 with + -DNO_BLOCK_FILES=1. + + * stage2/fat.h (fat_bpb): New structure describing bpb. + (FAT_CVT_U16): New macro. + (FAT_BPB_CHECK_SIG, FAT_BPB_NUM_SECTORS, + FAT_BPB_BYTES_PER_SECTOR, FAT_BPB_SECT_PER_CLUS, FAT_BPB_NUMFAT, + FAT_BPB_RESERVED_SECTORS, FAT_BPB_FAT_SECTORS_16, + FAT_BPB_FAT_SECTORS_32, FAT_BPB_IS_FAT32, FAT_BPB_FAT_SECTORS, + FAT_BPB_FAT_START, FAT_BPB_ROOT_DIR_CLUSTER, + FAT_BPB_HIDDEN_SECTORS, FAT_BPB_ROOT_DIR_START, + FAT_BPB_ROOT_DIR_LENGTH, FAT_BPB_DATA_OFFSET, + FAT_BPB_NUM_CLUST): Macros removed. + * stage2/fsys_fat.c (fat_superblock): New structure containing + all info about currently mounted filesystem. + (FAT_SUPER): New Macro. + (BPB): Macro removod. + (fat_mount): Use fat_bpb structure, fill FAT_SUPER. + (fat_read, fat_dir): Use FAT_SUPER info. + 2000-02-16 OKUJI Yoshinori Pass the boot partition information to a chain-loader, in the diff --git a/NEWS b/NEWS index 7c51f3439..343bc313a 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,7 @@ New in 0.5.94: check if GRUB accesses a drive in LBA mode by the command "geometry". * New commands "bootp", "dhcp" and "rarp" can be used to initialize a network device and get IP addresses from a network. +* Long filename support in the FAT filesystem is added. New in 0.5.93 - 1999-10-30: * ELF format of FreeBSD kernel is supported. diff --git a/configure b/configure index 02242f492..5fa861f5f 100644 --- a/configure +++ b/configure @@ -2479,7 +2479,7 @@ if test "${enable_3c90x+set}" = set; then fi if test "x$enable_3c90x" = xyes; then - NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C90x=1" + NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C90X=1" NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c90x.o" fi diff --git a/configure.in b/configure.in index a56d9297f..72aa14e91 100644 --- a/configure.in +++ b/configure.in @@ -254,7 +254,7 @@ fi AC_ARG_ENABLE(3c90x, [ --enable-3c90x enable 3Com90x driver]) if test "x$enable_3c90x" = xyes; then - NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C90x=1" + NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C90X=1" NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c90x.o" fi diff --git a/stage2/Makefile.am b/stage2/Makefile.am index 95c504d83..a34cb2bc0 100644 --- a/stage2/Makefile.am +++ b/stage2/Makefile.am @@ -80,7 +80,8 @@ e2fs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK) # For fat_stage1_5 target. fat_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ stage1_5.c fsys_fat.c bios.c -fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 +fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \ + -DNO_BLOCK_FILES=1 fat_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK) # For ffs_stage1_5 target. diff --git a/stage2/Makefile.in b/stage2/Makefile.in index bb10e68ff..13dd014f5 100644 --- a/stage2/Makefile.in +++ b/stage2/Makefile.in @@ -158,7 +158,8 @@ e2fs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK) fat_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \ stage1_5.c fsys_fat.c bios.c -fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 +fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \ + -DNO_BLOCK_FILES=1 fat_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK) # For ffs_stage1_5 target. diff --git a/stage2/char_io.c b/stage2/char_io.c index d6115c02d..ba4995ec2 100644 --- a/stage2/char_io.c +++ b/stage2/char_io.c @@ -675,7 +675,7 @@ grub_tolower (int c) int grub_isspace (int c) { - if (c == ' ' || c == '\t' || c == '\n') + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') return 1; return 0; diff --git a/stage2/disk_io.c b/stage2/disk_io.c index 9c6ece9dd..5936bbcd3 100644 --- a/stage2/disk_io.c +++ b/stage2/disk_io.c @@ -45,7 +45,7 @@ struct fsys_entry fsys_table[NUM_FSYS + 1] = {"tftp", tftp_mount, tftp_read, tftp_dir, tftp_close}, # endif # ifdef FSYS_FAT - {"fat", fat_mount, 0, fat_dir, 0}, + {"fat", fat_mount, fat_read, fat_dir, 0}, # endif # ifdef FSYS_EXT2FS {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0}, diff --git a/stage2/fat.h b/stage2/fat.h index d9665ec06..651abb118 100644 --- a/stage2/fat.h +++ b/stage2/fat.h @@ -23,65 +23,44 @@ * of the partition. */ -#define FAT_BPB_SIGNATURE 0x29 +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; -/* is checking for this signature thing even valid? */ -#define FAT_BPB_CHECK_SIG(bpb) \ - (*((unsigned char *) (((int)bpb) + 38)) == FAT_BPB_SIGNATURE) - -#define FAT_BPB_NUM_SECTORS(bpb) \ - ( *((unsigned short *) (((int)bpb) + 19)) ? \ - *((unsigned short *) (((int)bpb) + 19)) : \ - *((unsigned long *) (((int)bpb) + 32)) ) - -#define FAT_BPB_BYTES_PER_SECTOR(bpb) \ - (*((unsigned short *) (((int)bpb) + 11))) -#define FAT_BPB_SECT_PER_CLUST(bpb) \ - (*((unsigned char *) (((int)bpb) + 13))) -#define FAT_BPB_NUM_FAT(bpb) \ - (*((unsigned char *) (((int)bpb) + 16))) - -#define FAT_BPB_RESERVED_SECTORS(bpb) \ - (*((unsigned short *) (((int)bpb) + 14))) -#define FAT_BPB_FAT_SECTORS_16(bpb) \ - (*((unsigned short *) (((int)bpb) + 22))) -#define FAT_BPB_FAT_SECTORS_32(bpb) \ - (*((unsigned long *) (((int)bpb) + 36))) -#define FAT_BPB_IS_FAT32(bpb) \ - (FAT_BPB_FAT_SECTORS_16(bpb) == 0) -#define FAT_BPB_FAT_SECTORS(bpb) \ - (FAT_BPB_FAT_SECTORS_16(bpb) \ - ? FAT_BPB_FAT_SECTORS_16(bpb) : FAT_BPB_FAT_SECTORS_32(bpb)) -#define FAT_BPB_FAT_START(bpb) FAT_BPB_RESERVED_SECTORS(bpb) - -#define FAT_BPB_ROOT_DIR_CLUSTER(bpb) \ - (*((unsigned long *) (((int)bpb) + 44))) - -/* - * This appears to be a MAJOR kludge!! Don't use it if possible... +/* Note that some shorts are not aligned, and must therefore + * be declared as array of two bytes. */ -#define FAT_BPB_HIDDEN_SECTORS(bpb) \ - (*((unsigned long *) (((int)bpb) + 28))) +struct fat_bpb { + __s8 ignored[3]; /* Boot strap short or near jump */ + __s8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 bytes_per_sect[2]; /* bytes per logical sector */ + __u8 sects_per_clust;/* sectors/cluster */ + __u8 reserved_sects[2]; /* reserved sectors */ + __u8 num_fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 short_sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 long_sectors; /* number of sectors (if short_sectors == 0) */ -#define FAT_BPB_ROOT_DIR_START(bpb) \ - ( FAT_BPB_NUM_FAT(bpb) * FAT_BPB_FAT_SECTORS(bpb) \ - + FAT_BPB_FAT_START(bpb) ) + /* The following fields are only used by FAT32 */ + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u16 reserved2[6]; /* Unused */ +}; -#define FAT_BPB_ROOT_DIR_LENGTH(bpb) \ - ( (*((unsigned short *) (((int)bpb) + 17)) + 0xF) >> 4 ) - -#define FAT_BPB_DATA_OFFSET(bpb) \ - ( FAT_BPB_ROOT_DIR_START(bpb) + FAT_BPB_ROOT_DIR_LENGTH(bpb) ) - -#define FAT_BPB_NUM_CLUST(bpb) \ - ( ( FAT_BPB_NUM_SECTORS(bpb) - FAT_BPB_DATA_OFFSET(bpb) ) \ - / FAT_BPB_SECT_PER_CLUST(bpb) ) - -/* - * Defines minimum disk size to be considered a FAT partition - */ - -#define FAT_MIN_NUM_SECTORS 720 /* 360 K disk */ +#define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr)) /* * Defines how to differentiate a 12-bit and 16-bit FAT. @@ -89,13 +68,6 @@ #define FAT_MAX_12BIT_CLUST 4087 /* 4085 + 2 */ -#define FAT_BPB_FLOPPY_NUM_SECTORS(bpb) \ - ( *((unsigned short *) (((int)bpb) + 19)) \ - && !*((unsigned long *) (((int)bpb) + 32)) \ - && *((unsigned short *) (((int)bpb) + 19)) >= FAT_MIN_NUM_SECTORS \ - && ((*((unsigned short *) (((int)bpb) + 19)) - FAT_BPB_DATA_OFFSET(bpb)) \ - / FAT_BPB_SECT_PER_CLUST(bpb)) < (FAT_BPB_FAT_SECTORS(bpb) * 342) ) - /* * Defines for the file "attribute" byte */ @@ -103,7 +75,7 @@ #define FAT_ATTRIB_OK_MASK 0x37 #define FAT_ATTRIB_NOT_OK_MASK 0xC8 #define FAT_ATTRIB_DIR 0x10 - +#define FAT_ATTRIB_LONGNAME 0x0F /* * Defines for FAT directory entries @@ -115,9 +87,14 @@ (*((unsigned char *) (entry+11))) #define FAT_DIRENTRY_VALID(entry) \ ( ((*((unsigned char *) entry)) != 0) \ - & ((*((unsigned char *) entry)) != 0xE5) \ - & !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) + && ((*((unsigned char *) entry)) != 0xE5) \ + && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) #define FAT_DIRENTRY_FIRST_CLUSTER(entry) \ ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16)) #define FAT_DIRENTRY_FILELENGTH(entry) \ (*((unsigned long *) (entry+28))) + +#define FAT_LONGDIR_ID(entry) \ + (*((unsigned char *) (entry))) +#define FAT_LONGDIR_ALIASCHECKSUM(entry) \ + (*((unsigned char *) (entry+13))) diff --git a/stage2/filesys.h b/stage2/filesys.h index 7c3ce0987..282b3863f 100644 --- a/stage2/filesys.h +++ b/stage2/filesys.h @@ -33,11 +33,8 @@ int ffs_dir (char *dirname); #ifdef FSYS_FAT #define FSYS_FAT_NUM 1 int fat_mount (void); -/* XX FAT filesystem uses block filesystem code for read! */ +int fat_read (char *buf, int len); int fat_dir (char *dirname); -#ifdef NO_BLOCK_FILES -#undef NO_BLOCK_FILES -#endif /* NO_BLOCK_FILES */ #else #define FSYS_FAT_NUM 0 #endif diff --git a/stage2/fsys_fat.c b/stage2/fsys_fat.c index 50c8a3b88..7c1be888f 100644 --- a/stage2/fsys_fat.c +++ b/stage2/fsys_fat.c @@ -1,6 +1,7 @@ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 1996 Erich Boleyn + * Copyright (C) 2000 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,161 +21,247 @@ #ifdef FSYS_FAT #include "shared.h" - #include "filesys.h" - #include "fat.h" -static int num_clust; -static int mapblock; -static int data_offset; -static int fat_size; -static int root_dir; +struct fat_superblock +{ + int fat_offset; + int fat_length; + int fat_size; + int root_offset; + int root_max; + int data_offset; + + int num_sectors; + int num_clust; + int sects_per_clust; + int sectsize_bits; + int clustsize_bits; + int root_cluster; + + int cached_fat; + int file_cluster; + int current_cluster_num; + int current_cluster; +}; /* pointer(s) into filesystem info buffer for DOS stuff */ -#define BPB ( FSYS_BUF + 32256 ) /* 512 bytes long */ -#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ +#define FAT_SUPER ( (struct fat_superblock *) \ + ( FSYS_BUF + 32256) )/* 512 bytes long */ +#define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ +#define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ + +#define FAT_CACHE_SIZE 2048 int fat_mount (void) { - int retval = 1; - - if ((((current_drive & 0x80) || (current_slice != 0)) - && ! IS_PC_SLICE_TYPE_FAT (current_slice) - && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) - || !devread (0, 0, SECTOR_SIZE, (char *) BPB) - || FAT_BPB_BYTES_PER_SECTOR (BPB) != SECTOR_SIZE - || FAT_BPB_SECT_PER_CLUST (BPB) < 1 - || (FAT_BPB_SECT_PER_CLUST (BPB) & (FAT_BPB_SECT_PER_CLUST (BPB) - 1)) - || !((current_drive & 0x80) - || FAT_BPB_FLOPPY_NUM_SECTORS (BPB))) - retval = 0; - else + struct fat_bpb bpb; + int i; + + /* Check partition type for harddisk */ + if (((current_drive & 0x80) || (current_slice != 0)) + && ! IS_PC_SLICE_TYPE_FAT (current_slice) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_MSDOS))) + return 0; + + /* Read bpb */ + if (!devread (0, 0, sizeof(bpb), (char *) &bpb)) + return 0; + + for (i = 0; (1 << i) < FAT_CVT_U16(bpb.bytes_per_sect); i++) + {} + FAT_SUPER->sectsize_bits = i; + for (i = 0; (1 << i) < bpb.sects_per_clust; i++) + {} + FAT_SUPER->clustsize_bits = FAT_SUPER->sectsize_bits + i; + + /* Fill in info about super block */ + FAT_SUPER->num_sectors = FAT_CVT_U16(bpb.short_sectors) + ? FAT_CVT_U16(bpb.short_sectors) : bpb.long_sectors; + + /* FAT offset and length */ + FAT_SUPER->fat_offset = FAT_CVT_U16(bpb.reserved_sects); + FAT_SUPER->fat_length = + bpb.fat_length ? bpb.fat_length : bpb.fat32_length; + + /* Rootdir offset and length for FAT12/16 */ + FAT_SUPER->root_offset = + FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; + FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); + + /* Data offset and number of clusters */ + FAT_SUPER->data_offset = + FAT_SUPER->root_offset + + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; + FAT_SUPER->num_clust = + (FAT_SUPER->num_sectors - FAT_SUPER->data_offset) / bpb.sects_per_clust; + FAT_SUPER->sects_per_clust = bpb.sects_per_clust; + + if (!bpb.fat_length) { - mapblock = -4096; - data_offset = FAT_BPB_DATA_OFFSET (BPB); - num_clust = FAT_BPB_NUM_CLUST (BPB) + 2; - root_dir = -1; - if (FAT_BPB_IS_FAT32 (BPB)) - { - fat_size = 8; - root_dir = FAT_BPB_ROOT_DIR_CLUSTER (BPB); - } - else if (num_clust > FAT_MAX_12BIT_CLUST) - fat_size = 4; + /* This is a FAT32 */ + if (FAT_CVT_U16(bpb.dir_entries)) + return 0; + FAT_SUPER->fat_size = 8; + FAT_SUPER->root_cluster = bpb.root_cluster; + } + else + { + if (!FAT_SUPER->root_max) + return 0; + + FAT_SUPER->root_cluster = -1; + if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) + FAT_SUPER->fat_size = 4; else - fat_size = 3; + FAT_SUPER->fat_size = 3; } - - return retval; + + + /* Now do some sanity checks */ + + if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) + || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE + || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)) + || FAT_SUPER->num_clust <= 0 + || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) + > FAT_SUPER->fat_length)) + return 0; + + FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; + return 1; } - -static int -fat_create_blocklist (int first_fat_entry) +int +fat_read (char *buf, int len) { - BLK_CUR_FILEPOS = 0; - BLK_CUR_BLKNUM = 0; - BLK_CUR_BLKLIST = BLK_BLKLIST_START; - block_file = 1; - filepos = 0; - - if (first_fat_entry < 0) + int logical_clust; + int offset; + int ret = 0; + int size; + + if (FAT_SUPER->file_cluster < 0) { - /* root directory */ - - BLK_BLKSTART (BLK_BLKLIST_START) = FAT_BPB_ROOT_DIR_START (BPB); - fsmax = filemax = SECTOR_SIZE * (BLK_BLKLENGTH (BLK_BLKLIST_START) - = FAT_BPB_ROOT_DIR_LENGTH (BPB)); - return 1; + /* root directory for non-fat16 */ + size = FAT_SUPER->root_max - filepos; + if (size > len) + size = len; + if (!devread(FAT_SUPER->root_offset, filepos, size, buf)) + return 0; + filepos += size; + return size; } - else - /* any real directory/file */ + + logical_clust = filepos >> FAT_SUPER->clustsize_bits; + offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); + if (logical_clust < FAT_SUPER->current_cluster_num) { - int blk_cur_blklist = BLK_BLKLIST_START, blk_cur_blknum; - int last_fat_entry, new_mapblock; - - fsmax = 0; - - do - { - BLK_BLKSTART (blk_cur_blklist) - = (first_fat_entry - 2) * FAT_BPB_SECT_PER_CLUST (BPB) + data_offset; - blk_cur_blknum = 0; - - do - { - blk_cur_blknum += FAT_BPB_SECT_PER_CLUST (BPB); - last_fat_entry = first_fat_entry; - - /* - * Do FAT table translation here! - */ - - new_mapblock = (last_fat_entry * fat_size) >> 1; - if (new_mapblock > (mapblock + SECTOR_SIZE * 4 - 3) - || new_mapblock < (mapblock + 3)) - { - mapblock = ((new_mapblock < 6) ? 0 : - ((new_mapblock - 6) & ~0x1FF)); - if (!devread ((mapblock >> 9) + FAT_BPB_FAT_START (BPB), - 0, SECTOR_SIZE * 4, (char *) FAT_BUF)) - return 0; - } - - first_fat_entry - = *((unsigned long *) (FAT_BUF + (new_mapblock - mapblock))); - - if (fat_size == 3) - { - if (last_fat_entry & 1) - first_fat_entry >>= 4; - - first_fat_entry &= 0xFFF; - } - else if (fat_size == 4) - first_fat_entry &= 0xFFFF; - - if (first_fat_entry < 2) - { - errnum = ERR_FSYS_CORRUPT; - return 0; - } - } - while (first_fat_entry == (last_fat_entry + 1) - && first_fat_entry < num_clust); - - BLK_BLKLENGTH (blk_cur_blklist) = blk_cur_blknum; - fsmax += blk_cur_blknum * SECTOR_SIZE; - blk_cur_blklist += BLK_BLKLIST_INC_VAL; - } - while (first_fat_entry < num_clust && blk_cur_blklist < (FAT_BUF - 7)); + FAT_SUPER->current_cluster_num = 0; + FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; } - - return first_fat_entry >= num_clust; + + while (len > 0) + { + int sector; + while (logical_clust > FAT_SUPER->current_cluster_num) + { + /* calculate next cluster */ + int fat_entry = + FAT_SUPER->current_cluster * FAT_SUPER->fat_size; + int next_cluster; + int cached_pos = (fat_entry - FAT_SUPER->cached_fat); + + if (cached_pos < 0 || + (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) + { + FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); + cached_pos = (fat_entry - FAT_SUPER->cached_fat); + sector = FAT_SUPER->fat_offset + + FAT_SUPER->cached_fat / (2*SECTOR_SIZE); + if (!devread (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) + return 0; + } + next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1)); + if (FAT_SUPER->fat_size == 3) + { + if (cached_pos & 1) + next_cluster >>= 4; + next_cluster &= 0xFFF; + } + else if (FAT_SUPER->fat_size == 4) + next_cluster &= 0xFFFF; + + if (next_cluster < 2) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + if (next_cluster >= FAT_SUPER->num_clust) + return ret; + + FAT_SUPER->current_cluster = next_cluster; + FAT_SUPER->current_cluster_num++; + } + + sector = FAT_SUPER->data_offset + + ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits + - FAT_SUPER->sectsize_bits)); + size = (1 << FAT_SUPER->clustsize_bits) - offset; + if (size > len) + size = len; + +#ifndef STAGE1_5 + disk_read_func = disk_read_hook; +#endif /* STAGE1_5 */ + + devread(sector, offset, size, buf); + +#ifndef STAGE1_5 + disk_read_func = NULL; +#endif /* STAGE1_5 */ + + len -= size; + buf += size; + ret += size; + filepos += size; + logical_clust++; + offset = 0; + } + return errnum ? 0 : ret; } - -/* XX FAT filesystem uses the block-list filesystem read function, - so none is defined here. */ - - int fat_dir (char *dirname) { - char *rest, ch, filename[13], dir_buf[FAT_DIRENTRY_LENGTH]; - int attrib = FAT_ATTRIB_DIR, map = root_dir; - -/* main loop to find desired directory entry */ -loop: - - if (!fat_create_blocklist (map)) - return 0; - + char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; + char *filename = (char *) NAME_BUF; + int attrib = FAT_ATTRIB_DIR; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif + + /* XXX I18N: + * the positions 2,4,6 etc are high bytes of a 16 bit unicode char + */ + static unsigned char longdir_pos[] = + { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; + int slot = -2; + int alias_checksum = -1; + + FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; + filepos = 0; + FAT_SUPER->current_cluster_num = MAXINT; + + /* main loop to find desired directory entry */ + loop: + /* if we have a real file (and we're not just printing possibilities), then this is where we want to exit */ - + if (!*dirname || isspace (*dirname)) { if (attrib & FAT_ATTRIB_DIR) @@ -182,33 +269,40 @@ loop: errnum = ERR_BAD_FILETYPE; return 0; } - + return 1; } - + /* continue with the file/directory name interpretation */ - + while (*dirname == '/') dirname++; - - filemax = fsmax; - - if (!filemax || !(attrib & FAT_ATTRIB_DIR)) + + if (!(attrib & FAT_ATTRIB_DIR)) { errnum = ERR_BAD_FILETYPE; return 0; } - + /* Directories don't have a file size */ + filemax = MAXINT; + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++); - + *rest = 0; - - do + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif + + while (1) { - if (grub_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH) + if (fat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH + || dir_buf[0] == 0) { if (!errnum) { +# ifndef STAGE1_5 if (print_possibilities < 0) { #if 0 @@ -216,54 +310,123 @@ loop: #endif return 1; } - +# endif /* STAGE1_5 */ + errnum = ERR_FILE_NOT_FOUND; *rest = ch; } - + return 0; } - + + if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) + { + /* This is a long filename. The filename is build from back + * to front and may span multiple entries. To bind these + * entries together they all contain the same checksum over + * the short alias. + * + * The id field tells if this is the first entry (the last + * part) of the long filename, and also at which offset this + * belongs. + * + * We just write the part of the long filename this entry + * describes and continue with the next dir entry. + */ + int i, offset; + unsigned char id = FAT_LONGDIR_ID(dir_buf); + + if ((id & 0x40)) + { + id &= 0x3f; + slot = id; + filename[slot * 13] = 0; + alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); + } + + if (id != slot || slot == 0 + || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) + { + alias_checksum = -1; + continue; + } + + slot--; + offset = slot * 13; + + for (i=0; i < 13; i++) + filename[offset+i] = dir_buf[longdir_pos[i]]; + continue; + } + if (!FAT_DIRENTRY_VALID (dir_buf)) continue; - + + if (alias_checksum != -1 && slot == 0) + { + int i; + unsigned char sum; + + slot = -2; + for (sum = 0, i = 0; i< 11; i++) + sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; + + if (sum == alias_checksum) + { +# ifndef STAGE1_5 + if (do_possibilities) + goto print_filename; +# endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; + } + } + /* XXX convert to 8.3 filename format here */ { int i, j, c; - + for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i])) - && !isspace (c); i++); - + && !isspace (c); i++); + filename[i++] = '.'; - + for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j])) - && !isspace (c); j++); - + && !isspace (c); j++); + if (j == 0) i--; - + filename[i + j] = 0; } - + # ifndef STAGE1_5 - if (print_possibilities && ch != '/' - && (!*dirname || substring (dirname, filename) <= 0)) + if (do_possibilities) { - if (print_possibilities > 0) - print_possibilities = -print_possibilities; - print_a_completion (filename); + print_filename: + if (substring (dirname, filename) <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (filename); + } + continue; } # endif /* STAGE1_5 */ + + if (substring (dirname, filename) == 0) + break; } - while (substring (dirname, filename) != 0 || - (print_possibilities && ch != '/')); - + *(dirname = rest) = ch; - + attrib = FAT_DIRENTRY_ATTRIB (dir_buf); filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); - map = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); - + filepos = 0; + FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); + FAT_SUPER->current_cluster_num = MAXINT; + /* go back to main loop at top of function */ goto loop; }