The Pedigree Project  0.1
msdos.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "msdos.h"
21 #include "Partition.h"
22 #include "pedigree/kernel/LockGuard.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/Spinlock.h"
25 #include "pedigree/kernel/machine/Disk.h"
26 #include "pedigree/kernel/utilities/StaticString.h"
27 #include "pedigree/kernel/utilities/String.h"
28 #include "pedigree/kernel/utilities/utility.h"
29 
30 class Device;
31 
32 static Spinlock g_Lock;
33 
34 // Partition number for a DOS extended partition (another partition table)
35 const uint8_t g_ExtendedPartitionNumber = 5;
36 // Partition number for an empty partition.
37 const uint8_t g_EmptyPartitionNumber = 0;
38 
39 static const char *g_pPartitionTypes[256] = {"Empty",
40  "FAT12",
41  "XENIX root",
42  "XENIX usr",
43  "FAT16 <32M",
44  "Extended",
45  "FAT16",
46  "HPFS/NTFS",
47  "AIX",
48  "AIX bootable",
49  "OS/2 Boot Manag",
50  "W95 FAT32",
51  "W95 FAT32 (LBA)",
52  "",
53  "W95 FAT16 (LBA)",
54  "W95 Ext",
55  "OPUS",
56  "Hidden FAT12",
57  "Compaq diagnost",
58  "",
59  "Hidden FAT16 <3",
60  "",
61  "Hidden FAT16",
62  "Hidden HPFS/NTF",
63  "AST SmartSleep",
64  "",
65  "",
66  "Hidden W95 FAT3",
67  "Hidden W95 FAT3",
68  "",
69  "Hidden W95 FAT1",
70  "",
71  "",
72  "",
73  "",
74  "",
75  "NEC DOS",
76  "",
77  "",
78  "",
79  "",
80  "",
81  "",
82  "",
83  "",
84  "",
85  "",
86  "",
87  "",
88  "",
89  "",
90  "",
91  "",
92  "",
93  "",
94  "",
95  "",
96  "Plan 9",
97  "",
98  "",
99  "PartitionMagic",
100  "",
101  "",
102  "",
103  "Venix 80286",
104  "PPC PReP Boot",
105  "SFS",
106  "",
107  "",
108  "",
109  "",
110  "",
111  "",
112  "",
113  "",
114  "",
115  "",
116  "QNX4.x",
117  "QNX4.x 2nd part",
118  "QNX4.x 3rd part",
119  "OnTrack DM",
120  "OnTrack DM6 Aux",
121  "CP/M",
122  "OnTrack DM6 Aux",
123  "OnTrackDM6",
124  "EZ-Drive",
125  "Golden Bow",
126  "",
127  "",
128  "",
129  "",
130  "",
131  "Priam Edisk",
132  "",
133  "",
134  "",
135  "",
136  "SpeedStor",
137  "",
138  "GNU HURD or Sys",
139  "Novell Netware",
140  "Novell Netware",
141  "",
142  "",
143  "",
144  "",
145  "",
146  "",
147  "",
148  "",
149  "",
150  "",
151  "DiskSecure Mult",
152  "",
153  "",
154  "",
155  "",
156  "PC/IX",
157  "",
158  "",
159  "",
160  "",
161  "",
162  "",
163  "",
164  "",
165  "",
166  "",
167  "Old Minix",
168  "Minix / old Lin",
169  "Linux swap / So",
170  "Linux",
171  "OS/2 hidden C:",
172  "Linux extended",
173  "NTFS volume set",
174  "NTFS volume set",
175  "Linux plaintext",
176  "",
177  "",
178  "",
179  "",
180  "",
181  "Linux LVM",
182  "",
183  "",
184  "",
185  "",
186  "Amoeba",
187  "Amoeba BBT",
188  "",
189  "",
190  "",
191  "",
192  "",
193  "",
194  "",
195  "",
196  "",
197  "",
198  "BSD/OS",
199  "IBM Thinkpad hi",
200  "",
201  "",
202  "",
203  "",
204  "FreeBSD",
205  "OpenBSD",
206  "NeXTSTEP",
207  "Darwin UFS",
208  "NetBSD",
209  "",
210  "Darwin boot",
211  "",
212  "",
213  "",
214  "",
215  "",
216  "",
217  "",
218  "",
219  "",
220  "",
221  "",
222  "BSDI fs",
223  "BSDI swap",
224  "",
225  "",
226  "Boot Wizard hid",
227  "",
228  "",
229  "Solaris boot",
230  "Solaris",
231  "",
232  "DRDOS/sec (FAT-",
233  "",
234  "",
235  "DRDOS/sec (FAT-",
236  "",
237  "DRDOS/sec (FAT-",
238  "Syrinx",
239  "",
240  "",
241  "",
242  "",
243  "",
244  "",
245  "",
246  "",
247  "",
248  "",
249  "",
250  "",
251  "",
252  "",
253  "",
254  "",
255  "",
256  "",
257  "Non-FS data",
258  "CP/M / CTOS / .",
259  "",
260  "",
261  "Dell Utility",
262  "BootIt",
263  "",
264  "DOS access",
265  "",
266  "DOS R/O",
267  "SpeedStor",
268  "",
269  "",
270  "",
271  "",
272  "",
273  "",
274  "BeOS fs",
275  "",
276  "",
277  "EFI GPT",
278  "EFI (FAT-12/16/",
279  "Linux/PA-RISC b",
280  "SpeedStor",
281  "DOS secondary",
282  "",
283  "SpeedStor",
284  "",
285  "",
286  "",
287  "",
288  "",
289  "",
290  "",
291  "",
292  "Linux RAID auto",
293  "LANstep",
294  "BBT"};
295 
297 static int gNextPartition = 0;
298 
299 static void
300 msdosRegPartition(MsdosPartitionInfo *pPartitions, int i, Disk *pDisk)
301 {
302 #ifdef THREADS
303  LockGuard<Spinlock> guard(g_Lock);
304 #endif
305 
306  // Look up the partition string.
307  const char *pStr = g_pPartitionTypes[pPartitions[i].type];
308  NormalStaticString sstr("(");
309  sstr += gNextPartition++;
310  sstr += ") ";
311  sstr += pStr;
312  String str(sstr);
313 
314  // Create a partition object.
315  Partition *pObj = new Partition(
316  str,
317  static_cast<uint64_t>(LITTLE_TO_HOST32(pPartitions[i].start_lba)) *
318  512ULL, /* start_lba is in /sectors/. */
319  static_cast<uint64_t>(LITTLE_TO_HOST32(pPartitions[i].size)) * 512ULL);
320  pObj->setParent(static_cast<Device *>(pDisk));
321  pDisk->addChild(static_cast<Device *>(pObj));
322 }
323 
324 static bool msdosReadExtTable(
325  MsdosPartitionInfo *pPartitions, Disk *pDisk, int n, uint64_t partitionBase,
326  uint64_t currentBase)
327 {
328  for (int i = 0; i < MSDOS_EXT_PARTTAB_NUM; i++)
329  {
330  // Legit?
331  if ((pPartitions[i].active != 0) && (pPartitions[i].active != 0x80))
332  {
333  WARNING("Invalid partition record found");
334  continue;
335  }
336 
337  // Check the type of the partition.
338  if (pPartitions[i].type == g_ExtendedPartitionNumber)
339  {
340  // In a linked extended partition record, the LBA start is the
341  // difference between the start of the actual extended partition and
342  // the next extended partition record's MBR sector.
343  uint64_t startLba =
344  LITTLE_TO_HOST32(pPartitions[i].start_lba) + partitionBase;
345 
346  // Update the partition information. Forget about turning it back
347  // into whatever endianness it was in before.
348  pPartitions[i].start_lba =
349  static_cast<uint32_t>(startLba & 0xFFFFFFFF);
350 
351  // Extended partition - read in 512 bytes and recurse.
352  uintptr_t buff;
353  if ((buff = pDisk->read(pPartitions[i].start_lba * 512ULL)) == 0)
354  {
355  WARNING(
356  "Couldn't read next sector for the extended partition.");
357  continue;
358  }
359 
360  uint8_t *buffer = reinterpret_cast<uint8_t *>(buff);
361 
362  // Is it a "valid" MBR?
363  if (buffer[510] != MSDOS_IDENT_1 || buffer[511] != MSDOS_IDENT_2)
364  {
365  WARNING("Extended partition record read failed.");
366  continue;
367  }
368 
369  // Call the extended partition reader. We pass in the current
370  // extended partition record's base, along with the base of the
371  // extended partition record we're about to parse.
372  MsdosPartitionInfo *pNextPartitions =
373  reinterpret_cast<MsdosPartitionInfo *>(
374  &buffer[MSDOS_PARTTAB_START]);
375  if (!msdosReadExtTable(
376  pNextPartitions, pDisk, MSDOS_PARTTAB_NUM, partitionBase,
377  startLba))
378  WARNING("Reading the extended partition table failed");
379  }
380  else if (pPartitions[i].type == g_EmptyPartitionNumber)
381  {
382  // Empty partition - end of chain
383  return true;
384  }
385  else
386  {
387  // The start LBA of a logical partition is relative to the extended
388  // partition record which describes it
389  uint64_t startLba =
390  LITTLE_TO_HOST32(pPartitions[i].start_lba) + currentBase;
391 
392  pPartitions[i].start_lba =
393  HOST_TO_LITTLE32(static_cast<uint32_t>(startLba & 0xFFFFFFFF));
394  msdosRegPartition(pPartitions, i, pDisk);
395  }
396  }
397  return true;
398 }
399 
400 bool msdosReadTable(MsdosPartitionInfo *pPartitions, Disk *pDisk)
401 {
402  for (int i = 0; i < MSDOS_PARTTAB_NUM; i++)
403  {
404  // Legit?
405  if ((pPartitions[i].active != 0) && (pPartitions[i].active != 0x80))
406  {
407  WARNING("Invalid partition record found");
408  continue;
409  }
410 
411  // Check the type of the partition.
412  if (pPartitions[i].type == g_ExtendedPartitionNumber)
413  {
414  uint64_t startLba = LITTLE_TO_HOST32(pPartitions[i].start_lba);
415 
416  // Extended partition - read in 512 bytes and recurse. The first
417  // sector will always be relative to this sector (zero).
418  uintptr_t buff;
419  if ((buff = pDisk->read(startLba * 512ULL)) == 0)
420  {
421  WARNING(
422  "Couldn't read next sector for the extended partition.");
423  continue;
424  }
425 
426  uint8_t *buffer = reinterpret_cast<uint8_t *>(buff);
427 
428  // Is it valid?
429  if (buffer[510] != MSDOS_IDENT_1 || buffer[511] != MSDOS_IDENT_2)
430  {
431  WARNING("Extended partition record read failed.");
432  continue;
433  }
434 
435  // Call the extended partition reader, give it the base of this
436  // partition entry for its calculations.
437  MsdosPartitionInfo *pReadPartitions =
438  reinterpret_cast<MsdosPartitionInfo *>(
439  &buffer[MSDOS_PARTTAB_START]);
440  if (!msdosReadExtTable(
441  pReadPartitions, pDisk, MSDOS_PARTTAB_NUM, startLba,
442  startLba))
443  WARNING("Reading the extended partition table failed");
444  }
445  else if (pPartitions[i].type == g_EmptyPartitionNumber)
446  {
447  // Empty partition - do nothing.
448  }
449  else
450  {
451  msdosRegPartition(pPartitions, i, pDisk);
452  }
453  }
454  return true;
455 }
456 
457 bool msdosProbeDisk(Disk *pDisk)
458 {
459  // Read the first sector (512 bytes) of the disk into a buffer.
460  uintptr_t buff;
461  if ((buff = pDisk->read(0ULL)) == 0)
462  {
463  WARNING("Disk read failure during MS-DOS partition table search.");
464  return false;
465  }
466 
467  uint8_t *buffer = reinterpret_cast<uint8_t *>(buff);
468 
469  String diskName;
470  pDisk->getName(diskName);
471 
472  // Check for the magic bytes.
473  if (buffer[510] != MSDOS_IDENT_1 || buffer[511] != MSDOS_IDENT_2)
474  {
475  NOTICE("MS-DOS partition not found on disk " << diskName);
476  return false;
477  }
478 
479  NOTICE("MS-DOS partition table found on disk " << diskName);
480 
481  MsdosPartitionInfo *pPartitions =
482  reinterpret_cast<MsdosPartitionInfo *>(&buffer[MSDOS_PARTTAB_START]);
483  return msdosReadTable(pPartitions, pDisk);
484 }
bool msdosReadTable(MsdosPartitionInfo *pPartitions, Disk *pDisk)
Definition: msdos.cc:400
Definition: String.h:49
Definition: Disk.h:32
Definition: Device.h:43
#define WARNING(text)
Definition: Log.h:78
virtual uintptr_t read(uint64_t location)
Definition: Disk.cc:56
#define NOTICE(text)
Definition: Log.h:74
void addChild(Device *pDevice)
Definition: Device.cc:127
void setParent(Device *p)
Definition: Device.h:154
virtual void getName(String &str)
Definition: Disk.cc:46
bool msdosProbeDisk(Disk *pDisk)
Definition: msdos.cc:457