1 #include <errno.h> 2 #include <fcntl.h> 3 #include <unistd.h> 4 #include <stdint.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <sys/stat.h> 9 10 static uint8_t *sCopyBuffer = NULL; 11 static size_t sCopyBufferSize = 2 * 1024 * 1024; 12 13 14 int 15 copyLoop(int from, int to, off_t position) 16 { 17 while (true) { 18 ssize_t copyLength = read(from, sCopyBuffer, sCopyBufferSize); 19 if (copyLength <= 0) 20 return copyLength; 21 22 ssize_t written = pwrite(to, sCopyBuffer, copyLength, position); 23 if (written != copyLength) { 24 if (written < 0) 25 return written; 26 else 27 return -1; 28 } 29 30 position += copyLength; 31 } 32 } 33 34 35 void 36 checkError(bool failed, const char *message) 37 { 38 if (!failed) 39 return; 40 41 printf("%s: %s\n", message, strerror(errno)); 42 free(sCopyBuffer); 43 exit(1); 44 } 45 46 47 void 48 chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack, 49 uint32_t headsPerCylinder) 50 { 51 if (offset >= 1024 * sectorsPerTrack * headsPerCylinder) { 52 // not encodable, force LBA 53 address[0] = 0xff; 54 address[1] = 0xff; 55 address[2] = 0xff; 56 return; 57 } 58 59 uint32_t cylinders = 0; 60 uint32_t heads = 0; 61 uint32_t sectors = 0; 62 63 uint32_t temp = 0; 64 while (temp * sectorsPerTrack * headsPerCylinder <= offset) 65 cylinders = temp++; 66 67 offset -= (sectorsPerTrack * headsPerCylinder * cylinders); 68 69 temp = 0; 70 while (temp * sectorsPerTrack <= offset) 71 heads = temp++; 72 73 sectors = offset - (sectorsPerTrack * heads) + 1; 74 75 address[0] = heads; 76 address[1] = sectors; 77 address[1] |= (cylinders >> 2) & 0xc0; 78 address[2] = cylinders; 79 } 80 81 82 int 83 main(int argc, char *argv[]) 84 { 85 if (argc < 5) { 86 printf("usage: %s <outputFile> <isoFile> <mbrFile> <imageFile>\n", 87 argv[0]); 88 return 1; 89 } 90 91 sCopyBuffer = (uint8_t *)malloc(sCopyBufferSize); 92 checkError(sCopyBuffer == NULL, "no memory for copy buffer"); 93 94 static const size_t kBlockSize = 512; 95 96 int outputFile = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 97 S_IRUSR | S_IWUSR); 98 checkError(outputFile < 0, "failed to open output file"); 99 100 int isoFile = open(argv[2], O_RDONLY); 101 checkError(isoFile < 0, "failed to open ISO file"); 102 103 struct stat stat; 104 int result = fstat(isoFile, &stat); 105 checkError(result != 0, "failed to stat ISO file"); 106 off_t isoSize = stat.st_size; 107 108 int mbrFile = open(argv[3], O_RDONLY); 109 checkError(mbrFile < 0, "failed to open MBR file"); 110 111 int imageFile = open(argv[4], O_RDONLY); 112 checkError(imageFile < 0, "failed to open image file"); 113 114 result = fstat(imageFile, &stat); 115 checkError(result != 0, "failed to stat image file"); 116 117 off_t imageSize = stat.st_size; 118 119 result = copyLoop(isoFile, outputFile, 0); 120 checkError(result != 0, "failed to copy iso file to output"); 121 122 // isoSize rounded to the next full megabyte 123 off_t alignment = 1 * 1024 * 1024 - 1; 124 off_t imageOffset = (isoSize + alignment) & ~alignment; 125 126 result = copyLoop(imageFile, outputFile, imageOffset); 127 checkError(result != 0, "failed to copy image file to output"); 128 129 result = copyLoop(mbrFile, outputFile, 0); 130 checkError(result != 0, "failed to copy mbr to output"); 131 132 // construct the partition table 133 uint8_t partition[16] = { 134 0x80, // active 135 0xff, 0xff, 0xff, // CHS first block (default to LBA) 136 0xeb, // partition type (BFS) 137 0xff, 0xff, 0xff, // CHS last block (default to LBA) 138 0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below) 139 0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below) 140 }; 141 142 alignment = kBlockSize - 1; 143 imageSize = (imageSize + alignment) & ~alignment; 144 145 // fill in LBA values 146 uint32_t partitionOffset = (uint32_t)(imageOffset / kBlockSize); 147 ((uint32_t *)partition)[2] = partitionOffset; 148 ((uint32_t *)partition)[3] = (uint32_t)(imageSize / kBlockSize); 149 150 #if 0 151 // while this should basically work, it makes the boot code needlessly 152 // use chs which has a high potential of failure due to the geometry 153 // being unknown beforehand (a fixed geometry would be used here). 154 155 uint32_t sectorsPerTrack = 63; 156 uint32_t headsPerCylinder = 255; 157 158 // fill in CHS values 159 chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack, 160 headsPerCylinder); 161 chsAddressFor(partitionOffset + (uint32_t)(imageSize / kBlockSize), 162 &partition[5], sectorsPerTrack, headsPerCylinder); 163 #endif 164 165 ssize_t written = pwrite(outputFile, partition, 16, 512 - 2 - 16 * 4); 166 checkError(written != 16, "failed to write partition entry"); 167 168 // and make the image bootable 169 written = pwrite(outputFile, &partitionOffset, 4, 170 imageOffset + 512 - 2 - 4); 171 checkError(written != 4, "failed to make image bootable"); 172 173 free(sCopyBuffer); 174 return 0; 175 } 176