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