Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/fs/vfat/fsys_vfat.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /*
0019  * Based on:
0020  *
0021  *  GRUB  --  GRand Unified Bootloader
0022  *  Copyright (C) 2000,2001,2005   Free Software Foundation, Inc.
0023  *
0024  *  This program is free software; you can redistribute it and/or modify
0025  *  it under the terms of the GNU General Public License as published by
0026  *  the Free Software Foundation; either version 2 of the License, or
0027  *  (at your option) any later version.
0028  *
0029  *  This program is distributed in the hope that it will be useful,
0030  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0031  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0032  *  GNU General Public License for more details.
0033  *
0034  *  You should have received a copy of the GNU General Public License
0035  *  along with this program; if not, write to the Free Software
0036  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0037  */
0038 
0039 #include "fs/filesys.h"
0040 #include "drivers/usb/umsc.h"
0041 #include "arch/i386.h"
0042 #include "util/printf.h"
0043 #include "types.h"
0044 
0045 //#define DEBUG_VFAT
0046 #ifdef DEBUG_VFAT
0047 #define DLOG(fmt,...) DLOG_PREFIX("vfat",fmt,##__VA_ARGS__)
0048 #else
0049 #define DLOG(fmt,...) ;
0050 #endif
0051 
0052 #define UMSC_DEVICE_INDEX 0
0053 #define VFAT_FIRST_PARTITION 63
0054 
0055 static int
0056 devread_vfat (int sector, int byte_offset, int byte_len, char *buf)
0057 {
0058   uint8 s[512];
0059   int len = byte_len;
0060   sector+=VFAT_FIRST_PARTITION; /* offset into the first partition */
0061   DLOG ("fsys_vfat: devread_vfat (%d, %d, %d, %p)",
0062         sector, byte_offset, byte_len, buf);
0063   while (len > 0) {
0064     if (umsc_read_sector (UMSC_DEVICE_INDEX, sector, s, sizeof (s)) != sizeof (s))
0065       return 0;
0066     int seclen;
0067     if (len > sizeof (s) - byte_offset)
0068       seclen = sizeof (s) - byte_offset;
0069     else
0070       seclen = len;
0071     memcpy (buf, &s[byte_offset], seclen);
0072     len -= seclen;
0073     buf += seclen;
0074     sector++;
0075     byte_offset = 0;
0076   }
0077   return byte_len;
0078 }
0079 
0080 static int
0081 tolower (int c)
0082 {
0083   if (c >= 'A' && c <= 'Z')
0084     return (c + ('a' - 'A'));
0085 
0086   return c;
0087 }
0088 
0089 static int
0090 isspace (int c)
0091 {
0092   switch (c)
0093     {
0094     case ' ':
0095     case '\t':
0096     case '\r':
0097     case '\n':
0098       return 1;
0099     default:
0100       break;
0101     }
0102 
0103   return 0;
0104 }
0105 
0106 static int
0107 substring (const char *s1, const char *s2)
0108 {
0109   while (*s1 == *s2)
0110     {
0111       /* The strings match exactly. */
0112       if (! *(s1++))
0113     return 0;
0114       s2 ++;
0115     }
0116 
0117   /* S1 is a substring of S2. */
0118   if (*s1 == 0)
0119     return -1;
0120 
0121   /* S1 isn't a substring. */
0122   return 1;
0123 }
0124 
0125 #ifndef NULL
0126 #define NULL ((void *)0)
0127 #endif
0128 
0129 #define MAXINT     0x7FFFFFFF
0130 
0131 /********************
0132  * #include "fat.h" *
0133  ********************/
0134 
0135 /*
0136  *  Defines for the FAT BIOS Parameter Block (embedded in the first block
0137  *  of the partition.
0138  */
0139 
0140 typedef __signed__ char __s8;
0141 typedef unsigned char __u8;
0142 typedef __signed__ short __s16;
0143 typedef unsigned short __u16;
0144 typedef __signed__ int __s32;
0145 typedef unsigned int __u32;
0146 
0147 /* Note that some shorts are not aligned, and must therefore
0148  * be declared as array of two bytes.
0149  */
0150 struct fat_bpb {
0151         __s8    ignored[3];     /* Boot strap short or near jump */
0152         __s8    system_id[8];   /* Name - can be used to special case
0153                                    partition manager volumes */
0154         __u8    bytes_per_sect[2];      /* bytes per logical sector */
0155         __u8    sects_per_clust;/* sectors/cluster */
0156         __u8    reserved_sects[2];      /* reserved sectors */
0157         __u8    num_fats;       /* number of FATs */
0158         __u8    dir_entries[2]; /* root directory entries */
0159         __u8    short_sectors[2];       /* number of sectors */
0160         __u8    media;          /* media code (unused) */
0161         __u16   fat_length;     /* sectors/FAT */
0162         __u16   secs_track;     /* sectors per track */
0163         __u16   heads;          /* number of heads */
0164         __u32   hidden;         /* hidden sectors (unused) */
0165         __u32   long_sectors;   /* number of sectors (if short_sectors == 0) */
0166 
0167         /* The following fields are only used by FAT32 */
0168         __u32   fat32_length;   /* sectors/FAT */
0169         __u16   flags;          /* bit 8: fat mirroring, low 4: active fat */
0170         __u8    version[2];     /* major, minor filesystem version */
0171         __u32   root_cluster;   /* first cluster in root directory */
0172         __u16   info_sector;    /* filesystem info sector */
0173         __u16   backup_boot;    /* backup boot sector */
0174         __u16   reserved2[6];   /* Unused */
0175 };
0176 
0177 #define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr))
0178 
0179 /*
0180  *  Defines how to differentiate a 12-bit and 16-bit FAT.
0181  */
0182 
0183 #define FAT_MAX_12BIT_CLUST       4087  /* 4085 + 2 */
0184 
0185 /*
0186  *  Defines for the file "attribute" byte
0187  */
0188 
0189 #define FAT_ATTRIB_OK_MASK        0x37
0190 #define FAT_ATTRIB_NOT_OK_MASK    0xC8
0191 #define FAT_ATTRIB_DIR            0x10
0192 #define FAT_ATTRIB_LONGNAME       0x0F
0193 
0194 /*
0195  *  Defines for FAT directory entries
0196  */
0197 
0198 #define FAT_DIRENTRY_LENGTH       32
0199 
0200 #define FAT_DIRENTRY_ATTRIB(entry) \
0201   (*((unsigned char *) (entry+11)))
0202 #define FAT_DIRENTRY_VALID(entry) \
0203   ( ((*((unsigned char *) entry)) != 0) \
0204     && ((*((unsigned char *) entry)) != 0xE5) \
0205     && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) )
0206 #define FAT_DIRENTRY_FIRST_CLUSTER(entry) \
0207   ((*((unsigned short *) (entry+26)))+(*((unsigned short *) (entry+20)) << 16))
0208 #define FAT_DIRENTRY_FILELENGTH(entry) \
0209   (*((unsigned long *) (entry+28)))
0210 
0211 #define FAT_LONGDIR_ID(entry) \
0212   (*((unsigned char *) (entry)))
0213 #define FAT_LONGDIR_ALIASCHECKSUM(entry) \
0214   (*((unsigned char *) (entry+13)))
0215 
0216 struct fat_superblock
0217 {
0218   int fat_offset;
0219   int fat_length;
0220   int fat_size;
0221   int root_offset;
0222   int root_max;
0223   int data_offset;
0224 
0225   int num_sectors;
0226   int num_clust;
0227   int clust_eof_marker;
0228   int sects_per_clust;
0229   int sectsize_bits;
0230   int clustsize_bits;
0231   int root_cluster;
0232 
0233   int cached_fat;
0234   int file_cluster;
0235   int current_cluster_num;
0236   int current_cluster;
0237 };
0238 
0239 static int errnum;
0240 static char fsys_buf[0x8000];
0241 static int filepos;
0242 static int filemax;
0243 
0244 
0245 /* pointer(s) into filesystem info buffer for DOS stuff */
0246 #define FAT_SUPER ( (struct fat_superblock *) \
0247                     ( fsys_buf + 32256) )/* 512 bytes long */
0248 #define FAT_BUF   ( fsys_buf + 30208 )  /* 4 sector FAT buffer */
0249 #define NAME_BUF  ( fsys_buf + 29184 )  /* Filename buffer (833 bytes) */
0250 
0251 #define FAT_CACHE_SIZE 2048
0252 
0253 static __inline__ unsigned long
0254 log2 (unsigned long word)
0255 {
0256   __asm__ ("bsfl %1,%0"
0257            : "=r" (word)
0258            : "r" (word));
0259   return word;
0260 }
0261 
0262 static bool mounted = FALSE;
0263 int
0264 vfat_mount (void)
0265 {
0266   struct fat_bpb bpb;
0267   __u32 magic, first_fat;
0268 
0269   /* Read bpb */
0270   if (! devread_vfat (0, 0, sizeof (bpb), (char *) &bpb))
0271     return 0;
0272 
0273   /* Check if the number of sectors per cluster is zero here, to avoid
0274      zero division.  */
0275   if (bpb.sects_per_clust == 0)
0276     return 0;
0277 
0278   FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect));
0279   FAT_SUPER->clustsize_bits
0280     = FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust);
0281 
0282   /* Fill in info about super block */
0283   FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors)
0284     ? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors;
0285 
0286   /* FAT offset and length */
0287   FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects);
0288   FAT_SUPER->fat_length =
0289     bpb.fat_length ? bpb.fat_length : bpb.fat32_length;
0290 
0291   /* Rootdir offset and length for FAT12/16 */
0292   FAT_SUPER->root_offset =
0293     FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length;
0294   FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries);
0295 
0296   /* Data offset and number of clusters */
0297   FAT_SUPER->data_offset =
0298     FAT_SUPER->root_offset
0299     + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1;
0300   FAT_SUPER->num_clust =
0301     2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset)
0302          / bpb.sects_per_clust);
0303   FAT_SUPER->sects_per_clust = bpb.sects_per_clust;
0304 
0305   if (!bpb.fat_length)
0306     {
0307       /* This is a FAT32 */
0308       if (FAT_CVT_U16(bpb.dir_entries))
0309         return 0;
0310 
0311       if (bpb.flags & 0x0080)
0312         {
0313           /* FAT mirroring is disabled, get active FAT */
0314           int active_fat = bpb.flags & 0x000f;
0315           if (active_fat >= bpb.num_fats)
0316             return 0;
0317           FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length;
0318         }
0319 
0320       FAT_SUPER->fat_size = 8;
0321       FAT_SUPER->root_cluster = bpb.root_cluster;
0322 
0323       /* Yes the following is correct.  FAT32 should be called FAT28 :) */
0324       FAT_SUPER->clust_eof_marker = 0xffffff8;
0325     }
0326   else
0327     {
0328       if (!FAT_SUPER->root_max)
0329         return 0;
0330 
0331       FAT_SUPER->root_cluster = -1;
0332       if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST)
0333         {
0334           FAT_SUPER->fat_size = 4;
0335           FAT_SUPER->clust_eof_marker = 0xfff8;
0336         }
0337       else
0338         {
0339           FAT_SUPER->fat_size = 3;
0340           FAT_SUPER->clust_eof_marker = 0xff8;
0341         }
0342     }
0343 
0344   /* Now do some sanity checks */
0345 
0346   if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits)
0347       || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE
0348       || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits
0349                                        - FAT_SUPER->sectsize_bits))
0350       || FAT_SUPER->num_clust <= 2
0351       || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE)
0352           > FAT_SUPER->fat_length))
0353     return 0;
0354 
0355   /* kbs: Media check on first FAT entry [ported from PUPA] */
0356 
0357   if (!devread_vfat(FAT_SUPER->fat_offset, 0,
0358                sizeof(first_fat), (char *)&first_fat))
0359     return 0;
0360 
0361   if (FAT_SUPER->fat_size == 8)
0362     {
0363       first_fat &= 0x0fffffff;
0364       magic = 0x0fffff00;
0365     }
0366   else if (FAT_SUPER->fat_size == 4)
0367     {
0368       first_fat &= 0x0000ffff;
0369       magic = 0xff00;
0370     }
0371   else
0372     {
0373       first_fat &= 0x00000fff;
0374       magic = 0x0f00;
0375     }
0376 
0377   /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media
0378      descriptor, even if it is a so-called superfloppy (e.g. an USB key).
0379      The check may be too strict for this kind of stupid BIOSes, as
0380      they overwrite the media descriptor.  */
0381   if ((first_fat | 0x8) != (magic | bpb.media | 0x8))
0382     return 0;
0383 
0384   FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE;
0385   mounted = TRUE;
0386   return 1;
0387 }
0388 
0389 int
0390 vfat_read (char *buf, int len)
0391 {
0392   int logical_clust;
0393   int offset;
0394   int ret = 0;
0395   int size;
0396 
0397   errnum=0;
0398 
0399   if (FAT_SUPER->file_cluster < 0)
0400     {
0401       /* root directory for fat16 */
0402       size = FAT_SUPER->root_max - filepos;
0403       if (size > len)
0404         size = len;
0405       if (!devread_vfat(FAT_SUPER->root_offset, filepos, size, buf))
0406         return 0;
0407       filepos += size;
0408       return size;
0409     }
0410 
0411   logical_clust = filepos >> FAT_SUPER->clustsize_bits;
0412   offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1));
0413   if (logical_clust < FAT_SUPER->current_cluster_num)
0414     {
0415       FAT_SUPER->current_cluster_num = 0;
0416       FAT_SUPER->current_cluster = FAT_SUPER->file_cluster;
0417     }
0418 
0419   while (len > 0)
0420     {
0421       int sector;
0422       while (logical_clust > FAT_SUPER->current_cluster_num)
0423         {
0424           /* calculate next cluster */
0425           int fat_entry =
0426             FAT_SUPER->current_cluster * FAT_SUPER->fat_size;
0427           int next_cluster;
0428           int cached_pos = (fat_entry - FAT_SUPER->cached_fat);
0429 
0430           if (cached_pos < 0 ||
0431               (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE)
0432             {
0433               FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1));
0434               cached_pos = (fat_entry - FAT_SUPER->cached_fat);
0435               sector = FAT_SUPER->fat_offset
0436                 + FAT_SUPER->cached_fat / (2*SECTOR_SIZE);
0437               if (!devread_vfat (sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF))
0438                 return 0;
0439             }
0440           next_cluster = * (unsigned long *) (FAT_BUF + (cached_pos >> 1));
0441           if (FAT_SUPER->fat_size == 3)
0442             {
0443               if (cached_pos & 1)
0444                 next_cluster >>= 4;
0445               next_cluster &= 0xFFF;
0446             }
0447           else if (FAT_SUPER->fat_size == 4)
0448             next_cluster &= 0xFFFF;
0449 
0450           if (next_cluster >= FAT_SUPER->clust_eof_marker)
0451             return ret;
0452           if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust)
0453             {
0454               errnum = ERR_FSYS_CORRUPT;
0455               return 0;
0456             }
0457 
0458           FAT_SUPER->current_cluster = next_cluster;
0459           FAT_SUPER->current_cluster_num++;
0460         }
0461 
0462       sector = FAT_SUPER->data_offset +
0463         ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits
0464                                               - FAT_SUPER->sectsize_bits));
0465       size = (1 << FAT_SUPER->clustsize_bits) - offset;
0466       if (size > len)
0467         size = len;
0468 
0469       devread_vfat(sector, offset, size, buf);
0470 
0471       len -= size;
0472       buf += size;
0473       ret += size;
0474       filepos += size;
0475       logical_clust++;
0476       offset = 0;
0477     }
0478   return errnum ? 0 : ret;
0479 }
0480 
0481 int
0482 vfat_dir (char *dirname)
0483 {
0484   if (!mounted) vfat_mount ();
0485   DLOG ("vfat_dir (\"%s\")", dirname);
0486   char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH];
0487   char *filename = (char *) NAME_BUF;
0488   int attrib = FAT_ATTRIB_DIR;
0489 
0490   /* XXX I18N:
0491    * the positions 2,4,6 etc are high bytes of a 16 bit unicode char
0492    */
0493   static unsigned char longdir_pos[] =
0494   { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
0495   int slot = -2;
0496   int alias_checksum = -1;
0497 
0498   FAT_SUPER->file_cluster = FAT_SUPER->root_cluster;
0499   filepos = 0;
0500   FAT_SUPER->current_cluster_num = MAXINT;
0501 
0502   /* main loop to find desired directory entry */
0503  loop:
0504 
0505   /* if we have a real file (and we're not just printing possibilities),
0506      then this is where we want to exit */
0507 
0508   if (!*dirname || isspace (*dirname))
0509     {
0510       if (attrib & FAT_ATTRIB_DIR)
0511         {
0512           errnum = ERR_BAD_FILETYPE;
0513           return -1;
0514         }
0515 
0516       return filemax;
0517     }
0518 
0519   /* continue with the file/directory name interpretation */
0520 
0521   while (*dirname == '/')
0522     dirname++;
0523 
0524   if (!(attrib & FAT_ATTRIB_DIR))
0525     {
0526       errnum = ERR_BAD_FILETYPE;
0527       return -1;
0528     }
0529   /* Directories don't have a file size */
0530   filemax = MAXINT;
0531 
0532   for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++);
0533 
0534   *rest = 0;
0535 
0536   while (1)
0537     {
0538       if (vfat_read (dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH
0539           || dir_buf[0] == 0)
0540         {
0541           if (!errnum)
0542             {
0543               errnum = ERR_FILE_NOT_FOUND;
0544               *rest = ch;
0545             }
0546 
0547           return -1;
0548         }
0549 
0550       if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME)
0551         {
0552           /* This is a long filename.  The filename is build from back
0553            * to front and may span multiple entries.  To bind these
0554            * entries together they all contain the same checksum over
0555            * the short alias.
0556            *
0557            * The id field tells if this is the first entry (the last
0558            * part) of the long filename, and also at which offset this
0559            * belongs.
0560            *
0561            * We just write the part of the long filename this entry
0562            * describes and continue with the next dir entry.
0563            */
0564           int i, offset;
0565           unsigned char id = FAT_LONGDIR_ID(dir_buf);
0566 
0567           if ((id & 0x40))
0568             {
0569               id &= 0x3f;
0570               slot = id;
0571               filename[slot * 13] = 0;
0572               alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf);
0573             }
0574 
0575           if (id != slot || slot == 0
0576               || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf))
0577             {
0578               alias_checksum = -1;
0579               continue;
0580             }
0581 
0582           slot--;
0583           offset = slot * 13;
0584 
0585           for (i=0; i < 13; i++)
0586             filename[offset+i] = dir_buf[longdir_pos[i]];
0587           continue;
0588         }
0589 
0590       if (!FAT_DIRENTRY_VALID (dir_buf))
0591         continue;
0592 
0593       if (alias_checksum != -1 && slot == 0)
0594         {
0595           int i;
0596           unsigned char sum;
0597 
0598           slot = -2;
0599           for (sum = 0, i = 0; i< 11; i++)
0600             sum = ((sum >> 1) | (sum << 7)) + dir_buf[i];
0601 
0602           if (sum == alias_checksum)
0603             {
0604               if (substring (dirname, filename) == 0)
0605                 break;
0606             }
0607         }
0608 
0609       /* XXX convert to 8.3 filename format here */
0610       {
0611         int i, j, c;
0612 
0613         for (i = 0; i < 8 && (c = filename[i] = tolower (dir_buf[i]))
0614                && !isspace (c); i++);
0615 
0616         filename[i++] = '.';
0617 
0618         for (j = 0; j < 3 && (c = filename[i + j] = tolower (dir_buf[8 + j]))
0619                && !isspace (c); j++);
0620 
0621         if (j == 0)
0622           i--;
0623 
0624         filename[i + j] = 0;
0625       }
0626 
0627       if (substring (dirname, filename) == 0)
0628         break;
0629     }
0630 
0631   *(dirname = rest) = ch;
0632 
0633   attrib = FAT_DIRENTRY_ATTRIB (dir_buf);
0634   filemax = FAT_DIRENTRY_FILELENGTH (dir_buf);
0635   filepos = 0;
0636   FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf);
0637   FAT_SUPER->current_cluster_num = MAXINT;
0638 
0639   /* go back to main loop at top of function */
0640   goto loop;
0641 }
0642 
0643 /*
0644  * Local Variables:
0645  * indent-tabs-mode: nil
0646  * mode: C
0647  * c-file-style: "gnu"
0648  * c-basic-offset: 2
0649  * End:
0650  */
0651 
0652 /* vi: set et sw=2 sts=2: */