static int vbr_write(struct exfat_dev* dev) { struct exfat_super_block sb; uint32_t checksum; le32_t* sector = malloc(get_sector_size()); size_t i; if (sector == NULL) { exfat_error("failed to allocate sector-sized block of memory"); return 1; } init_sb(&sb); if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0) { free(sector); exfat_error("failed to write super block sector"); return 1; } checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block)); memset(sector, 0, get_sector_size()); sector[get_sector_size() / sizeof(sector[0]) - 1] = cpu_to_le32(0xaa550000); for (i = 0; i < 8; i++) { if (exfat_write(dev, sector, get_sector_size()) < 0) { free(sector); exfat_error("failed to write a sector with boot signature"); return 1; } checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum); } memset(sector, 0, get_sector_size()); for (i = 0; i < 2; i++) { if (exfat_write(dev, sector, get_sector_size()) < 0) { free(sector); exfat_error("failed to write an empty sector"); return 1; } checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum); } for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++) sector[i] = cpu_to_le32(checksum); if (exfat_write(dev, sector, get_sector_size()) < 0) { free(sector); exfat_error("failed to write checksum sector"); return 1; } free(sector); return 0; }
static ssize_t read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) { int sector_size = get_sector_size(fd); off_t offset = lba * sector_size; uint64_t lastlba; ssize_t bytesread; lseek(fd, offset, SEEK_SET); bytesread = read(fd, buffer, bytes); lastlba = last_lba(fd); if (!lastlba) return bytesread; /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. This is only used by gpt.c, and only to read one sector, so we don't have to be fancy. */ if (!bytesread && !(lastlba & 1) && lba == lastlba) { bytesread = read_lastoddsector(fd, lba, buffer, bytes); } return bytesread; }
static void init_sb(struct exfat_super_block* sb) { uint32_t clusters_max; uint32_t fat_sectors; clusters_max = get_volume_size() / get_cluster_size(); fat_sectors = DIV_ROUND_UP((loff_t) clusters_max * sizeof(cluster_t), get_sector_size()); memset(sb, 0, sizeof(struct exfat_super_block)); sb->jump[0] = 0xeb; sb->jump[1] = 0x76; sb->jump[2] = 0x90; memcpy(sb->oem_name, "EXFAT ", sizeof(sb->oem_name)); sb->sector_start = cpu_to_le64(get_first_sector()); sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size()); sb->fat_sector_start = cpu_to_le32( fat.get_alignment() / get_sector_size()); sb->fat_sector_count = cpu_to_le32(ROUND_UP( le32_to_cpu(sb->fat_sector_start) + fat_sectors, 1 << get_spc_bits()) - le32_to_cpu(sb->fat_sector_start)); sb->cluster_sector_start = cpu_to_le32( get_position(&cbm) / get_sector_size()); sb->cluster_count = cpu_to_le32(clusters_max - ((le32_to_cpu(sb->fat_sector_start) + le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits())); sb->rootdir_cluster = cpu_to_le32( (get_position(&rootdir) - get_position(&cbm)) / get_cluster_size() + EXFAT_FIRST_DATA_CLUSTER); sb->volume_serial = cpu_to_le32(get_volume_serial()); sb->version.major = 1; sb->version.minor = 0; sb->volume_state = cpu_to_le16(0); sb->sector_bits = get_sector_bits(); sb->spc_bits = get_spc_bits(); sb->fat_count = 1; sb->drive_no = 0x80; sb->allocated_percent = 0; sb->boot_signature = cpu_to_le16(0xaa55); }
/************************************************************ * _get_num_sectors * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * Last LBA value on success * 0 on error * * Try getting BLKGETSIZE64 and BLKSSZGET first, * then BLKGETSIZE if necessary. * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 * which returns the number of 512-byte sectors, not the size of * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. ************************************************************/ static uint64_t _get_num_sectors(int filedes) { int rc; uint64_t bytes=0; rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); return 0; }
static int read_extended_partition(int fd, struct partition *ep, int en, struct slice *sp, int ns) { struct partition p; unsigned long start, here, next; unsigned char *bp; int loopct = 0; int moretodo = 1; int i, n=0; int sector_size_mul = get_sector_size(fd)/512; next = start = sector_size_mul * le32_to_cpu(ep->start_sect); while (moretodo) { here = next; moretodo = 0; if (++loopct > 100) return n; bp = (unsigned char *)getblock(fd, here); if (bp == NULL) return n; if (bp[510] != 0x55 || bp[511] != 0xaa) return n; for (i=0; i<2; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_extended(p.sys_type)) { if (p.start_sect && p.nr_sects && !moretodo) { next = start + sector_size_mul * le32_to_cpu(p.start_sect); moretodo = 1; } continue; } if (n < ns) { sp[n].start = here + sector_size_mul * le32_to_cpu(p.start_sect); sp[n].size = sector_size_mul * le32_to_cpu(p.nr_sects); sp[n].container = en + 1; n++; } else { fprintf(stderr, "dos_extd_partition: too many slices\n"); return n; } loopct = 0; } } return n; }
static int get_partition_info(int fd, uint32_t options, uint32_t part, uint64_t *start, uint64_t *size, uint8_t *signature, uint8_t *mbr_type, uint8_t *signature_type) { legacy_mbr *mbr; void *mbr_sector; size_t mbr_size; off_t offset __attribute__((unused)); int this_bytes_read = 0; int gpt_invalid=0, mbr_invalid=0; int rc=0; int sector_size = get_sector_size(fd); mbr_size = lcm(sizeof(*mbr), sector_size); if ((rc = posix_memalign(&mbr_sector, sector_size, mbr_size)) != 0) goto error; memset(mbr_sector, '\0', mbr_size); offset = lseek(fd, 0, SEEK_SET); this_bytes_read = read(fd, mbr_sector, mbr_size); if (this_bytes_read < (ssize_t)sizeof(*mbr)) { rc=1; goto error_free_mbr; } mbr = (legacy_mbr *)mbr_sector; gpt_invalid = gpt_disk_get_partition_info(fd, part, start, size, signature, mbr_type, signature_type, (options & EFIBOOT_OPTIONS_IGNORE_PMBR_ERR)?1:0); if (gpt_invalid) { mbr_invalid = msdos_disk_get_partition_info(fd, (options & EFIBOOT_OPTIONS_WRITE_SIGNATURE)?1:0, mbr, part, start, size, signature, mbr_type, signature_type); if (mbr_invalid) { rc=1; goto error_free_mbr; } } error_free_mbr: free(mbr_sector); error: return rc; }
/************************************************************ * _get_num_sectors * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * Last LBA value on success * 0 on error * * Try getting BLKGETSIZE64 and BLKSSZGET first, * then BLKGETSIZE if necessary. * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 * which returns the number of 512-byte sectors, not the size of * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. ************************************************************/ static uint64_t _get_num_sectors(int filedes) { unsigned long sectors=0; uint64_t bytes=0; int rc; if (kernel_has_blkgetsize64()) { rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); } rc = ioctl(filedes, BLKGETSIZE, §ors); if (rc) return 0; return sectors; }
/************************************************************ * _get_num_sectors * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * Last LBA value on success * 0 on error * * Try getting BLKGETSIZE64 and BLKSSZGET first, * then BLKGETSIZE if necessary. * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 * which returns the number of 512-byte sectors, not the size of * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. ************************************************************/ static uint64_t _get_num_sectors(int filedes) { unsigned long sectors=0; int rc; #if 0 uint64_t bytes=0; rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); #endif rc = ioctl(filedes, BLKGETSIZE, §ors); if (rc) return 0; return sectors; }
int read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) { struct partition p; unsigned long offset = all.start; int i, n=4; unsigned char *bp; uint64_t sector_size_mul = get_sector_size(fd)/512; bp = (unsigned char *)getblock(fd, offset); if (bp == NULL) return -1; if (bp[510] != 0x55 || bp[511] != 0xaa) return -1; for (i=0; i<4; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_gpt(p.sys_type)) return 0; if (i < ns) { sp[i].start = sector_size_mul * le32_to_cpu(p.start_sect); sp[i].size = sector_size_mul * le32_to_cpu(p.nr_sects); } else { fprintf(stderr, "dos_partition: too many slices\n"); break; } if (is_extended(p.sys_type)) { /* extended partitions only get one or two sectors mapped for LILO to install, whichever is needed to have 1kb of space */ if (sector_size_mul == 1) sp[i].size = 2; else sp[i].size = sector_size_mul; n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; }
/** * read_gpt_pt() * @fd * @all - slice with start/size of whole disk * * 0 if this isn't our partition table * number of partitions if successful * */ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns) { gpt_header *gpt = NULL; gpt_entry *ptes = NULL; uint32_t i; int n = 0; int last_used_index=-1; int sector_size_mul = get_sector_size(fd)/512; if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) { if (gpt) free (gpt); if (ptes) free (ptes); return 0; } for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) { if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) { sp[n].start = 0; sp[n].size = 0; n++; } else { sp[n].start = sector_size_mul * __le64_to_cpu(ptes[i].starting_lba); sp[n].size = sector_size_mul * (__le64_to_cpu(ptes[i].ending_lba) - __le64_to_cpu(ptes[i].starting_lba) + 1); last_used_index=n; n++; } } free (ptes); free (gpt); return last_used_index+1; }
int get_device_info(int fd, struct device_info *info) { struct stat stat; int ret; *info = device_info_clueless; ret = fstat(fd, &stat); if (ret < 0) { perror("fstat on target failed"); return -1; } if (S_ISREG(stat.st_mode)) { /* there is nothing more to discover for an image file */ info->type = TYPE_FILE; info->partition = 0; info->size = stat.st_size; return 0; } if (!S_ISBLK(stat.st_mode)) { /* neither regular file nor block device? not usable */ info->type = TYPE_BAD; return 0; } get_block_device_size(info, fd); get_block_geometry(info, fd); get_sector_size(info, fd); /* use udev information if available */ udev_fill_info(info, &stat); return 0; }
static ssize_t read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) { int sector_size = get_sector_size(fd); off_t offset = lba * sector_size; ssize_t bytesread; void *iobuf; size_t iobuf_size; int rc; off_t new_offset; iobuf_size = lcm(bytes, sector_size); rc = posix_memalign(&iobuf, sector_size, iobuf_size); if (rc) return rc; memset(iobuf, 0, bytes); new_offset = lseek(fd, offset, SEEK_SET); if (new_offset == (off_t)-1) { free(iobuf); return 0; } bytesread = read(fd, iobuf, iobuf_size); memcpy(buffer, iobuf, bytes); free(iobuf); /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. This is only used by gpt.c, and only to read one sector, so we don't have to be fancy. */ if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) { bytesread = read_lastoddsector(fd, lba, buffer, bytes); } return bytesread; }
static loff_t vbr_size(void) { return 12 * get_sector_size(); }
static off_t fat_alignment(void) { return (off_t) 128 * get_sector_size(); }
static loff_t vbr_alignment(void) { return get_sector_size(); }
static off64_t vbr_size(void) { return 12 * get_sector_size(); }
int main(int argc, char *argv[]) { int ret; int i, j; int dev_fd; struct master_boot_sector mbs; struct hd_geometry geo; u32 total_secs; u64 total_bytes; u32 part_secs; u32 sec_size; u32 start_sec; assert(argc > 2); dev_fd = open(argv[1], O_RDWR | O_BINARY); if (dev_fd < 0) { print_error("open device \"%s\"", argv[1]); return -1; } ret = fread_master_boot_sector(dev_fd, &mbs); if (ret < 0) { error_msg("fread_master_boot_sector"); goto out_close_dev; } ret = fget_device_geometry(dev_fd, &geo); if (ret < 0) { error_msg("fget_device_geometry"); goto out_close_dev; } ret = get_sector_size(dev_fd, &sec_size); if (ret < 0) { error_msg("get_sector_size"); goto out_close_dev; } ret = fget_device_size(dev_fd, &total_bytes); if (ret < 0) { error_msg("fget_device_size"); goto out_close_dev; } total_secs = total_bytes / sec_size; println("device size = %s = %d(sector)", size2text(total_bytes), total_secs); show_geometry(&geo); memset(mbs.disk_part_tables, 0, 64); start_sec = sector_cylinder_lalignment(text2size(argv[2], NULL) / sec_size, &geo); println("start_sec = %d(sector)", start_sec); if (start_sec == 0) { start_sec = 1; } for (i = 0, j = 3; i < 4 && j < argc; i++, j++) { part_secs = sector_cylinder_alignment_auto(text2size(argv[j], NULL) / sec_size, &geo); println("partition start address = %d(sector), size = %d(sector)", start_sec, part_secs); if (start_sec + part_secs > total_secs) { ret = -1; error_msg("partition size is too large"); goto out_close_dev; } set_part_address(mbs.disk_part_tables + i, &geo, start_sec, calculate_partition_size(start_sec, part_secs, &geo)); mbs.disk_part_tables[i].file_system_mark = 0x83; start_sec += part_secs; } if (i < 4 && start_sec < total_secs) { part_secs = sector_cylinder_lalignment(total_secs - start_sec, &geo); set_part_address(mbs.disk_part_tables + i, &geo, start_sec, calculate_partition_size(start_sec, part_secs, &geo)); mbs.disk_part_tables[i].file_system_mark = 0x83; } mbs.magic_number = 0xAA55; ret = fwrite_master_boot_sector(dev_fd, &mbs); if (ret < 0) { error_msg("fwrite_master_boot_sector"); goto out_close_dev; } ret = freread_part_table(dev_fd); if (ret < 0) { error_msg("freread_part_table_retry"); goto out_close_dev; } out_close_dev: close(dev_fd); return ret; }
int get_cluster_size(void) { return get_sector_size() << get_spc_bits(); }
int get_sector_bits(int drive) { int sector_size = get_sector_size(drive); return log2(sector_size); }