bool
PartitionMap::Check(off_t sessionSize) const
{
	int32 partitionCount = CountPartitions();

	// 1. check partition locations
	for (int32 i = 0; i < partitionCount; i++) {
		if (!PartitionAt(i)->CheckLocation(sessionSize))
			return false;
	}

	// 2. check overlapping of partitions and location of partition tables
	bool result = true;
	Partition** byOffset = new(nothrow) Partition*[partitionCount];
	off_t* tableOffsets = new(nothrow) off_t[partitionCount - 3];
	if (byOffset && tableOffsets) {
		// fill the arrays
		int32 byOffsetCount = 0;
		int32 tableOffsetCount = 1;	// primary partition table
		tableOffsets[0] = 0;
		for (int32 i = 0; i < partitionCount; i++) {
			Partition* partition = (Partition*)PartitionAt(i);
			if (!partition->IsExtended())
				byOffset[byOffsetCount++] = partition;

			// add only logical partition partition table locations
			if (i >= 4) {
				tableOffsets[tableOffsetCount++]
					= partition->PartitionTableOffset();
			}
		}

		// sort the arrays
		qsort(byOffset, byOffsetCount, sizeof(Partition*),
			cmp_partition_offset);
		qsort(tableOffsets, tableOffsetCount, sizeof(off_t), cmp_offset);

		// check for overlappings
		off_t nextOffset = 0;
		for (int32 i = 0; i < byOffsetCount; i++) {
			Partition* partition = byOffset[i];
			if (partition->Offset() < nextOffset && i > 0) {
				Partition* previousPartition = byOffset[i - 1];
				off_t previousSize = previousPartition->Size()
					- (nextOffset - partition->Offset());
				TRACE(("intel: PartitionMap::Check(): "));
				if (previousSize == 0) {
					previousPartition->Unset();
					TRACE(("partition offset hides previous partition."
						" Removing previous partition from disk layout.\n"));
				} else {
					TRACE(("overlapping partitions! Setting partition %ld "
						"size to %lld\n", i - 1, previousSize));
					previousPartition->SetSize(previousSize);
				}
			}
			nextOffset = partition->Offset() + partition->Size();
		}

		// check uniqueness of partition table offsets and whether they lie
		// outside of the non-extended partitions
		if (result) {
			for (int32 i = 0; i < tableOffsetCount; i++) {
				if (i > 0 && tableOffsets[i] == tableOffsets[i - 1]) {
					TRACE(("intel: PartitionMap::Check(): same partition table "
						"for different extended partitions!\n"));
					result = false;
					break;
				} else if (is_inside_partitions(tableOffsets[i],
						(const Partition**)byOffset, byOffsetCount)) {
					TRACE(("intel: PartitionMap::Check(): a partition table "
						"lies inside a non-extended partition!\n"));
					result = false;
					break;
				}
			}
		}
	} else
		result = false;		// no memory: assume failure

	// cleanup
	delete[] byOffset;
	delete[] tableOffsets;

	return result;
}
Exemple #2
0
// main
int
main(int argc, const char *const *argv)
{
	kArgc = argc;
	kArgv = argv;

	if (argc < 2)
		print_usage_and_exit(true);

	// parameters
	const char **files = new const char*[argc];
	int fileCount = 0;
	bool dryRun = false;
	off_t startOffset = 0;

	// parse arguments
	for (int argi = 1; argi < argc;) {
		const char *arg = argv[argi++];

		if (arg[0] == '-') {
			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
				print_usage_and_exit(false);
			} else if (strcmp(arg, "--dry-run") == 0) {
				dryRun = true;
			} else if (strcmp(arg, "-alert") == 0) {
				// ignore
			} else if (strcmp(arg, "-full") == 0) {
				// ignore
			} else if (strcmp(arg, "--start-offset") == 0) {
				if (argi >= argc)
					print_usage_and_exit(true);
				startOffset = strtoll(argv[argi++], NULL, 0);
			} else if (strcmp(arg, "-safe") == 0) {
				fprintf(stderr, "Error: Sorry, BeOS R3 isn't supported!\n");
				exit(1);
			} else {
				print_usage_and_exit(true);
			}

		} else {
			files[fileCount++] = arg;
		}
	}

	// we need at least one file
	if (fileCount == 0)
		print_usage_and_exit(true);

	// read the boot code
	uint8 *bootCodeData = NULL;
#ifndef __ANTARES__
	bootCodeData = read_boot_code_data(argv[0]);
#else
	image_info info;
	if (find_own_image(&info) == B_OK)
		bootCodeData = read_boot_code_data(info.name);
#endif
	if (!bootCodeData) {
		fprintf(stderr, "Error: Failed to read \n");
		exit(1);
	}

	// iterate through the files and make them bootable
	status_t error;
	for (int i = 0; i < fileCount; i++) {
		const char *fileName = files[i];
		BEntry entry;
		error = entry.SetTo(fileName, true);
		if (error != B_OK) {
			fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
				fileName, strerror(error));
			exit(1);
		}

		// get stat to check the type of the file
		struct stat st;
		error = entry.GetStat(&st);
		if (error != B_OK) {
			fprintf(stderr, "Error: Failed to stat \"%s\": %s\n",
				fileName, strerror(error));
			exit(1);
		}

		bool noPartition = false;
		int64 partitionOffset = 0;
		fs_info info;	// needs to be here (we use the device name later)
		if (S_ISDIR(st.st_mode)) {
			#if defined(__BEOS__) || defined(__ANTARES__)

				// a directory: get the device
				error = fs_stat_dev(st.st_dev, &info);
				if (error != B_OK) {
					fprintf(stderr, "Error: Failed to determine device for "
						"\"%s\": %s\n", fileName, strerror(error));
					exit(1);
				}

				fileName = info.device_name;

			#else

				(void)info;
				fprintf(stderr, "Error: Specifying directories not supported "
					"on this platform!\n");
				exit(1);

			#endif

		} else if (S_ISREG(st.st_mode)) {
			// a regular file: fine
			noPartition = true;
		} else if (S_ISCHR(st.st_mode)) {
			// character special: a device or partition under BeOS
			// or under FreeBSD
			#if !(defined(__BEOS__) || defined(__ANTARES__)) && !defined(ANTARES_HOST_PLATFORM_FREEBSD)

				fprintf(stderr, "Error: Character special devices not "
					"supported on this platform.\n");
				exit(1);

			#endif

			#ifdef ANTARES_HOST_PLATFORM_FREEBSD

				// chop off the trailing number
				int fileNameLen = strlen(fileName);
				int baseNameLen = -1;
				for (int k = fileNameLen - 1; k >= 0; k--) {
					if (!isdigit(fileName[k])) {
						baseNameLen = k + 1;
						break;
					}
				}

				// Remove de 's' from 'ad2s2' slice device (partition for DOS
				// users) to get 'ad2' base device
				baseNameLen--;

				if (baseNameLen < 0) {
					// only digits?
					fprintf(stderr, "Error: Failed to get base device name.\n");
					exit(1);
				}

				if (baseNameLen < fileNameLen) {
					// get base device name and partition index
					char baseDeviceName[B_PATH_NAME_LENGTH];
					int partitionIndex = atoi(fileName + baseNameLen + 1);
						// Don't forget the 's' of slice :)
					memcpy(baseDeviceName, fileName, baseNameLen);
					baseDeviceName[baseNameLen] = '\0';

					// open base device
					int baseFD = open(baseDeviceName, O_RDONLY);
					if (baseFD < 0) {
						fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
							baseDeviceName, strerror(errno));
						exit(1);
					}

					// get device size
					int64 deviceSize;
					if (ioctl(baseFD, DIOCGMEDIASIZE, &deviceSize) == -1) {
						fprintf(stderr, "Error: Failed to get device geometry "
							"for \"%s\": %s\n", baseDeviceName,
							strerror(errno));
						exit(1);
					}

					// parse the partition map
					// TODO: block size!
					PartitionMapParser parser(baseFD, 0, deviceSize, 512);
					PartitionMap map;
					error = parser.Parse(NULL, &map);
					if (error != B_OK) {
						fprintf(stderr, "Error: Parsing partition table on "
							"device \"%s\" failed: %s\n", baseDeviceName,
							strerror(error));
						exit(1);
					}

					close(baseFD);

					// check the partition we are supposed to write at
					Partition *partition = map.PartitionAt(partitionIndex - 1);
					if (!partition || partition->IsEmpty()) {
						fprintf(stderr, "Error: Invalid partition index %d.\n",
							partitionIndex);
						dump_partition_map(map);
						exit(1);
					}

					if (partition->IsExtended()) {
						fprintf(stderr, "Error: Partition %d is an extended "
							"partition.\n", partitionIndex);
						dump_partition_map(map);
						exit(1);
					}

					partitionOffset = partition->Offset();

				} else {
					// The given device is the base device. We'll write at
					// offset 0.
				}

			#endif // ANTARES_HOST_PLATFORM_FREEBSD

		} else if (S_ISBLK(st.st_mode)) {
			// block device: a device or partition under Linux or Darwin
			#ifdef ANTARES_HOST_PLATFORM_LINUX

				// chop off the trailing number
				int fileNameLen = strlen(fileName);
				int baseNameLen = -1;
				for (int k = fileNameLen - 1; k >= 0; k--) {
					if (!isdigit(fileName[k])) {
						baseNameLen = k + 1;
						break;
					}
				}

				if (baseNameLen < 0) {
					// only digits?
					fprintf(stderr, "Error: Failed to get base device name.\n");
					exit(1);
				}

				if (baseNameLen < fileNameLen) {
					// get base device name and partition index
					char baseDeviceName[B_PATH_NAME_LENGTH];
					int partitionIndex = atoi(fileName + baseNameLen);
					memcpy(baseDeviceName, fileName, baseNameLen);
					baseDeviceName[baseNameLen] = '\0';

					// open base device
					int baseFD = open(baseDeviceName, O_RDONLY);
					if (baseFD < 0) {
						fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
							baseDeviceName, strerror(errno));
						exit(1);
					}

					// get device size -- try BLKGETSIZE64, but, if it doesn't
					// work, fall back to the obsolete HDIO_GETGEO
					int64 deviceSize;
					hd_geometry geometry;
					if (ioctl(baseFD, BLKGETSIZE64, &deviceSize) == 0
						&& deviceSize > 0) {
						// looks good
					} else if (ioctl(baseFD, HDIO_GETGEO, &geometry) == 0) {
						deviceSize = (int64)geometry.heads * geometry.sectors
							* geometry.cylinders * 512;
					} else {
						fprintf(stderr, "Error: Failed to get device geometry "
							"for \"%s\": %s\n", baseDeviceName,
							strerror(errno));
						exit(1);
					}

					// parse the partition map
					// TODO: block size!
					PartitionMapParser parser(baseFD, 0, deviceSize, 512);
					PartitionMap map;
					error = parser.Parse(NULL, &map);
					if (error != B_OK) {
						fprintf(stderr, "Error: Parsing partition table on "
							"device \"%s\" failed: %s\n", baseDeviceName,
							strerror(error));
						exit(1);
					}

					close(baseFD);

					// check the partition we are supposed to write at
					Partition *partition = map.PartitionAt(partitionIndex - 1);
					if (!partition || partition->IsEmpty()) {
						fprintf(stderr, "Error: Invalid partition index %d.\n",
							partitionIndex);
						dump_partition_map(map);
						exit(1);
					}

					if (partition->IsExtended()) {
						fprintf(stderr, "Error: Partition %d is an extended "
							"partition.\n", partitionIndex);
						dump_partition_map(map);
						exit(1);
					}

					partitionOffset = partition->Offset();
				} else {
					// The given device is the base device. We'll write at
					// offset 0.
				}

			#elif defined(ANTARES_HOST_PLATFORM_DARWIN)
				// chop off the trailing number
				int fileNameLen = strlen(fileName);
				int baseNameLen = fileNameLen - 2;

				// get base device name and partition index
				char baseDeviceName[B_PATH_NAME_LENGTH];
				int partitionIndex = atoi(fileName + baseNameLen + 1);
				memcpy(baseDeviceName, fileName, baseNameLen);
				baseDeviceName[baseNameLen] = '\0';

				// open base device
				int baseFD = open(baseDeviceName, O_RDONLY);
				if (baseFD < 0) {
					fprintf(stderr, "Error: Failed to open \"%s\": %s\n",
							baseDeviceName, strerror(errno));
					exit(1);
				}

				// get device size
				int64 blockSize;
				int64 blockCount;
				int64 deviceSize;
				if (ioctl(baseFD, DKIOCGETBLOCKSIZE, &blockSize) == -1) {
					fprintf(stderr, "Error: Failed to get block size "
							"for \"%s\": %s\n", baseDeviceName,
							strerror(errno));
					exit(1);
				}
				if (ioctl(baseFD, DKIOCGETBLOCKCOUNT, &blockCount) == -1) {
					fprintf(stderr, "Error: Failed to get block count "
							"for \"%s\": %s\n", baseDeviceName,
							strerror(errno));
					exit(1);
				}

				deviceSize = blockSize * blockCount;

				// parse the partition map
				PartitionMapParser parser(baseFD, 0, deviceSize, blockSize);
				PartitionMap map;
				error = parser.Parse(NULL, &map);
				if (error != B_OK) {
					fprintf(stderr, "Error: Parsing partition table on "
							"device \"%s\" failed: %s\n", baseDeviceName,
							strerror(error));
					exit(1);
				}

				close(baseFD);

				// check the partition we are supposed to write at
				Partition *partition = map.PartitionAt(partitionIndex - 1);
				if (!partition || partition->IsEmpty()) {
					fprintf(stderr, "Error: Invalid partition index %d.\n",
						partitionIndex);
					dump_partition_map(map);
					exit(1);
				}

				if (partition->IsExtended()) {
					fprintf(stderr, "Error: Partition %d is an extended "
						"partition.\n", partitionIndex);
					dump_partition_map(map);
					exit(1);
				}
				partitionOffset = partition->Offset();
			#else
			// partitions are block devices under Antares, but not under BeOS
			#ifndef __ANTARES__
				fprintf(stderr, "Error: Block devices not supported on this "
					"platform!\n");
				exit(1);
			#endif	// __ANTARES__

			#endif
		} else {
			fprintf(stderr, "Error: File type of \"%s\" is not supported.\n",
				fileName);
			exit(1);
		}

		// open the file
		int fd = open(fileName, O_RDWR);
		if (fd < 0) {
			fprintf(stderr, "Error: Failed to open \"%s\": %s\n", fileName,
				strerror(errno));
			exit(1);
		}

		#if (defined(__BEOS__) || defined(__ANTARES__))

			// get a partition info
			if (!noPartition
				&& strlen(fileName) >= 3
				&& strncmp("raw", fileName + strlen(fileName) - 3, 3)) {
				partition_info partitionInfo;
				if (ioctl(fd, B_GET_PARTITION_INFO, &partitionInfo,
						sizeof(partitionInfo)) == 0) {
					partitionOffset = partitionInfo.offset;
				} else {
					fprintf(stderr, "Error: Failed to get partition info: %s\n",
						strerror(errno));
					exit(1);
				}
			}

		#endif	// __BEOS__

		// adjust the partition offset in the boot code data
		// hard coded sector size: 512 bytes
		*(uint32*)(bootCodeData + kPartitionOffsetOffset)
			= B_HOST_TO_LENDIAN_INT32((uint32)(partitionOffset / 512));

		// write the boot code
		printf("Writing boot code to \"%s\" (partition offset: %" B_PRId64
			" bytes, start offset = %" B_PRIdOFF ") "
			"...\n", fileName, partitionOffset, startOffset);

		write_boot_code_part(fileName, fd, startOffset, bootCodeData, 0,
			kFirstBootCodePartSize, dryRun);
		write_boot_code_part(fileName, fd, startOffset, bootCodeData,
			kSecondBootCodePartOffset, kSecondBootCodePartSize,
			dryRun);

#ifdef __ANTARES__
		// check if this partition is mounted
		BDiskDeviceRoster roster;
		BPartition* partition;
		BDiskDevice device;
		status_t status = roster.GetPartitionForPath(fileName, &device,
			&partition);
		if (status != B_OK) {
			status = roster.GetFileDeviceForPath(fileName, &device);
			if (status == B_OK)
				partition = &device;
		}
		if (status == B_OK && partition->IsMounted() && !dryRun) {
			// This partition is mounted, we need to tell BFS to update its
			// boot block (we are using part of the same logical block).
			BPath path;
			status = partition->GetMountPoint(&path);
			if (status == B_OK) {
				update_boot_block update;
				update.offset = kSecondBootCodePartOffset - 512;
				update.data = bootCodeData + kSecondBootCodePartOffset;
				update.length = kSecondBootCodePartSize;

				int mountFD = open(path.Path(), O_RDONLY);
				if (ioctl(mountFD, BFS_IOCTL_UPDATE_BOOT_BLOCK, &update,
						sizeof(update_boot_block)) != 0) {
					fprintf(stderr, "Could not update BFS boot block: %s\n",
						strerror(errno));
				}
				close(mountFD);
			} else {
				fprintf(stderr, "Could not update BFS boot code while the "
					"partition is mounted!\n");
			}
		}
#endif	// __ANTARES__

		close(fd);
	}

	return 0;
}