Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * dosio.c -- Disk I/O module for the ext2fs/DOS library.
      3  *
      4  * Copyright (c) 1997 by Theodore Ts'o.
      5  *
      6  * Copyright (c) 1997 Mark Habersack
      7  * This file may be distributed under the terms of the GNU Public License.
      8  *
      9  */
     10 
     11 #include <stdio.h>
     12 #include <bios.h>
     13 #include <string.h>
     14 #include <ctype.h>
     15 #include <io.h>
     16 #ifdef HAVE_ERRNO_H
     17 #include <errno.h>
     18 #endif
     19 
     20 #include <ext2fs/ext2_types.h>
     21 #include "utils.h"
     22 #include "dosio.h"
     23 #include "et/com_err.h"
     24 #include "ext2_err.h"
     25 #include "ext2fs/io.h"
     26 
     27 /*
     28  * Some helper macros
     29  */
     30 #define LINUX_EXT2FS       0x83
     31 #define LINUX_SWAP         0x82
     32 #define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_))
     33 #define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_))
     34 
     35 /*
     36  * Exported variables
     37  */
     38 unsigned long        _dio_error;
     39 unsigned long        _dio_hw_error;
     40 
     41 /*
     42  * Array of all opened partitions
     43  */
     44 static PARTITION        **partitions = NULL;
     45 static unsigned short   npart = 0; /* Number of mapped partitions */
     46 static PARTITION        *active = NULL;
     47 
     48 /*
     49  * I/O Manager routine prototypes
     50  */
     51 static errcode_t dos_open(const char *dev, int flags, io_channel *channel);
     52 static errcode_t dos_close(io_channel channel);
     53 static errcode_t dos_set_blksize(io_channel channel, int blksize);
     54 static errcode_t dos_read_blk(io_channel channel, unsigned long block,
     55                                              int count, void *buf);
     56 static errcode_t dos_write_blk(io_channel channel, unsigned long block,
     57                                int count, const void *buf);
     58 static errcode_t dos_flush(io_channel channel);
     59 
     60 static struct struct_io_manager struct_dos_manager = {
     61         EXT2_ET_MAGIC_IO_MANAGER,
     62         "DOS I/O Manager",
     63         dos_open,
     64         dos_close,
     65         dos_set_blksize,
     66         dos_read_blk,
     67         dos_write_blk,
     68         dos_flush
     69 };
     70 io_manager dos_io_manager = &struct_dos_manager;
     71 
     72 /*
     73  * Macro taken from unix_io.c
     74  */
     75 /*
     76  * For checking structure magic numbers...
     77  */
     78 
     79 #define EXT2_CHECK_MAGIC(struct, code) \
     80           if ((struct)->magic != (code)) return (code)
     81 
     82 /*
     83  * Calculates a CHS address of a sector from its LBA
     84  * offset for the given partition.
     85  */
     86 static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part)
     87 {
     88   unsigned long      abss;
     89 
     90   chs->offset = lba_addr & 0x000001FF;
     91   abss = (lba_addr >> 9) + part->start;
     92   chs->cyl    = abss / (part->sects * part->heads);
     93   chs->head   = (abss / part->sects) % part->heads;
     94   chs->sector = (abss % part->sects) + 1;
     95 }
     96 
     97 #ifdef __TURBOC__
     98 #pragma argsused
     99 #endif
    100 /*
    101  * Scans the passed partition table looking for *pno partition
    102  * that has LINUX_EXT2FS type.
    103  *
    104  * TODO:
    105  * For partition numbers >5 Linux uses DOS extended partitions -
    106  * dive into them an return an appropriate entry. Also dive into
    107  * extended partitions when scanning for a first Linux/ext2fs.
    108  */
    109 static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry,
    110                                           unsigned short phys,
    111                                           unsigned char *pno)
    112 {
    113   unsigned        i;
    114 
    115   if(*pno != 0xFF && *pno >= 5)
    116      return NULL; /* We don't support extended partitions for now */
    117 
    118   if(*pno != 0xFF)
    119   {
    120     if(pentry[*pno].type == LINUX_EXT2FS)
    121       return &pentry[*pno];
    122     else
    123     {
    124       if(!pentry[*pno].type)
    125         *pno = 0xFE;
    126       else if(pentry[*pno].type == LINUX_SWAP)
    127         *pno = 0xFD;
    128       return NULL;
    129     }
    130   }
    131 
    132   for(i = 0; i < 4; i++)
    133     if(pentry[i].type == LINUX_EXT2FS)
    134     {
    135       *pno = i;
    136       return &pentry[i];
    137     }
    138 
    139   return NULL;
    140 }
    141 
    142 /*
    143  * Allocate libext2fs structures associated with I/O manager
    144  */
    145 static io_channel alloc_io_channel(PARTITION *part)
    146 {
    147   io_channel     ioch;
    148 
    149   ioch = (io_channel)malloc(sizeof(struct struct_io_channel));
    150   if (!ioch)
    151 	  return NULL;
    152   memset(ioch, 0, sizeof(struct struct_io_channel));
    153   ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL;
    154   ioch->manager = dos_io_manager;
    155   ioch->name = (char *)malloc(strlen(part->dev)+1);
    156   if (!ioch->name) {
    157 	  free(ioch);
    158 	  return NULL;
    159   }
    160   strcpy(ioch->name, part->dev);
    161   ioch->private_data = part;
    162   ioch->block_size = 1024; /* The smallest ext2fs block size */
    163   ioch->read_error = 0;
    164   ioch->write_error = 0;
    165 
    166   return ioch;
    167 }
    168 
    169 #ifdef __TURBOC__
    170 #pragma argsused
    171 #endif
    172 /*
    173  * Open the 'name' partition, initialize all information structures
    174  * we need to keep and create libext2fs I/O manager.
    175  */
    176 static errcode_t dos_open(const char *dev, int flags, io_channel *channel)
    177 {
    178   unsigned char  *tmp, sec[512];
    179   PARTITION      *part;
    180   PTABLE_ENTRY   *pent;
    181   PARTITION        **newparts;
    182 
    183   if(!dev)
    184   {
    185     _dio_error = ERR_BADDEV;
    186     return EXT2_ET_BAD_DEVICE_NAME;
    187   }
    188 
    189   /*
    190    * First check whether the dev name is OK
    191    */
    192   tmp = (unsigned char*)strrchr(dev, '/');
    193   if(!tmp)
    194   {
    195     _dio_error = ERR_BADDEV;
    196     return EXT2_ET_BAD_DEVICE_NAME;
    197   }
    198   *tmp = 0;
    199   if(strcmp(dev, "/dev"))
    200   {
    201     _dio_error = ERR_BADDEV;
    202     return EXT2_ET_BAD_DEVICE_NAME;
    203   }
    204   *tmp++ = '/';
    205 
    206   /*
    207    * Check whether the partition data is already in cache
    208    */
    209 
    210   part = (PARTITION*)malloc(sizeof(PARTITION));
    211   if (!part)
    212 	  return ENOMEM;
    213   {
    214     int   i = 0;
    215 
    216     for(;i < npart; i++)
    217       if(!strcmp(partitions[i]->dev, dev))
    218       {
    219         /* Found it! Make it the active one */
    220         active = partitions[i];
    221         *channel = alloc_io_channel(active);
    222 	if (!*channel)
    223 		return ENOMEM;
    224         return 0;
    225       }
    226   }
    227 
    228   /*
    229    * Drive number & optionally partn number
    230    */
    231   switch(tmp[0])
    232   {
    233     case 'h':
    234     case 's':
    235       part->phys = 0x80;
    236       part->phys += toupper(tmp[2]) - 'A';
    237       /*
    238        * Do we have the partition number?
    239        */
    240       if(tmp[3])
    241         part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0;
    242       else
    243         part->pno = 0xFF;
    244       break;
    245 
    246     case 'f':
    247       if(tmp[2])
    248         part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0;
    249       else
    250         part->phys = 0x00; /* We'll assume /dev/fd0 */
    251       break;
    252 
    253     default:
    254       _dio_error = ERR_BADDEV;
    255       return ENODEV;
    256   }
    257 
    258   if(part->phys < 0x80)
    259   {
    260      /* We don't support floppies for now */
    261      _dio_error = ERR_NOTSUPP;
    262      return EINVAL;
    263   }
    264 
    265   part->dev = strdup(dev);
    266 
    267   /*
    268    * Get drive's geometry
    269    */
    270   _dio_hw_error = biosdisk(DISK_GET_GEOMETRY,
    271                            part->phys,
    272                            0, /* head */
    273                            0, /* cylinder */
    274                            1, /* sector */
    275                            1, /* just one sector */
    276                            sec);
    277 
    278   if(!HW_OK())
    279   {
    280     _dio_error = ERR_HARDWARE;
    281     if (part)
    282 	    free(part);
    283     return EFAULT;
    284   }
    285 
    286   /*
    287    * Calculate the geometry
    288    */
    289   part->cyls  = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1);
    290   part->heads = sec[3] + 1;
    291   part->sects = sec[0] & 0x3F;
    292 
    293   /*
    294    * Now that we know all we need, let's look for the partition
    295    */
    296   _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec);
    297 
    298   if(!HW_OK())
    299   {
    300     _dio_error = ERR_HARDWARE;
    301     if (part)
    302 	    free(part);
    303     return EFAULT;
    304   }
    305 
    306   pent = (PTABLE_ENTRY*)&sec[0x1BE];
    307   pent = scan_partition_table(pent, part->phys, &part->pno);
    308 
    309   if(!pent)
    310   {
    311     _dio_error = part->pno == 0xFE ? ERR_EMPTYPART :
    312                  part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS;
    313     if (part)
    314 	    free(part);
    315     return ENODEV;
    316   }
    317 
    318   /*
    319    * Calculate the remaining figures
    320    */
    321   {
    322     unsigned long    fsec, fhead, fcyl;
    323 
    324     fsec = (unsigned long)(pent->start_sec & 0x3F);
    325     fhead = (unsigned long)pent->start_head;
    326     fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl;
    327     part->start = fsec + fhead * part->sects + fcyl *
    328                   (part->heads * part->sects) - 1;
    329     part->len = pent->size;
    330   }
    331 
    332   /*
    333    * Add the partition to the table
    334    */
    335   newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart);
    336   if (!newparts) {
    337 	  free(part);
    338 	  return ENOMEM;
    339   }
    340   partitions = newparts;
    341   partitions[npart++] = active = part;
    342 
    343   /*
    344    * Now alloc all libe2fs structures
    345    */
    346   *channel = alloc_io_channel(active);
    347   if (!*channel)
    348 	  return ENOMEM;
    349 
    350   return 0;
    351 }
    352 
    353 static errcode_t dos_close(io_channel channel)
    354 {
    355 	if (channel->name)
    356 		free(channel->name);
    357 	if (channel)
    358 		free(channel);
    359 
    360 	return 0;
    361 }
    362 
    363 static errcode_t dos_set_blksize(io_channel channel, int blksize)
    364 {
    365   channel->block_size = blksize;
    366 
    367   return 0;
    368 }
    369 
    370 static errcode_t dos_read_blk(io_channel channel, unsigned long block,
    371                                              int count, void *buf)
    372 {
    373   PARTITION     *part;
    374   size_t        size;
    375   ext2_loff_t   loc;
    376   CHS           chs;
    377 
    378   EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    379   part = (PARTITION*)channel->private_data;
    380 
    381   size = (size_t)((count < 0) ? -count : count * channel->block_size);
    382   loc = (ext2_loff_t) block * channel->block_size;
    383 
    384   lba2chs(loc, &chs, part);
    385   /*
    386    * Potential bug here:
    387    *   If DJGPP is used then reads of >18 sectors will fail!
    388    *   Have to rewrite biosdisk.
    389    */
    390   _dio_hw_error = biosdisk(DISK_READ,
    391                            part->phys,
    392                            chs.head,
    393                            chs.cyl,
    394                            chs.sector,
    395                            size < 512 ? 1 : size/512,
    396                            buf);
    397 
    398   if(!HW_OK())
    399   {
    400     _dio_error = ERR_HARDWARE;
    401     return EFAULT;
    402   }
    403 
    404   return 0;
    405 }
    406 
    407 static errcode_t dos_write_blk(io_channel channel, unsigned long block,
    408                                int count, const void *buf)
    409 {
    410   PARTITION     *part;
    411   size_t        size;
    412   ext2_loff_t   loc;
    413   CHS           chs;
    414 
    415   EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    416   part = (PARTITION*)channel->private_data;
    417 
    418   if(count == 1)
    419     size = (size_t)channel->block_size;
    420   else
    421   {
    422     if (count < 0)
    423       size = (size_t)-count;
    424     else
    425       size = (size_t)(count * channel->block_size);
    426   }
    427 
    428   loc = (ext2_loff_t)block * channel->block_size;
    429   lba2chs(loc, &chs, part);
    430   _dio_hw_error = biosdisk(DISK_WRITE,
    431                            part->phys,
    432                            chs.head,
    433                            chs.cyl,
    434                            chs.sector,
    435                            size < 512 ? 1 : size/512,
    436                            (void*)buf);
    437 
    438   if(!HW_OK())
    439   {
    440     _dio_error = ERR_HARDWARE;
    441     return EFAULT;
    442   }
    443 
    444   return 0;
    445 }
    446 
    447 #ifdef __TURBOC__
    448 #pragma argsused
    449 #endif
    450 static errcode_t dos_flush(io_channel channel)
    451 {
    452   /*
    453    * No buffers, no flush...
    454    */
    455   return 0;
    456 }
    457