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