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