Updated for GRUB2 2.00.
[AROS.git] / workbench / c / Install-grub2-i386-pc.c
blobb88c397eba66517972ba8a8ad8d7fd37101f0d87
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
4 */
5 /******************************************************************************
8 NAME
10 Install-grub2-i386-pc
12 SYNOPSIS
14 DEVICE/A, UNIT/N/K/A, PARTITIONNUMBER=PN/K/N, GRUB/K/A, FORCELBA/S
16 LOCATION
20 FUNCTION
22 Installs the GRUB 2 bootloader to the boot block of the specified
23 disk or partition.
25 INPUTS
27 DEVICE -- Device name (e.g. ata.device)
28 UNIT -- Unit number
29 PN -- Specifies a partition number. If specified, GRUB is installed
30 to this partition's boot block. Otherwise, GRUB is installed to
31 the disk's boot block.
32 GRUB -- Path to GRUB directory.
33 FORCELBA -- Force use of LBA mode.
35 RESULT
37 NOTES
39 EXAMPLE
41 Install-grub2-i386-pc DEVICE ata.device UNIT 0 GRUB DH0:boot/grub
43 BUGS
45 SEE ALSO
47 Partition, SYS:System/Format
49 INTERNALS
51 ******************************************************************************/
53 #define DEBUG 0
54 #include <aros/debug.h>
56 #include <string.h>
57 #include <proto/exec.h>
58 #include <proto/dos.h>
59 #include <proto/partition.h>
60 #include <proto/utility.h>
61 #include <aros/macros.h>
62 #include <devices/hardblocks.h>
63 #include <devices/newstyle.h>
64 #include <exec/errors.h>
65 #include <exec/memory.h>
66 #include <libraries/partition.h>
70 /* Defines for grub2 data */
71 /* boot.img pointers */
72 #define GRUB_BOOT_MACHINE_BPB_START 0x03
73 #define GRUB_BOOT_MACHINE_BPB_END 0x5a
74 #define GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC 0x01b8 /* Following grub2 grub-setup sources */
75 #define GRUB_BOOT_MACHINE_PART_START 0x01be
76 #define GRUB_BOOT_MACHINE_PART_END 0x01fe
77 #define GRUB_BOOT_MACHINE_KERNEL_SECTOR 0x5c
78 #define GRUB_BOOT_MACHINE_BOOT_DRIVE 0x64
79 #define GRUB_BOOT_MACHINE_DRIVE_CHECK 0x66
81 /* core.img pointers */
82 #define GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE 0x18
84 /* BIOS drive flag */
85 #define BIOS_HDISK_FLAG 0x80
87 #define MBR_MAX_PARTITIONS 4
88 #define MBRT_EXTENDED 0x05
89 #define MBRT_EXTENDED2 0x0f
90 #define BLCKLIST_ELEMENTS 14
92 struct Volume
94 struct MsgPort *mp;
95 struct IOExtTD *iotd;
96 ULONG readcmd;
97 ULONG writecmd;
98 ULONG startblock;
99 ULONG countblock;
100 CONST_STRPTR device;
101 ULONG unitnum;
102 UWORD SizeBlock;
103 UBYTE flags;
104 BYTE partnum;
105 ULONG *blockbuffer;
106 ULONG dos_id;
109 #define VF_IS_TRACKDISK (1<<0)
110 #define VF_IS_RDB (1<<1)
112 struct BlockNode
114 ULONG sector_lo;
115 ULONG sector_hi;
116 UWORD count;
117 UWORD seg_adr;
120 const TEXT version[] = "$VER: Install-grub2-i386-pc 41.4 (15.9.2012)";
122 CONST_STRPTR CORE_IMG_FILE_NAME = "i386-pc/core.img";
124 STRPTR template =
125 (STRPTR) ("DEVICE/A," "UNIT/N/K/A," "PARTITIONNUMBER=PN/K/N," "GRUB/K/A,"
126 "FORCELBA/S");
128 IPTR myargs[7] = { 0, 0, 0, 0, 0, 0 };
130 struct FileSysStartupMsg *getDiskFSSM(CONST_STRPTR path)
132 struct DosList *dl;
133 struct DeviceNode *dn;
134 TEXT dname[32];
135 UBYTE i;
137 D(bug("[install] getDiskFSSM('%s')\n", path));
139 for (i = 0; (path[i]) && (path[i] != ':'); i++)
140 dname[i] = path[i];
141 if (path[i] == ':')
143 dname[i] = 0;
144 dl = LockDosList(LDF_READ);
145 if (dl)
147 dn = (struct DeviceNode *) FindDosEntry(dl, dname, LDF_DEVICES);
148 UnLockDosList(LDF_READ);
149 if (dn)
151 dname[i] = ':';
152 dname[i + 1] = '\0';
153 if (IsFileSystem(dname))
155 return (struct FileSysStartupMsg *) BADDR(dn->dn_Startup);
157 else
158 Printf("device '%s' doesn't contain a file system\n",
159 dname);
161 else
162 PrintFault(ERROR_OBJECT_NOT_FOUND, dname);
165 else
166 Printf("'%s' doesn't contain a device name\n", path);
167 return 0;
170 void fillGeometry(struct Volume *volume, struct DosEnvec *de)
172 ULONG spc;
174 D(bug("[install] fillGeometry(%x)\n", volume));
176 spc = de->de_Surfaces * de->de_BlocksPerTrack;
177 volume->SizeBlock = de->de_SizeBlock;
178 volume->startblock = de->de_LowCyl * spc;
179 volume->countblock =
180 ((de->de_HighCyl - de->de_LowCyl + 1) * spc) - 1 + de->de_Reserved;
183 void nsdCheck(struct Volume *volume)
185 struct NSDeviceQueryResult nsdq;
186 UWORD *cmdcheck;
188 D(bug("[install] nsdCheck(%x)\n", volume));
190 if (((volume->startblock + volume->countblock) * /* last block */
191 ((volume->SizeBlock << 2) / 512) /* 1 portion (block) equals 512 (bytes) */
192 ) > 8388608)
194 nsdq.SizeAvailable = 0;
195 nsdq.DevQueryFormat = 0;
196 volume->iotd->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
197 volume->iotd->iotd_Req.io_Data = &nsdq;
198 volume->iotd->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
199 if (DoIO((struct IORequest *) &volume->iotd->iotd_Req) == IOERR_NOCMD)
201 Printf("Device doesn't understand NSD-Query\n");
203 else
205 if ((volume->iotd->iotd_Req.io_Actual >
206 sizeof(struct NSDeviceQueryResult))
207 || (volume->iotd->iotd_Req.io_Actual == 0)
208 || (volume->iotd->iotd_Req.io_Actual != nsdq.SizeAvailable))
210 Printf("WARNING wrong io_Actual using NSD\n");
212 else
214 if (nsdq.DeviceType != NSDEVTYPE_TRACKDISK)
215 Printf("WARNING no trackdisk type\n");
216 for (cmdcheck = nsdq.SupportedCommands; *cmdcheck; cmdcheck++)
218 if (*cmdcheck == NSCMD_TD_READ64)
219 volume->readcmd = NSCMD_TD_READ64;
220 if (*cmdcheck == NSCMD_TD_WRITE64);
221 volume->writecmd = NSCMD_TD_WRITE64;
223 if ((volume->readcmd != NSCMD_TD_READ64) ||
224 (volume->writecmd != NSCMD_TD_WRITE64))
225 Printf("WARNING no READ64/WRITE64\n");
231 struct Volume *initVolume(CONST_STRPTR device, ULONG unit, ULONG flags,
232 struct DosEnvec *de)
234 struct Volume *volume;
235 LONG error = 0;
237 D(bug("[install] initVolume(%s:%d)\n", device, unit));
239 volume = AllocVec(sizeof(struct Volume), MEMF_PUBLIC | MEMF_CLEAR);
240 if (volume)
242 volume->mp = CreateMsgPort();
243 if (volume->mp)
245 volume->iotd =
246 (struct IOExtTD *) CreateIORequest(volume->mp,
247 sizeof(struct IOExtTD));
248 if (volume->iotd)
250 volume->blockbuffer =
251 AllocVec(de->de_SizeBlock << 2, MEMF_PUBLIC | MEMF_CLEAR);
252 if (volume->blockbuffer)
254 if (OpenDevice
255 (device,
256 unit, (struct IORequest *) volume->iotd, flags) == 0)
258 if (strcmp((const char *) device, TD_NAME) == 0)
259 volume->flags |= VF_IS_TRACKDISK;
260 else
261 volume->flags |= VF_IS_RDB; /* just assume we have RDB */
262 volume->readcmd = CMD_READ;
263 volume->writecmd = CMD_WRITE;
264 volume->device = device;
265 volume->unitnum = unit;
266 volume->dos_id = 0;
267 fillGeometry(volume, de);
268 nsdCheck(volume);
269 return volume;
271 else
272 error = ERROR_NO_FREE_STORE;
273 FreeVec(volume->blockbuffer);
275 else
276 error = ERROR_NO_FREE_STORE;
277 DeleteIORequest((struct IORequest *) volume->iotd);
279 else
280 error = ERROR_NO_FREE_STORE;
281 DeleteMsgPort(volume->mp);
283 else
284 error = ERROR_NO_FREE_STORE;
285 FreeVec(volume);
287 else
288 error = ERROR_NO_FREE_STORE;
290 PrintFault(error, NULL);
291 return NULL;
294 void uninitVolume(struct Volume *volume)
296 D(bug("[install] uninitVolume(%x)\n", volume));
298 CloseDevice((struct IORequest *) volume->iotd);
299 FreeVec(volume->blockbuffer);
300 DeleteIORequest((struct IORequest *) volume->iotd);
301 DeleteMsgPort(volume->mp);
302 FreeVec(volume);
305 static ULONG _readwriteBlock(struct Volume *volume,
306 ULONG block, APTR buffer, ULONG length,
307 ULONG command)
309 UQUAD offset;
310 ULONG retval = 0;
312 volume->iotd->iotd_Req.io_Command = command;
313 volume->iotd->iotd_Req.io_Length = length;
314 volume->iotd->iotd_Req.io_Data = buffer;
315 offset = (UQUAD) (volume->startblock + block) * (volume->SizeBlock << 2);
316 volume->iotd->iotd_Req.io_Offset = offset & 0xFFFFFFFF;
317 volume->iotd->iotd_Req.io_Actual = offset >> 32;
318 retval = DoIO((struct IORequest *) &volume->iotd->iotd_Req);
319 if (volume->flags & VF_IS_TRACKDISK)
321 volume->iotd->iotd_Req.io_Command = TD_MOTOR;
322 volume->iotd->iotd_Req.io_Length = 0;
323 DoIO((struct IORequest *) &volume->iotd->iotd_Req);
325 return retval;
328 ULONG readBlock(struct Volume * volume, ULONG block, APTR buffer, ULONG size)
330 D(bug("[install] readBlock(vol:%x, block:%d, %d bytes)\n",
331 volume, block, size));
333 return _readwriteBlock(volume, block, buffer, size, volume->readcmd);
336 ULONG writeBlock(struct Volume * volume, ULONG block, APTR buffer, ULONG size)
338 D(bug("[install] writeBlock(vol:%x, block:%d, %d bytes)\n",
339 volume, block, size));
341 return _readwriteBlock(volume, block, buffer, size, volume->writecmd);
344 static BOOL isKnownFs(ULONG dos_id)
346 switch (dos_id)
348 case ID_FFS_DISK:
349 case ID_INTER_DOS_DISK:
350 case ID_INTER_FFS_DISK:
351 case ID_FASTDIR_DOS_DISK:
352 case ID_FASTDIR_FFS_DISK:
353 case ID_SFS_BE_DISK:
354 case ID_SFS_LE_DISK:
355 return TRUE;
358 return FALSE;
361 BOOL isvalidFileSystem(struct Volume * volume, CONST_STRPTR device,
362 ULONG unit)
364 BOOL retval = FALSE;
365 struct PartitionBase *PartitionBase;
366 struct PartitionHandle *ph;
367 ULONG dos_id;
369 D(bug("[install] isvalidFileSystem(%x, %s, %d)\n", volume, device, unit));
371 if (readBlock(volume, 0, volume->blockbuffer, 512))
373 Printf("Read Error\n");
374 return FALSE;
377 dos_id = AROS_BE2LONG(volume->blockbuffer[0]);
379 if (!isKnownFs(dos_id))
381 /* first block has no DOS\x so we don't have RDB for sure */
382 volume->flags &= ~VF_IS_RDB;
383 if (readBlock(volume, 1, volume->blockbuffer, 512))
385 Printf("Read Error\n");
386 return FALSE;
389 dos_id = AROS_BE2LONG(volume->blockbuffer[0]);
391 if (!isKnownFs(dos_id))
392 return FALSE;
393 else
394 volume->dos_id = dos_id;
396 else
397 volume->dos_id = dos_id;
399 volume->partnum = -1;
401 PartitionBase =
402 (struct PartitionBase *) OpenLibrary((CONST_STRPTR)
403 "partition.library", 1);
404 if (PartitionBase)
406 ph = OpenRootPartition(device, unit);
407 if (ph)
409 if (OpenPartitionTable(ph) == 0)
411 struct TagItem tags[3];
412 IPTR type;
414 tags[1].ti_Tag = TAG_DONE;
415 tags[0].ti_Tag = PTT_TYPE;
416 tags[0].ti_Data = (STACKIPTR) & type;
417 GetPartitionTableAttrs(ph, tags);
418 if (type == PHPTT_MBR)
420 struct PartitionHandle *pn;
421 struct DosEnvec de;
422 struct PartitionHandle *extph = NULL;
423 struct PartitionType ptype = { };
425 tags[0].ti_Tag = PT_DOSENVEC;
426 tags[0].ti_Data = (STACKIPTR) & de;
427 tags[1].ti_Tag = PT_TYPE;
428 tags[1].ti_Data = (STACKIPTR) & ptype;
429 tags[2].ti_Tag = TAG_DONE;
430 pn = (struct PartitionHandle *) ph->table->list.lh_Head;
431 while (pn->ln.ln_Succ)
433 ULONG scp;
435 GetPartitionAttrs(pn, tags);
436 if (ptype.id[0] == MBRT_EXTENDED
437 || ptype.id[0] == MBRT_EXTENDED2)
438 extph = pn;
439 else
441 scp = de.de_Surfaces * de.de_BlocksPerTrack;
442 if ((volume->startblock >= (de.de_LowCyl * scp))
443 && (volume->startblock <=
444 (((de.de_HighCyl + 1) * scp) - 1)))
445 break;
447 pn = (struct PartitionHandle *) pn->ln.ln_Succ;
449 if (pn->ln.ln_Succ)
451 tags[0].ti_Tag = PT_POSITION;
452 tags[0].ti_Data = (STACKIPTR) & type;
453 tags[1].ti_Tag = TAG_DONE;
454 GetPartitionAttrs(pn, tags);
455 volume->partnum = (UBYTE) type;
456 retval = TRUE;
457 D(bug
458 ("[install] Primary partition found: partnum=%d\n",
459 volume->partnum));
461 else if (extph != NULL)
463 if (OpenPartitionTable(extph) == 0)
465 tags[0].ti_Tag = PTT_TYPE;
466 tags[0].ti_Data = (STACKIPTR) & type;
467 tags[1].ti_Tag = TAG_DONE;
468 GetPartitionTableAttrs(extph, tags);
469 if (type == PHPTT_EBR)
471 tags[0].ti_Tag = PT_DOSENVEC;
472 tags[0].ti_Data = (STACKIPTR) & de;
473 tags[1].ti_Tag = TAG_DONE;
474 pn = (struct PartitionHandle *) extph->table->
475 list.lh_Head;
476 while (pn->ln.ln_Succ)
478 ULONG offset, scp;
480 offset = extph->de.de_LowCyl
481 * extph->de.de_Surfaces
482 * extph->de.de_BlocksPerTrack;
483 GetPartitionAttrs(pn, tags);
484 scp =
485 de.de_Surfaces * de.de_BlocksPerTrack;
486 if ((volume->startblock >=
487 offset + (de.de_LowCyl * scp))
488 && (volume->startblock <=
489 offset +
490 (((de.de_HighCyl + 1) * scp) -
491 1)))
492 break;
493 pn = (struct PartitionHandle *) pn->ln.
494 ln_Succ;
496 if (pn->ln.ln_Succ)
498 tags[0].ti_Tag = PT_POSITION;
499 tags[0].ti_Data = (STACKIPTR) & type;
500 GetPartitionAttrs(pn, tags);
501 volume->partnum =
502 MBR_MAX_PARTITIONS + (UBYTE) type;
503 retval = TRUE;
504 D(bug
505 ("[install] Logical partition found: partnum=%d\n",
506 (int) volume->partnum));
509 ClosePartitionTable(extph);
513 else
515 if (type == PHPTT_RDB)
517 /* just use whole hard disk */
518 retval = TRUE;
520 else
521 Printf
522 ("only MBR and RDB partition tables are supported\n");
524 ClosePartitionTable(ph);
526 else
528 /* just use whole hard disk */
529 retval = TRUE;
531 CloseRootPartition(ph);
533 else
534 Printf("Error OpenRootPartition(%s,%lu)\n", device, (long)unit);
535 CloseLibrary((struct Library *) PartitionBase);
537 else
538 Printf("Couldn't open partition.library\n");
539 return retval;
542 struct Volume *getGrubStageVolume(CONST_STRPTR device, ULONG unit,
543 ULONG flags, struct DosEnvec *de)
545 struct Volume *volume;
547 volume = initVolume(device, unit, flags, de);
549 D(bug("[install] getGrubStageVolume(): volume=%x\n", volume));
551 if (volume)
553 if (isvalidFileSystem(volume, device, unit))
554 return volume;
555 else
557 Printf("stage2 is on an unsupported file system\n");
558 PrintFault(ERROR_OBJECT_WRONG_TYPE, NULL);
560 uninitVolume(volume);
562 return 0;
565 BOOL isvalidPartition(CONST_STRPTR device, ULONG unit, LONG * pnum,
566 struct DosEnvec * de)
568 struct PartitionBase *PartitionBase;
569 struct PartitionHandle *ph;
570 ULONG type;
571 BOOL retval = FALSE;
573 D(bug
574 ("[install] isvalidPartition(%s:%d, part:%d)\n", device, unit, pnum));
576 PartitionBase =
577 (struct PartitionBase *) OpenLibrary((CONST_STRPTR)
578 "partition.library", 1);
579 if (PartitionBase)
581 ph = OpenRootPartition(device, unit);
582 if (ph)
584 struct TagItem tags[2];
586 tags[1].ti_Tag = TAG_DONE;
587 /* is there a partition table? */
588 if (OpenPartitionTable(ph) == 0)
590 if (pnum)
592 /* install into partition bootblock */
593 tags[0].ti_Tag = PTT_TYPE;
594 tags[0].ti_Data = (STACKIPTR) & type;
595 GetPartitionTableAttrs(ph, tags);
596 if (type == PHPTT_MBR)
598 struct PartitionHandle *pn;
600 /* search for partition */
601 tags[0].ti_Tag = PT_POSITION;
602 tags[0].ti_Data = (STACKIPTR) & type;
603 pn = (struct PartitionHandle *) ph->table->list.
604 lh_Head;
605 while (pn->ln.ln_Succ)
607 GetPartitionAttrs(pn, tags);
608 if (type == *pnum)
609 break;
610 pn = (struct PartitionHandle *) pn->ln.ln_Succ;
612 if (pn->ln.ln_Succ)
614 struct PartitionType ptype;
616 /* is it an AROS partition? */
617 tags[0].ti_Tag = PT_TYPE;
618 tags[0].ti_Data = (STACKIPTR) & ptype;
619 GetPartitionAttrs(pn, tags);
620 if (ptype.id[0] == 0x30)
622 tags[0].ti_Tag = PT_DOSENVEC;
623 tags[0].ti_Data = (STACKIPTR) de;
624 GetPartitionAttrs(pn, tags);
625 retval = TRUE;
627 else
628 Printf
629 ("partition is not of type AROS (0x30)\n");
631 else
633 Printf
634 ("partition %ld not found on device %s unit %lu\n",
635 (long)*pnum, device, (long)unit);
638 else
639 Printf
640 ("you can only install in partitions which are MBR partitioned\n");
642 else
644 /* install into MBR */
645 tags[0].ti_Tag = PTT_TYPE;
646 tags[0].ti_Data = (STACKIPTR) & type;
647 GetPartitionTableAttrs(ph, tags);
648 if ((type == PHPTT_MBR) || (type == PHPTT_RDB))
650 tags[0].ti_Tag = PT_DOSENVEC;
651 tags[0].ti_Data = (STACKIPTR) de;
652 GetPartitionAttrs(ph, tags);
653 retval = TRUE;
655 else
656 Printf
657 ("partition table type must be either MBR or RDB\n");
659 ClosePartitionTable(ph);
661 else
663 /* FIXME: GetPartitionAttr() should always work for root partition */
664 CopyMem(&ph->de, de, sizeof(struct DosEnvec));
665 retval = TRUE;
667 CloseRootPartition(ph);
669 else
670 Printf("Error OpenRootPartition(%s,%lu)\n", device, (long)unit);
671 CloseLibrary((struct Library *) PartitionBase);
673 else
674 Printf("Couldn't open partition.library\n");
675 return retval;
678 struct Volume *getBBVolume(CONST_STRPTR device, ULONG unit, LONG * partnum)
680 struct Volume *volume;
681 struct DosEnvec de;
683 D(bug("[install] getBBVolume(%s:%d, %d)\n", device, unit, partnum));
685 if (isvalidPartition(device, unit, partnum, &de))
687 volume = initVolume(device, unit, 0, &de);
688 volume->partnum = partnum ? *partnum : -1;
689 readBlock(volume, 0, volume->blockbuffer, 512);
690 if (AROS_BE2LONG(volume->blockbuffer[0]) != IDNAME_RIGIDDISK)
692 /* Clear the boot sector region! */
693 memset(volume->blockbuffer, 0x00, 446);
694 return volume;
696 else
697 Printf("no space for bootblock (RDB is on block 0)\n");
699 return NULL;
702 /* Convert a unit number into a drive number as understood by GRUB */
703 UWORD getDriveNumber(CONST_STRPTR device, ULONG unit)
705 struct PartitionHandle *ph;
706 ULONG i;
707 UWORD hd_count = 0;
709 for (i = 0; i < unit; i++)
711 ph = OpenRootPartition(device, i);
712 if (ph != NULL)
714 hd_count++;
715 CloseRootPartition(ph);
719 return hd_count;
722 BOOL writeBootIMG(STRPTR bootimgpath, struct Volume * bootimgvol, struct Volume * coreimgvol,
723 ULONG block /* first block of core.img file */, ULONG unit)
725 BOOL retval = FALSE;
726 LONG error = 0;
727 BPTR fh;
729 D(bug("[install] writeBootIMG(%x)\n", bootimgvol));
731 fh = Open(bootimgpath, MODE_OLDFILE);
732 if (fh)
734 if (Read(fh, bootimgvol->blockbuffer, 512) == 512)
736 /* install into MBR ? */
737 if ((bootimgvol->startblock == 0)
738 && (!(bootimgvol->flags & VF_IS_TRACKDISK)))
740 APTR boot_img = bootimgvol->blockbuffer;
742 UBYTE *boot_drive =
743 (UBYTE *) (boot_img + GRUB_BOOT_MACHINE_BOOT_DRIVE);
744 UWORD *boot_drive_check =
745 (UWORD *) (boot_img + GRUB_BOOT_MACHINE_DRIVE_CHECK);
747 if (unit == bootimgvol->unitnum)
748 *boot_drive = 0xFF;
749 else
750 *boot_drive = getDriveNumber(coreimgvol->device, unit)
751 | BIOS_HDISK_FLAG;
752 *boot_drive_check = 0x9090;
754 D(bug("[install] writeBootIMG: Install to HARDDISK\n"));
756 /* read old MBR */
757 error = readBlock(bootimgvol, 0, coreimgvol->blockbuffer, 512);
759 D(bug("[install] writeBootIMG: MBR Buffer @ %x\n", bootimgvol->blockbuffer));
760 D(bug("[install] writeBootIMG: Copying MBR BPB to %x\n",
761 (char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START));
762 /* copy BPB (BIOS Parameter Block) */
763 CopyMem
764 ((APTR) ((char *) coreimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START),
765 (APTR) ((char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START),
766 (GRUB_BOOT_MACHINE_BPB_END - GRUB_BOOT_MACHINE_BPB_START));
768 /* copy partition table - [Overwrites Floppy boot code] */
769 D(bug("[install] writeBootIMG: Copying MBR Partitions to %x\n",
770 (char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC));
771 CopyMem((APTR) ((char *) coreimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC),
772 (APTR) ((char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC),
773 (GRUB_BOOT_MACHINE_PART_END - GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC));
775 /* Store the core.img pointer .. */
776 ULONG * coreimg_sector_start = (ULONG *) (boot_img
777 + GRUB_BOOT_MACHINE_KERNEL_SECTOR);
778 coreimg_sector_start[0] = block;
779 D(bug("[install] writeBootIMG: core.img pointer = %ld\n", block));
781 else
783 D(bug("[install] writeBootIMG: Install to FLOPPY\n"));
786 if (error == 0)
788 error = writeBlock(bootimgvol, 0, bootimgvol->blockbuffer, 512);
790 if (error)
791 Printf("WriteError %lu\n", (long)error);
792 else
793 retval = TRUE;
795 else
796 Printf("WriteError %lu\n", (long)error);
798 else
799 Printf("%s: Read Error\n", bootimgpath);
800 Close(fh);
802 else
803 PrintFault(IoErr(), bootimgpath);
805 return retval;
808 /* Collects the list of blocks that a file occupies on FFS filesystem */
809 ULONG collectBlockListFFS(struct Volume *volume, ULONG block, struct BlockNode *blocklist)
811 ULONG retval, first_block;
812 WORD blk_count,count;
813 UWORD i;
815 D(bug("[install] collectBlockListFFS(%x, %ld, %x)\n", volume, block, blocklist));
818 /* Clear the core.img sector pointers region! */
819 memset((UBYTE*)&blocklist[-BLCKLIST_ELEMENTS],0x00, BLCKLIST_ELEMENTS*sizeof(struct BlockNode));
822 The number of first block of core.img will be stored in boot.img
823 so skip the first filekey in the first loop
826 retval = _readwriteBlock(volume, block, volume->blockbuffer, volume->SizeBlock<<2,
827 volume->readcmd);
829 if (retval)
831 D(bug("[install] collectBlockListFFS: ERROR reading block (error: %ld\n", retval));
832 Printf("ReadError %lu\n", (long)retval);
833 return 0;
836 i = volume->SizeBlock - 52;
837 first_block = AROS_BE2LONG(volume->blockbuffer[volume->SizeBlock-51]);
838 blk_count=0;
840 D(bug("[install] collectBlockListFFS: First block @ %ld, i:%d\n", first_block, i));
845 retval = _readwriteBlock(volume, block, volume->blockbuffer, volume->SizeBlock<<2,
846 volume->readcmd);
847 if (retval)
849 D(bug("[install] collectBlockListFFS: ERROR reading block (error: %ld)\n", retval));
850 Printf("ReadError %lu\n", (long)retval);
851 return 0;
854 D(bug("[install] collectBlockListFFS: read block %ld, i = %d\n", block, i));
855 while ((i>=6) && (volume->blockbuffer[i]))
857 D(bug("[install] collectBlockListFFS: i = %d\n", i));
859 if current sector follows right after last sector
860 then we don't need a new element
862 if ((blocklist[blk_count].sector_lo) &&
863 ((blocklist[blk_count].sector_lo+blocklist[blk_count].count)==
864 AROS_BE2LONG(volume->blockbuffer[i])))
866 blocklist[blk_count].count += 1;
867 D(bug("[install] collectBlockListFFS: sector %d follows previous - increasing count of block %d to %d\n",
868 i, blk_count, blocklist[blk_count].count));
870 else
872 blk_count--; /* decrement first */
873 D(bug("[install] collectBlockListFFS: store new block (%d)\n", blk_count));
875 if ((blk_count-1) <= -BLCKLIST_ELEMENTS)
877 D(bug("[install] collectBlockListFFS: ERROR: out of block space at sector %d, block %d\n",
878 i, blk_count));
879 Printf("There is no more space to save blocklist in core.img\n");
880 return 0;
882 D(bug("[install] collectBlockListFFS: storing sector pointer for %d in block %d\n",
883 i, blk_count));
884 blocklist[blk_count].sector_lo = AROS_BE2LONG(volume->blockbuffer[i]);
885 blocklist[blk_count].sector_hi = 0;
886 blocklist[blk_count].count = 1;
888 i--;
890 i = volume->SizeBlock - 51;
891 block = AROS_BE2LONG(volume->blockbuffer[volume->SizeBlock - 2]);
892 D(bug("[install] collectBlockListFFS: next block %d, i = %d\n", block, i));
893 } while (block);
897 blocks in blocklist are relative to the first
898 sector of the HD (not partition)
901 D(bug("[install] collectBlockListFFS: successfully updated pointers for %d blocks\n", blk_count));
903 i = 0;
904 for (count=-1;count>=blk_count;count--)
906 blocklist[count].sector_lo += volume->startblock;
907 blocklist[count].seg_adr = 0x820 + (i*32);
908 i += blocklist[count].count;
909 D(bug("[install] collectBlockListFFS: correcting block %d for partition start\n", count));
910 D(bug("[install] collectBlockListFFS: sector : %ld seg_adr : %x\n",
911 blocklist[count].sector_lo, blocklist[count].seg_adr));
914 first_block += volume->startblock;
915 D(bug("[install] collectBlockListFFS: corrected first block for partition start: %ld\n", first_block));
917 return first_block;
920 /* Collects the list of blocks that a file occupies on SFS filesystem */
921 ULONG collectBlockListSFS(struct Volume *volume, ULONG objectnode, struct BlockNode *blocklist)
923 ULONG retval, first_block = 0;
924 WORD blk_count = 0, count = 0;
925 ULONG block_objectnoderoot = 0, block_sfsobjectcontainer = 0, block_extentbnoderoot = 0;
926 ULONG nextblock = 0, searchedblock = 0;
927 WORD i = 0;
928 UBYTE * tmpBytePtr = NULL;
930 D(bug("[install] collectBlockListSFS(startblock: %ld, objectnode: %ld)\n", volume->startblock, objectnode));
931 D(bug("[install] collectBlockListSFS(%ld, %d, %d)\n", volume->countblock, volume->SizeBlock, volume->partnum));
933 /* Clear the core.img sector pointers region! */
934 memset((UBYTE*)&blocklist[-BLCKLIST_ELEMENTS],0x00, BLCKLIST_ELEMENTS*sizeof(struct BlockNode));
936 /* Description of actions:
937 * 1. Load SFS root block
938 * 2. From root block find the block containing root of objectnodes
939 * 3. Traverse the tree of objectnodes until block of objectdescriptor is found
940 * 4. Search the objectdescriptor for entry matching given objectnode from entry read the
941 * first block of file
942 * 5. Having first file block, find the extentbnode for that block and read number
943 * of blocks. Put first block and number of blocks into BlockList.
944 * 6. If the file has more blocks than this exntentbnode hold, find first file
945 * block in next extentbnode. Go to step 5.
946 * Use the SFS source codes for reference. They operate on structures not pointers
947 * and are much easier to understand.
950 /* Read root block */
951 retval = _readwriteBlock(volume, 0, volume->blockbuffer, volume->SizeBlock<<2,
952 volume->readcmd);
954 if (retval)
956 D(bug("[install] collectBlockListSFS: ERROR reading root block (error: %ld)\n", retval));
957 Printf("ReadError %lu\n", (long)retval);
958 return 0;
961 /* Get block pointers from root block */
962 block_objectnoderoot = AROS_BE2LONG(volume->blockbuffer[28]); /* objectnoderoot - 29th ULONG */
963 block_extentbnoderoot = AROS_BE2LONG(volume->blockbuffer[27]); /* extentbnoderoot - 28th ULONG */
965 D(bug("[install] collectBlockListSFS: objectnoderoot: %ld, extentbnoderoot %ld\n",
966 block_objectnoderoot, block_extentbnoderoot));
970 /* Find the SFSObjectContainer block for given objectnode */
971 /* Reference: SFS, nodes.c, function findnode */
972 nextblock = block_objectnoderoot;
973 D(bug("[install] collectBlockListSFS: searching in nextblock %d for sfsobjectcontainer for objectnode %ld\n",
974 nextblock, objectnode));
975 while(1)
977 _readwriteBlock(volume, nextblock, volume->blockbuffer, volume->SizeBlock<<2,
978 volume->readcmd);
980 /* If nodes == 1, we are at the correct nodecontainer, else go to next nodecontainer */
981 if (AROS_BE2LONG(volume->blockbuffer[4]) == 1)
983 /* read entry from position: be_node + sizeof(fsObjectNode) * (objectnode - be_nodenumber) */
984 tmpBytePtr = (UBYTE*)volume->blockbuffer;
985 ULONG index = 20 + 10 * (objectnode - AROS_BE2LONG(volume->blockbuffer[3]));
986 block_sfsobjectcontainer = AROS_BE2LONG(((ULONG*)(tmpBytePtr + index))[0]);
987 D(bug("[install] collectBlockListSFS: leaf found in nextblock %ld, sfsobjectcontainer block is %ld \n",
988 nextblock, block_sfsobjectcontainer));
989 break;
991 else
993 UWORD containerentry =
994 (objectnode - AROS_BE2LONG(volume->blockbuffer[3]))/AROS_BE2LONG(volume->blockbuffer[4]);
995 nextblock = AROS_BE2LONG(volume->blockbuffer[containerentry + 5]) >> 4; /* 9-5 (2^9 = 512) */;
996 D(bug("[install] collectBlockListSFS: check next block %ld\n", nextblock));
1000 if (block_sfsobjectcontainer == 0)
1002 D(bug("[install] collectBlockListSFS: SFSObjectContainer not found\n"));
1003 Printf("SFSObjectContainer not found\n");
1004 return 0;
1009 /* Find the SFSObject in SFSObjectContainer for given objectnode */
1010 first_block = 0;
1011 while((block_sfsobjectcontainer != 0) && (first_block == 0))
1013 /* Read next SFS container block */
1014 retval = _readwriteBlock(volume, block_sfsobjectcontainer, volume->blockbuffer, volume->SizeBlock<<2,
1015 volume->readcmd);
1017 if (retval)
1019 D(bug("[install] collectBlockListSFS: ERROR reading block (error: %ld)\n", retval));
1020 Printf("ReadError %lu\n", (long)retval);
1021 return 0;
1024 /* Iterate over SFS objects and match the objectnode */
1026 * The first offset comes from :
1027 * sizeof(sfsblockheader) = uint32 + uint32 + uint32 (field of sfsobjectcontainer)
1028 * parent, next, previous = uint32 + uint32 + uint32 (fields of sfsobjectcontainers)
1030 tmpBytePtr = ((UBYTE*)volume->blockbuffer) + 12 + 12; /* tmpBytePtr points to first object in container */
1032 while (AROS_BE2LONG(((ULONG*)(tmpBytePtr + 4))[0]) > 0) /* check on the objectnode field */
1035 /* Compare objectnode */
1036 if (AROS_BE2LONG(((ULONG*)(tmpBytePtr + 4))[0]) == objectnode)
1038 /* Found! */
1039 first_block = AROS_BE2LONG(((ULONG*)(tmpBytePtr + 12))[0]); /* data */
1040 D(bug("[install] collectBlockListSFS: first block is %ld\n", first_block));
1041 break;
1044 /* Move to next object */
1045 /* Find end of name and end of comment */
1046 tmpBytePtr += 25; /* Point to name */
1047 count = 0;
1048 for (i = 2; i > 0; tmpBytePtr++, count++)
1049 if (*tmpBytePtr == '\0')
1050 i--;
1052 /* Correction for aligment */
1053 if ((count & 0x01) == 0 )
1054 tmpBytePtr++;
1057 /* Move to next sfs object container block */
1058 block_sfsobjectcontainer = AROS_BE2LONG(volume->blockbuffer[4]); /* next field */
1062 if (first_block == 0)
1064 D(bug("[install] collectBlockListSFS: First block not found\n"));
1065 Printf("First block not found\n");
1066 return 0;
1071 /* First file block found. Find all blocks of file */
1072 searchedblock = first_block;
1073 blk_count = 0;
1075 while(1)
1077 nextblock = block_extentbnoderoot;
1078 UBYTE * BNodePtr = NULL;
1080 while(1)
1082 /* Find the extentbnode for this block */
1084 D(bug("[install] collectBlockListSFS: searching in nextblock %d for extentbnode for block %ld\n",
1085 nextblock, searchedblock));
1087 UBYTE * BTreeContainerPtr = NULL;
1088 BNodePtr = NULL;
1090 _readwriteBlock(volume, nextblock, volume->blockbuffer, volume->SizeBlock<<2,
1091 volume->readcmd);
1093 BTreeContainerPtr = (UBYTE*)(volume->blockbuffer + 3); /* Starts right after the header */
1095 D(bug("[install] collectBlockListSFS: tree container nodecount: %d\n",
1096 AROS_BE2WORD(((UWORD*)BTreeContainerPtr)[0])));
1098 for (i = AROS_BE2WORD(((UWORD*)BTreeContainerPtr)[0]) - 1; i >=0; i--) /* Start from last element */
1100 /* Read the BNode */
1101 tmpBytePtr = BTreeContainerPtr + 4 + i * BTreeContainerPtr[3];
1103 if (AROS_BE2LONG(((ULONG*)(tmpBytePtr))[0]) <= searchedblock) /* Check on the key field */
1105 BNodePtr = tmpBytePtr;
1106 break;
1110 /* Fail if BNodePtr still NULL */
1111 if (BNodePtr == NULL)
1113 D(bug("[install] collectBlockListSFS: Failed to travers extentbnode tree.\n"));
1114 Printf("Failed to travers extentbnode tree.\n");
1115 return 0;
1118 /* If we are at the leaf, stop */
1119 if (BTreeContainerPtr[2])
1120 break;
1122 /* Else search further */
1123 nextblock = AROS_BE2LONG(((ULONG*)(BNodePtr))[1]); /* data / next field */
1126 /* Found. Add BlockList entry */
1127 D(bug("[install] collectBlockListSFS: extentbnode for block %ld found. Block count: %d\n",
1128 searchedblock, AROS_BE2WORD(((UWORD*)(BNodePtr + 12))[0])));
1130 /* Add blocklist entry */
1131 blk_count--;
1133 /* Check if we still have spece left to add data to BlockList */
1134 if ((blk_count-1) <= -BLCKLIST_ELEMENTS)
1136 D(bug("[install] collectBlockListSFS: ERROR: out of block space\n"));
1137 Printf("There is no more space to save blocklist in core.img\n");
1138 return 0;
1141 blocklist[blk_count].sector_lo = searchedblock;
1142 blocklist[blk_count].sector_hi = 0;
1143 blocklist[blk_count].count = AROS_BE2WORD(((UWORD*)(BNodePtr + 12))[0]);
1145 /* Handling of special situations */
1146 if (searchedblock == first_block)
1148 /* Writting first pack of blocks. Pointer needs to point to second file block */
1149 blocklist[blk_count].sector_lo++;
1150 blocklist[blk_count].count--;
1151 if (blocklist[blk_count].count == 0)
1153 /* This means that the first pack of blocks contained only one block - first block */
1154 /* Since the first blocklist needs to start at second file block, 'reset' the blk_count */
1155 /* so that next iteration will overwrite the current results */
1156 blk_count++;
1160 /* Are there more blocks to read? */
1161 if (AROS_BE2LONG(((ULONG*)(BNodePtr))[1]) == 0)
1163 D(bug("[install] collectBlockListSFS: All core.img blocks found!\n"));
1164 break;
1166 else
1167 searchedblock = AROS_BE2LONG(((ULONG*)(BNodePtr))[1]); /* data / next field */
1171 /* Correct blocks for volume start */
1173 /* Blocks in blocklist are relative to the first sector of the HD (not partition) */
1174 i = 0;
1175 for (count=-1;count>=blk_count;count--)
1177 blocklist[count].sector_lo += volume->startblock;
1178 blocklist[count].seg_adr = 0x820 + (i*32);
1179 i += blocklist[count].count;
1180 D(bug("[install] collectBlockListFFS: correcting block %d for partition start\n", count));
1181 D(bug("[install] collectBlockListFFS: sector : %ld seg_adr : %x\n",
1182 blocklist[count].sector_lo, blocklist[count].seg_adr));
1185 first_block += volume->startblock;
1187 return first_block;
1190 /* Flushes the cache on the volume containing the specified path. */
1191 VOID flushFS(CONST_STRPTR path)
1193 TEXT devname[256];
1194 UWORD i;
1196 for (i = 0; path[i] != ':'; i++)
1197 devname[i] = path[i];
1198 devname[i++] = ':';
1199 devname[i] = '\0';
1201 /* Try to flush 10 times. 5 seconds total */
1203 /* Failsafe in case first Inhibit fails in some way (was needed
1204 * for SFS because non flushed data was failing Inhibit) */
1206 for (i = 0; i < 10; i++)
1208 if (Inhibit(devname, DOSTRUE))
1210 Inhibit(devname, DOSFALSE);
1211 break;
1213 else
1214 Delay(25);
1218 BOOL writeCoreIMG(BPTR fh, UBYTE *buffer, struct Volume *volume)
1220 BOOL retval = FALSE;
1222 D(bug("[install] writeCoreIMG(%x)\n", volume));
1224 if (Seek(fh, 0, OFFSET_BEGINNING) != -1)
1226 D(bug("[install] writeCoreIMG - write first block\n"));
1228 /* write back first block */
1229 if (Write(fh, buffer, 512) == 512)
1233 /* read second core.img block */
1234 if (Read(fh, buffer, 512) == 512)
1236 /* set partition number where core.img is on */
1237 /* FIXME: set RDB part number of DH? */
1238 UBYTE *install_boot_device =
1239 buffer + GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE;
1241 D(bug("[install] set dos part = %d\n", volume->partnum));
1243 install_boot_device[0] = 0;
1244 install_boot_device[1] = 0;
1245 install_boot_device[2] = volume->partnum;
1247 /* write second core.img block back */
1248 if (Seek(fh, -512, OFFSET_CURRENT) != -1)
1250 if (Write(fh, buffer, 512) == 512)
1252 retval = TRUE;
1254 else
1255 Printf("Write Error\n");
1257 else
1258 Printf("Seek Error\n");
1260 else
1261 Printf("Read Error\n");
1263 else
1264 Printf("Write Error\n");
1266 else
1268 Printf("Seek Error\n");
1269 PrintFault(IoErr(), NULL);
1271 return retval;
1274 ULONG updateCoreIMG(CONST_STRPTR grubpath, /* path of grub dir */
1275 struct Volume *volume, /* volume core.img is on */
1276 ULONG *buffer /* a buffer of at least 512 bytes */)
1278 ULONG block = 0;
1279 struct FileInfoBlock fib;
1280 BPTR fh;
1281 TEXT coreimgpath[256];
1283 D(bug("[install] updateCoreIMG(%x)\n", volume));
1285 AddPart(coreimgpath, grubpath, 256);
1286 AddPart(coreimgpath, CORE_IMG_FILE_NAME, 256);
1287 fh = Open(coreimgpath, MODE_OLDFILE);
1288 if (fh)
1290 if (ExamineFH(fh, &fib))
1292 if (Read(fh, buffer, 512) == 512)
1295 Get and store all blocks of core.img in first block of core.img.
1296 First block of core.img will be returned.
1297 List of BlockNode starts at 512 - sizeof(BlockNode). List grows downwards.
1298 buffer is ULONG, buffer[128] is one pointer after first element(upwards).
1299 collectBlockList assumes it receives one pointer after first element(upwards).
1302 if (volume->dos_id == ID_SFS_BE_DISK)
1304 D(bug("[install] core.img on SFS file system\n"));
1305 block = collectBlockListSFS
1306 (volume, fib.fib_DiskKey, (struct BlockNode *)&buffer[128]);
1308 else
1309 if ((volume->dos_id == ID_FFS_DISK) || (volume->dos_id == ID_INTER_DOS_DISK) ||
1310 (volume->dos_id == ID_INTER_FFS_DISK) || (volume->dos_id == ID_FASTDIR_DOS_DISK) ||
1311 (volume->dos_id == ID_FASTDIR_FFS_DISK))
1313 D(bug("[install] core.img on FFS file system\n"));
1314 block = collectBlockListFFS
1315 (volume, fib.fib_DiskKey, (struct BlockNode *)&buffer[128]);
1317 else
1319 block = 0;
1320 D(bug("[install] core.img on unsupported file system\n"));
1321 Printf("Unsupported file system\n");
1324 D(bug("[install] core.img first block: %ld\n", block));
1326 if (block)
1328 if (!writeCoreIMG(fh, (UBYTE *)buffer, volume))
1329 block = 0;
1332 else
1333 Printf("%s: Read Error\n", coreimgpath);
1335 else
1336 PrintFault(IoErr(), coreimgpath);
1338 Close(fh);
1341 else
1342 PrintFault(IoErr(), coreimgpath);
1343 return block;
1346 /* Installs boot.img to MBR and updates core.img */
1347 BOOL installGrubFiles(struct Volume *coreimgvol, /* core.img volume */
1348 CONST_STRPTR grubpath, /* path to grub files */
1349 ULONG unit, /* unit core.img is on */
1350 struct Volume *bootimgvol) /* boot device for boot.img */
1352 BOOL retval = FALSE;
1353 TEXT bootimgpath[256];
1354 ULONG block;
1356 D(bug("[install] installStageFiles(%x)\n", bootimgvol));
1358 /* Flush GRUB volume's cache */
1359 flushFS(grubpath);
1361 block = updateCoreIMG(grubpath, coreimgvol, bootimgvol->blockbuffer);
1363 if (block)
1365 AddPart(bootimgpath, grubpath, 256);
1366 AddPart(bootimgpath, (CONST_STRPTR) "i386-pc/boot.img", 256);
1367 if (writeBootIMG(bootimgpath, bootimgvol, coreimgvol, block, unit))
1368 retval = TRUE;
1370 else
1371 bug("failed %d\n", IoErr());
1373 return retval;
1376 int main(int argc, char **argv)
1378 struct RDArgs *rdargs;
1379 struct Volume *grubvol;
1380 struct Volume *bbvol;
1381 struct FileSysStartupMsg *fssm;
1382 int ret = RETURN_OK;
1384 D(bug("[install] main()\n"));
1386 rdargs = ReadArgs(template, myargs, NULL);
1387 if (rdargs)
1389 CONST_STRPTR bootDevice = (CONST_STRPTR) myargs[0];
1390 LONG unit = *(LONG *) myargs[1];
1391 LONG *partnum = (LONG *) myargs[2];
1392 CONST_STRPTR grubpath = (CONST_STRPTR) myargs[3];
1394 D(bug("[install] FORCELBA = %d\n", myargs[4]));
1395 if (myargs[4])
1396 Printf("FORCELBA ignored\n");
1398 if (partnum)
1400 Printf("PARTITIONNUMBER not supported yet\n");
1401 FreeArgs(rdargs);
1402 return RETURN_ERROR;
1405 fssm = getDiskFSSM(grubpath);
1406 if (fssm != NULL)
1408 CONST_STRPTR grubDevice = AROS_BSTR_ADDR(fssm->fssm_Device);
1410 if (!strcmp((const char *) grubDevice, (const char *) bootDevice))
1412 struct DosEnvec *dosEnvec;
1413 dosEnvec = (struct DosEnvec *) BADDR(fssm->fssm_Environ);
1415 grubvol = getGrubStageVolume(grubDevice, fssm->fssm_Unit,
1416 fssm->fssm_Flags, dosEnvec);
1417 if (grubvol)
1420 bbvol = getBBVolume(bootDevice, unit, partnum);
1421 if (bbvol)
1423 if (!installGrubFiles(grubvol, grubpath,
1424 fssm->fssm_Unit, bbvol))
1425 ret = RETURN_ERROR;
1427 uninitVolume(bbvol);
1429 else
1431 D(bug("getBBVolume failed miserably\n"));
1432 ret = RETURN_ERROR;
1435 uninitVolume(grubvol);
1438 else
1440 Printf("%s is not on device %s unit %ld\n",
1441 grubpath, bootDevice, (long)unit);
1442 ret = RETURN_ERROR;
1445 else if (fssm)
1447 Printf("kernel path must begin with a device name\n");
1448 FreeArgs(rdargs);
1449 ret = RETURN_ERROR;
1452 FreeArgs(rdargs);
1454 else
1455 PrintFault(IoErr(), (STRPTR) argv[0]);
1457 return ret;