2 Copyright © 2003-2011, The AROS Development Team. All rights reserved.
6 /******************************************************************************
14 DEVICE, UNIT/N, SYSSIZE/K/N, SYSTYPE/K, SYSNAME/K, WORKSIZE/K/N,
15 MAXWORK/S, WORKTYPE/K, WORKNAME/K, WIPE/S, FORCE/S, QUIET/S, RDB/S
23 Partition creates either one or two AROS partitions on a given drive.
24 Existing partitions will be kept unless the WIPE option is specified
25 (or a serious bug occurs, for which we take no responsibility).
26 Partitions created by this command must be formatted before they can
29 By default, a single SFS System partition is created using the
30 largest amount of free space possible. A smaller size can be chosen
31 using the SYSSIZE argument. To also create a Work partition, either
32 WORKSIZE or MAXWORK must additionally be specified. The WORKSIZE
33 argument allows the size of the Work partition to be specified,
34 while setting the MAXWORK switch makes the Work partition as large
37 The filesystems used by the System and Work partitions may be
38 specified using the SYSTYPE and WORKTYPE arguments respectively.
39 The available options are "SFS" (Smart Filesystem, the default), and
40 "FFSIntl" (the traditional so-called Fast Filesystem).
42 The DOS device names used for the System and Work partitions may be
43 specified using the SYSNAME and WORKNAME arguments respectively.
44 By default, these are DH0 and DH1.
46 If you wish to use only AROS on the drive you run this command on,
47 you can specify the WIPE option, which destroys all existing
48 partitions on the drive. Be very careful with this option: it
49 deletes all other operating systems and data on the drive, and could
50 be disastrous if the wrong drive is accidentally partitioned.
52 If the drive does not already contain an extended partition, one is
53 created using the largest available region of free space. The AROS
54 partitions are then created as a logical partition within. This is
55 in order to make the addition of further partitions easier.
59 DEVICE -- Device driver name (ata.device by default)
60 UNIT -- The drive's unit number (0 by default, which is the primary
61 master when using ata.device)
62 SYSSIZE -- The System (boot) partition size in megabytes.
63 SYSTYPE -- The file system to use for the system partition, either
64 "SFS" (the default) or "FFSIntl".
65 SYSNAME -- The name to use for the system partition (defaults to DH0).
66 WORKSIZE -- The Work (secondary) partition size in megabytes. To use
67 this option, SYSSIZE must also be specified.
68 MAXWORK -- Make the Work partition as large as possible. To use this
69 option, SYSSIZE must also be specified.
70 WORKTYPE -- The file system to use for the work partition, either
71 "SFS" (the default) or "FFSIntl".
72 WORKNAME -- The name to use for the work partition (defaults to DH1).
73 WIPE -- Destroy all other partitions on the drive, including those
74 used by other operating systems (CAUTION!).
75 FORCE -- Do not ask for confirmation before partitioning the drive.
76 QUIET -- Do not print any output. This option can only be used when
77 FORCE is also specified.
78 RDB -- Create only RDB partitions, no MBR or EBR partitions will be
83 Standard DOS error codes.
87 Using HDToolBox instead of this command may sometimes be safer, as
88 it shows where partitions will be created on the drive before
89 changes are written to disk. However, HDToolBox can be unreliable.
93 Partition ata.device 1 SYSSIZE 200 MAXWORK
99 Sys:System/Format, SFSFormat
101 ******************************************************************************/
103 #include <utility/tagitem.h>
104 #include <libraries/partition.h>
105 #include <devices/trackdisk.h>
106 #include <proto/exec.h>
107 #include <proto/dos.h>
108 #include <proto/partition.h>
109 #include <proto/utility.h>
112 #include <aros/debug.h>
116 #define MB (1024LL * 1024LL)
117 #define MIN_SIZE 50 /* Minimum disk space allowed in MBs */
118 #define MAX_FFS_SIZE (4L * 1024)
119 #define MAX_SFS_SIZE (124L * 1024)
120 #define MAX_SIZE(A) (((A) == &sfs0) ? MAX_SFS_SIZE : MAX_FFS_SIZE)
122 const TEXT version_string
[] = "$VER: Partition 41.4 (16.6.2011)";
124 static const struct PartitionType dos3
= { "DOS\3", 4 };
126 static const struct PartitionType sfs0
= { "SFS\0", 4 };
128 /* atm, SFS is BE on AROS */
129 static const struct PartitionType sfs0
= { "SFS\0", 4 };
133 /*** Prototypes *************************************************************/
134 static VOID
FindLargestGap(struct PartitionHandle
*root
, QUAD
*gapLowBlock
,
136 static VOID
CheckGap(struct PartitionHandle
*root
, QUAD partLimitBlock
,
137 QUAD
*gapLowBlock
, QUAD
*gapHighBlock
);
138 static struct PartitionHandle
*CreateMBRPartition(
139 struct PartitionHandle
*parent
, ULONG lowcyl
, ULONG highcyl
, UBYTE id
);
140 static struct PartitionHandle
*CreateRDBPartition(
141 struct PartitionHandle
*parent
, ULONG lowcyl
, ULONG highcyl
,
142 CONST_STRPTR name
, BOOL bootable
, const struct PartitionType
*type
);
143 static ULONG
MBsToCylinders(ULONG size
, struct DosEnvec
*de
);
145 /*** Functions **************************************************************/
148 struct PartitionHandle
*diskPart
= NULL
, *sysPart
, *workPart
,
149 *root
= NULL
, *partition
, *extPartition
= NULL
, *parent
= NULL
;
152 const struct PartitionType
*sysType
= NULL
, *workType
= NULL
;
153 LONG error
= 0, sysSize
= 0, workSize
= 0, sysHighCyl
, lowCyl
, highCyl
,
154 reqHighCyl
, unit
, hasActive
= FALSE
;
155 QUAD rootLowBlock
= 1, rootHighBlock
= 0, extLowBlock
= 1,
156 extHighBlock
= 0, lowBlock
, highBlock
, rootBlocks
, extBlocks
;
159 /* Read and check arguments */
160 if (!ReadArguments())
165 if (ARG(WORKSIZE
) != (IPTR
)NULL
&& ARG(SYSSIZE
) == (IPTR
)NULL
)
167 PutStr("ERROR: Cannot specify WORKSIZE without SYSSIZE.\n");
168 error
= ERROR_REQUIRED_ARG_MISSING
;
171 if (ARG(WORKSIZE
) != (IPTR
)NULL
&& ARG(MAXWORK
))
173 PutStr("ERROR: Cannot specify both WORKSIZE and MAXWORK.\n");
174 error
= ERROR_TOO_MANY_ARGS
;
177 if (ARG(QUIET
) && !ARG(FORCE
))
179 PutStr("ERROR: Cannot specify QUIET without FORCE.\n");
180 error
= ERROR_REQUIRED_ARG_MISSING
;
183 if (ARG(SYSTYPE
) != (IPTR
)NULL
&&
184 Stricmp((CONST_STRPTR
)ARG(SYSTYPE
), "FFSIntl") != 0 &&
185 Stricmp((CONST_STRPTR
)ARG(SYSTYPE
), "SFS") != 0)
187 PutStr("ERROR: SYSTYPE must be either 'FFSIntl' or 'SFS'.\n");
188 error
= ERROR_REQUIRED_ARG_MISSING
;
191 if (ARG(WORKTYPE
) != (IPTR
)NULL
&&
192 Stricmp((CONST_STRPTR
)ARG(WORKTYPE
), "FFSIntl") != 0 &&
193 Stricmp((CONST_STRPTR
)ARG(WORKTYPE
), "SFS") != 0)
195 PutStr("ERROR: WORKTYPE must be either 'FFSIntl' or 'SFS'.\n");
196 error
= ERROR_REQUIRED_ARG_MISSING
;
202 /* Get DOSType for each partition */
203 if (ARG(SYSTYPE
) == (IPTR
)NULL
||
204 Stricmp((CONST_STRPTR
)ARG(SYSTYPE
), "SFS") != 0)
209 if (ARG(WORKTYPE
) == (IPTR
)NULL
||
210 Stricmp((CONST_STRPTR
)ARG(WORKTYPE
), "FFSIntl") != 0)
215 device
= (CONST_STRPTR
) ARG(DEVICE
);
216 unit
= *(LONG
*)ARG(UNIT
);
217 if (ARG(SYSSIZE
) != (IPTR
)NULL
)
218 sysSize
= *(LONG
*)ARG(SYSSIZE
);
219 if (ARG(WORKSIZE
) != (IPTR
)NULL
)
220 workSize
= *(LONG
*)ARG(WORKSIZE
);
222 D(bug("[C:Partition] Using %s, unit %ld\n", device
, unit
));
226 Printf("About to partition %s unit %d.\n", device
, unit
);
228 Printf("This will DESTROY ALL DATA on the drive!\n");
229 Printf("Are you sure? (y/N)"); Flush(Output());
231 Read(Input(), &choice
, 1);
238 if (choice
!= 'y' && choice
!= 'Y') return RETURN_WARN
;
242 Printf("Partitioning drive...");
246 D(bug("[C:Partition] Partitioning drive...\n"));
251 D(bug("[C:Partition] Open root partition\n"));
252 if ((root
= OpenRootPartition(device
, unit
)) == NULL
)
254 error
= ERROR_UNKNOWN
;
255 PutStr("*** Could not open root partition!\n");
259 /* Destroy any existing partition table if requested */
260 if (error
== 0 && ARG(WIPE
))
262 if (OpenPartitionTable(root
) == 0)
264 D(bug("[C:Partition] WIPE root partition table\n"));
265 error
= DestroyPartitionTable(root
);
271 /* Open the existing partition table, or create it if none exists */
272 D(bug("[C:Partition] Opening existing root partition table..."));
273 if (OpenPartitionTable(root
) != 0)
275 D(bug("failed\n[C:Partition] Creating %s root partition table\n",
276 ARG(RDB
) ? "RDB" : "MBR"));
278 ULONG type
= ARG(RDB
) ? PHPTT_RDB
: PHPTT_MBR
;
279 /* Create a root partition table */
280 if (CreatePartitionTable(root
, type
) != 0)
282 error
= ERROR_UNKNOWN
;
283 PutStr("*** ERROR: Creating root partition table failed.\n");
284 CloseRootPartition(root
);
294 /* Find largest gap in root partition */
295 FindLargestGap(root
, &rootLowBlock
, &rootHighBlock
);
299 /* Look for extended partition and count partitions */
300 ForeachNode(&root
->table
->list
, partition
)
302 if (OpenPartitionTable(partition
) == 0)
304 if (partition
->table
->type
== PHPTT_EBR
)
305 extPartition
= partition
;
307 ClosePartitionTable(partition
);
311 GetPartitionAttrsTags(partition
,
312 PT_ACTIVE
, (IPTR
) &hasActive
, TAG_DONE
);
317 /* Create extended partition if it doesn't exist */
318 if (extPartition
== NULL
)
320 D(bug("[C:Partition] Creating EBR partition\n"));
321 lowCyl
= (rootLowBlock
- 1)
322 / (LONG
)(root
->de
.de_Surfaces
* root
->de
.de_BlocksPerTrack
)
324 highCyl
= (rootHighBlock
+ 1)
325 / (root
->de
.de_Surfaces
* root
->de
.de_BlocksPerTrack
) - 1;
327 CreateMBRPartition(root
, lowCyl
, highCyl
, 0x5);
328 if (extPartition
!= NULL
)
330 if (CreatePartitionTable(extPartition
, PHPTT_EBR
) != 0)
332 PutStr("*** ERROR: Creating extended partition table failed.\n");
345 /* Find largest gap in extended partition */
346 if (extPartition
!= NULL
)
348 FindLargestGap(extPartition
, &extLowBlock
, &extHighBlock
);
351 /* Choose whether to create primary or logical partition */
352 rootBlocks
= rootHighBlock
- rootLowBlock
+ 1;
353 extBlocks
= extHighBlock
- extLowBlock
+ 1;
354 if (extBlocks
> rootBlocks
|| partCount
== 4)
356 parent
= extPartition
;
357 lowBlock
= extLowBlock
;
358 highBlock
= extHighBlock
;
363 lowBlock
= rootLowBlock
;
364 highBlock
= rootHighBlock
;
367 /* Convert block range to cylinders */
368 lowCyl
= ((LONG
)lowBlock
- 1)
369 / (LONG
)(parent
->de
.de_Surfaces
* parent
->de
.de_BlocksPerTrack
) + 1;
370 highCyl
= ((LONG
)highBlock
+ 1)
371 / (LONG
)(parent
->de
.de_Surfaces
* parent
->de
.de_BlocksPerTrack
) - 1;
373 /* Ensure neither partition is too large for its filesystem */
375 highCyl
- lowCyl
+ 1 >
376 MBsToCylinders(MAX_SIZE(sysType
), &parent
->de
)) ||
377 sysSize
> MAX_SIZE(sysType
))
378 sysSize
= MAX_SIZE(sysType
);
380 (highCyl
- lowCyl
+ 1)
381 - MBsToCylinders(sysSize
, &parent
->de
) + 1 >
382 MBsToCylinders(MAX_SIZE(workType
), &parent
->de
)) ||
383 workSize
> MAX_SIZE(workType
))
384 workSize
= MAX_SIZE(workType
);
386 /* Decide geometry for RDB virtual disk */
387 reqHighCyl
= lowCyl
+ MBsToCylinders(sysSize
, &parent
->de
)
388 + MBsToCylinders(workSize
, &parent
->de
);
389 if (sysSize
!= 0 && (workSize
!= 0 || !ARG(MAXWORK
))
390 && reqHighCyl
< highCyl
)
391 highCyl
= reqHighCyl
;
392 if (reqHighCyl
> highCyl
393 || (highCyl
- lowCyl
+ 1 < MBsToCylinders(MIN_SIZE
, &parent
->de
)
395 error
= ERROR_DISK_FULL
;
398 if (error
== 0 && !ARG(RDB
))
400 /* Create RDB virtual disk */
401 D(bug("[C:Partition] Creating RDB virtual disk\n"));
402 diskPart
= CreateMBRPartition(parent
, lowCyl
, highCyl
, 0x30);
403 if (diskPart
== NULL
)
405 PutStr("*** ERROR: Not enough disk space.\n");
406 error
= ERROR_UNKNOWN
;
410 if (error
== 0 && !ARG(RDB
))
412 /* Create RDB partition table inside virtual-disk partition */
413 D(bug("[C:Partition] Creating RDB partition table\n"));
414 error
= CreatePartitionTable(diskPart
, PHPTT_RDB
);
418 "*** ERROR: Creating RDB partition table failed. Aborting.\n");
428 /* Create partitions in the RDB table */
429 sysHighCyl
= sysSize
!= 0
430 ? lowCyl
+ MBsToCylinders(sysSize
, &diskPart
->de
)
433 /* Create System partition (defaults to FFSIntl) */
434 sysPart
= CreateRDBPartition(diskPart
, lowCyl
, sysHighCyl
, (APTR
)ARG(SYSNAME
),
437 error
= ERROR_UNKNOWN
;
441 && sysHighCyl
< diskPart
->de
.de_HighCyl
- diskPart
->de
.de_LowCyl
)
443 /* Create Work partition (defaults to SFS) */
444 workPart
= CreateRDBPartition(diskPart
, sysHighCyl
+ 1, highCyl
,
445 (APTR
)ARG(WORKNAME
), FALSE
, workType
);
446 if (workPart
== NULL
)
447 error
= ERROR_UNKNOWN
;
452 /* Create partitions in the RDB table */
453 sysHighCyl
= MBsToCylinders(sysSize
, &diskPart
->de
);
455 /* Create System partition (defaults to FFSIntl) */
456 sysPart
= CreateRDBPartition(diskPart
, 0, sysHighCyl
, (APTR
)ARG(SYSNAME
),
459 error
= ERROR_UNKNOWN
;
462 && sysHighCyl
< diskPart
->de
.de_HighCyl
- diskPart
->de
.de_LowCyl
)
464 /* Create Work partition (defaults to SFS) */
465 workPart
= CreateRDBPartition(diskPart
, sysHighCyl
+ 1, 0,
466 (APTR
)ARG(WORKNAME
), FALSE
, workType
);
467 if (workPart
== NULL
)
468 error
= ERROR_UNKNOWN
;
475 /* If MBR has no active partition, make extended partition active to
476 prevent broken BIOSes from treating disk as unbootable */
477 if (!hasActive
&& !ARG(RDB
))
478 SetPartitionAttrsTags(extPartition
, PT_ACTIVE
, TRUE
, TAG_DONE
);
480 /* Save to disk and deallocate */
481 WritePartitionTable(diskPart
);
482 ClosePartitionTable(diskPart
);
484 if (parent
== extPartition
)
486 WritePartitionTable(extPartition
);
487 ClosePartitionTable(extPartition
);
490 /* Save to disk and deallocate */
491 WritePartitionTable(root
);
492 ClosePartitionTable(root
);
496 CloseRootPartition(root
);
498 if (!ARG(QUIET
) && error
== 0) Printf("Partitioning completed\n");
500 PrintFault(error
, NULL
);
501 return (error
== 0) ? RETURN_OK
: RETURN_FAIL
;
504 /* Find the low and high cylinder values for the largest gap on the disk */
505 static VOID
FindLargestGap(struct PartitionHandle
*root
, QUAD
*gapLowBlock
,
508 struct PartitionHandle
*partition
;
509 LONG reservedBlocks
= 0;
512 /* Check gap between start of disk and first partition, or until end of
513 disk if there are no partitions */
514 GetPartitionTableAttrsTags(root
, PTT_RESERVED
, (IPTR
) &reservedBlocks
,
516 partLimitBlock
= reservedBlocks
;
517 CheckGap(root
, partLimitBlock
, gapLowBlock
, gapHighBlock
);
519 /* Check gap between each partition and the next one (or end of disk for
520 the last partition)*/
521 ForeachNode(&root
->table
->list
, partition
)
523 partLimitBlock
= (partition
->de
.de_HighCyl
+ 1)
524 * partition
->de
.de_Surfaces
525 * partition
->de
.de_BlocksPerTrack
;
526 CheckGap(root
, partLimitBlock
, gapLowBlock
, gapHighBlock
);
532 /* Check if the gap between the end of the current partition and the start of
533 the next one is bigger than the biggest gap previously found. If so, it is
534 stored as the new biggest gap. */
535 static VOID
CheckGap(struct PartitionHandle
*root
, QUAD partLimitBlock
,
536 QUAD
*gapLowBlock
, QUAD
*gapHighBlock
)
538 struct PartitionHandle
*nextPartition
;
539 QUAD lowBlock
, nextLowBlock
;
541 /* Search partitions for next partition by disk position */
542 nextLowBlock
= (root
->de
.de_HighCyl
- root
->de
.de_LowCyl
+ 1)
543 * root
->de
.de_Surfaces
* root
->de
.de_BlocksPerTrack
; /* End of disk */
544 ForeachNode(&root
->table
->list
, nextPartition
)
546 lowBlock
= nextPartition
->de
.de_LowCyl
547 * nextPartition
->de
.de_Surfaces
548 * nextPartition
->de
.de_BlocksPerTrack
;
549 if (lowBlock
>= partLimitBlock
&& lowBlock
< nextLowBlock
)
551 nextLowBlock
= lowBlock
;
555 /* Check if the gap between the current pair of partitions is the
556 biggest gap so far */
557 if (nextLowBlock
- partLimitBlock
> *gapHighBlock
- *gapLowBlock
+ 1)
559 *gapLowBlock
= partLimitBlock
;
560 *gapHighBlock
= nextLowBlock
- 1;
566 static struct PartitionHandle
*CreateMBRPartition
568 struct PartitionHandle
*parent
, ULONG lowcyl
, ULONG highcyl
, UBYTE id
571 struct DriveGeometry parentDG
= {0};
572 struct DosEnvec parentDE
= {0},
574 struct PartitionType type
= {{id
}, 1};
575 struct PartitionHandle
*partition
;
576 IPTR reserved
, leadIn
= 0;
579 GetPartitionAttrsTags
583 PT_DOSENVEC
, (IPTR
) &parentDE
,
584 PT_GEOMETRY
, (IPTR
) &parentDG
,
589 GetPartitionTableAttrsTags
592 PTT_RESERVED
, (IPTR
) &reserved
,
593 PTT_MAXLEADIN
, (IPTR
) &leadIn
,
599 lowcyl
= (reserved
- 1) /
600 (parentDG
.dg_Heads
* parentDG
.dg_TrackSectors
) + 1;
605 lowcyl
+= (leadIn
- 1) /
606 (parentDG
.dg_Heads
* parentDG
.dg_TrackSectors
) + 1;
609 maxcyl
= parentDE
.de_HighCyl
- parentDE
.de_LowCyl
;
610 if (highcyl
== 0 || highcyl
> maxcyl
)
615 CopyMem(&parentDE
, &partitionDE
, sizeof(struct DosEnvec
));
617 partitionDE
.de_SizeBlock
= parentDG
.dg_SectorSize
>> 2;
618 partitionDE
.de_Reserved
= 2;
619 partitionDE
.de_HighCyl
= highcyl
;
620 partitionDE
.de_LowCyl
= lowcyl
;
622 partition
= AddPartitionTags
625 PT_DOSENVEC
, (IPTR
) &partitionDE
,
626 PT_TYPE
, (IPTR
) &type
,
633 static struct PartitionHandle
*CreateRDBPartition
635 struct PartitionHandle
*parent
, ULONG lowcyl
, ULONG highcyl
,
636 CONST_STRPTR name
, BOOL bootable
, const struct PartitionType
*type
639 struct DriveGeometry parentDG
= {0};
640 struct DosEnvec parentDE
= {0},
642 struct PartitionHandle
*partition
;
645 GetPartitionAttrsTags
649 PT_DOSENVEC
, (IPTR
) &parentDE
,
650 PT_GEOMETRY
, (IPTR
) &parentDG
,
659 GetPartitionTableAttrsTags
661 parent
, PTT_RESERVED
, (IPTR
) &reserved
, TAG_DONE
664 lowcyl
= (reserved
- 1)
665 / (parentDG
.dg_Heads
* parentDG
.dg_TrackSectors
) + 1;
668 maxcyl
= parentDE
.de_HighCyl
- parentDE
.de_LowCyl
;
669 if (highcyl
== 0 || highcyl
> maxcyl
)
674 CopyMem(&parentDE
, &partitionDE
, sizeof(struct DosEnvec
));
676 partitionDE
.de_SizeBlock
= parentDG
.dg_SectorSize
>> 2;
677 partitionDE
.de_Surfaces
= parentDG
.dg_Heads
;
678 partitionDE
.de_BlocksPerTrack
= parentDG
.dg_TrackSectors
;
679 partitionDE
.de_BufMemType
= parentDG
.dg_BufMemType
;
680 partitionDE
.de_TableSize
= DE_DOSTYPE
;
681 partitionDE
.de_Reserved
= 2;
682 partitionDE
.de_HighCyl
= highcyl
;
683 partitionDE
.de_LowCyl
= lowcyl
;
684 partitionDE
.de_NumBuffers
= 100;
685 partitionDE
.de_MaxTransfer
= 0xFFFFFF;
686 partitionDE
.de_Mask
= 0xFFFFFFFE;
688 D(bug("[C:Partition] SizeBlock %ld\n", partitionDE
.de_SizeBlock
));
689 D(bug("[C:Partition] Surfaces %ld\n", partitionDE
.de_Surfaces
));
690 D(bug("[C:Partition] BlocksPerTrack %ld\n", partitionDE
.de_BlocksPerTrack
));
691 D(bug("[C:Partition] BufMemType %ld\n", partitionDE
.de_BufMemType
));
692 D(bug("[C:Partition] TableSize %ld\n", partitionDE
.de_TableSize
));
693 D(bug("[C:Partition] Reserved %ld\n", partitionDE
.de_Reserved
));
694 D(bug("[C:Partition] HighCyl %ld\n", partitionDE
.de_HighCyl
));
695 D(bug("[C:Partition] LowCyl %ld\n", partitionDE
.de_LowCyl
));
697 partition
= AddPartitionTags
701 PT_DOSENVEC
, (IPTR
) &partitionDE
,
702 PT_TYPE
, (IPTR
) type
,
703 PT_NAME
, (IPTR
) name
,
704 PT_BOOTABLE
, bootable
,
713 /* Convert a size in megabytes into a cylinder count. The figure returned
714 is rounded down to avoid breaching maximum filesystem sizes. */
715 static ULONG
MBsToCylinders(ULONG size
, struct DosEnvec
*de
)
723 cyls
= bytes
/ (UQUAD
)(de
->de_SizeBlock
* sizeof(ULONG
))
724 / de
->de_BlocksPerTrack
/ de
->de_Surfaces
;