Exemplo n.º 1
0
Arquivo: min.c Projeto: kmcostel/453
void build_superblock(superblock* superBlock, FILE* diskImage,
                      unsigned long offset, bool verbose) {
    fseek(diskImage, SUPER_BLOCK_OFFSET + offset, SEEK_SET);
    fread(superBlock, sizeof(superblock), 1, diskImage);
    if (verbose) {
        print_superblock(superBlock);
    }
    if (superBlock->s_magic != MINIX_MAGIC) {
        fprintf(stderr, "Bad magic number. (%#x)\n", superBlock->s_magic);
        fprintf(stderr, "This doesn't look like a MINIX filesystem.\n");
        exit(1);
    }
}
Exemplo n.º 2
0
int main(int argc, char **argv)
{
	struct minix_superblock sb;
	unsigned long size = 0;

	size = get_file_size();
	file_system = map2memory(size);
	assert(file_system != NULL);

	read_superblock(&sb);

	print_superblock(&sb);

	printf("first data zone is 0x%x\n", get_first_data_zone(sb));

	directory_walk(&sb, get_first_data_zone(sb));

	find_file_test(&sb);
	read_file_test(&sb);

	munmap(file_system, size);

	return 0;
}
Exemplo n.º 3
0
int ext2(void) {
    printf("Hello World, this is the Ext2 FS\n");
    // Hardcode test fs into memory so that we can test read
    // Set up the superblock
    struct ext2_super_block *sb;
    sb = (struct ext2_super_block*) (VIRT_MEM_LOCATION +
                                     EXT2_SUPERBLOCK_LOCATION);
    sb->s_inodes_count          = 50;
    sb->s_blocks_count          = 8192;
    sb->s_r_blocks_count        = 6;
    sb->s_free_blocks_count     = 8186;
    sb->s_free_inodes_count     = 49;
    sb->s_first_data_block      = 1;
    sb->s_log_block_size        = 0;
    sb->s_log_frag_size         = 0;
    sb->s_blocks_per_group      = 8192;
    sb->s_frags_per_group       = 8192;
    sb->s_inodes_per_group      = 50;
    sb->s_magic                 = EXT2_MAGIC;
    sb->s_state                 = EXT2_VALID_FS;
    sb->s_errors                = EXT2_ERRORS_CONTINUE;
    sb->s_creator_os            = EXT2_OS_XINU;
    sb->s_first_ino             = 2;
    sb->s_inode_size            = sizeof( struct ext2_inode );
    sb->s_block_group_nr        = 0;
    char name[16]               = "FAKE RAM FS :D";
    memcpy(sb->s_volume_name,name,16);

    // Set up the group descriptors table
    struct ext2_group_desc *gpd;
    // DUMB POINTER ARITHMATIC
    gpd = (struct ext2_group_desc *) (sb + 1);
    gpd->bg_block_bitmap        = 2;
    gpd->bg_inode_bitmap        = 3;
    gpd->bg_inode_table         = 4;
    gpd->bg_free_blocks_count   = 44;
    gpd->bg_free_inodes_count   = 19;
    gpd->bg_used_dirs_count     = 1;

    // Set up the block bitmap
    uint8 *blBitmap;
    blBitmap = (uint8 *) (sb + 2);
    blBitmap[0] = 0x3F;      // super block
    int i;
    for (i = 6; i < sb->s_blocks_count; i++)
        blBitmap[i] = 0;
    // Set up the inode bitmap
    uint8 *iBitmap;
    iBitmap = (uint8 *) (sb + 3);
    iBitmap[0] = 0x1;     // .
    for (i = 1; i < sb->s_inodes_count; i++)
        iBitmap[i] = 0;

    // Set up the inode table
    struct ext2_inode *iTbl;
    iTbl = (struct ext2_inode *) (sb + 4);
    // Set up . inode
    iTbl->i_mode = EXT2_S_IFDIR;
    iTbl->i_size = sizeof(struct ext2_dir_entry_2);
    iTbl->i_links_count = 0;
    iTbl->i_blocks = 1;
    iTbl->i_flags = EXT2_NODUMP_FL;
    iTbl->i_block[0] = 5;

    // Set up . entry for the home directory
    struct ext2_dir_entry_2 *blk5;
    blk5 = (struct ext2_dir_entry_2 *) (sb + 5);
    blk5->inode = 1;
    blk5->next_dirent = 0;
    blk5->name_len = 1;
    blk5->filetype = 2;
    char homeName[255] = ".";
    memcpy(blk5->name, homeName, 255);

    _fs_ext2_init();

    touch1( xinu_fs, "./", "test" );
    char bufferL[9] = "Go long!";
    uint32 bytes_written;
    ext2_write_status stat = ext2_write_file_by_path( xinu_fs, "./test", bufferL,
                                                      &bytes_written, 0, 8 );
    touch1( xinu_fs, "./", "yo");
    stat = ext2_write_file_by_path( xinu_fs, "./yo", bufferL,
                                                      &bytes_written, 0, 8 );
    touch1( xinu_fs, "./", "whoah" );
    stat = ext2_write_file_by_path( xinu_fs, "./whoah", bufferL,
                                                    &bytes_written, 0, 8 );
    touch1( xinu_fs, "./", "iasjdf" );
    stat = ext2_write_file_by_path( xinu_fs, "./iasjdf", bufferL,
                                                    &bytes_written, 0, 8 );
    touch1( xinu_fs, "./", "f" );
    stat = ext2_write_file_by_path( xinu_fs, "./f", bufferL,
                                                    &bytes_written, 0, 8 );
    ls1( xinu_fs, "./" );
    printf("removing yo\n");
    rm1( xinu_fs, "./", "yo" );
    ls1( xinu_fs, "./" );
    printf("touching asdf\n");
    touch1( xinu_fs, "./", "asdf" );
    stat = ext2_write_file_by_path( xinu_fs, "./asdf", bufferL,
                                                    &bytes_written, 0, 8 );
    ls1( xinu_fs, "./" );
    printf("mking dir\n");
    mkdir1( xinu_fs, "./", "dir" );
    ls1( xinu_fs, "./" );
    printf("touching yo\n");
    touch1( xinu_fs, "./dir/", "yo" );
    ls1( xinu_fs, "./dir/");

    copy1( xinu_fs, "./", "whoah", "./", "hello" );
    ls1( xinu_fs, "./" );
    cat1( xinu_fs, "./", "hello" );

    copy1( xinu_fs, "./", "hello", "./dir/", "hello" );
    printf("HERE\n");
    ls1( xinu_fs, "./dir/" );
    cat1( xinu_fs, "./dir/", "hello" );

    printf("removeing\n");
    mv1( xinu_fs, "./dir/", "yo", "./", "a" );
    ls1( xinu_fs, "./" );
    cat1( xinu_fs, "./", "a" );

#if 0
    // Test the read/write functions
    printf("Testing hardcoded data\n");
    print_superblock( xinu_fs->sb );
    struct ext2_inode *i1 = ext2_get_inode(xinu_fs, 1);
    print_inode( i1, 1, xinu_fs );
    struct ext2_dir_entry_2 *home = ext2_get_first_dirent(xinu_fs, i1 );
    print_dirent( home );

    uint32 inode_num = ext2_inode_alloc( xinu_fs );
    struct ext2_inode *i2 = ext2_get_inode( xinu_fs, inode_num+1 );
    i2->i_mode = EXT2_S_IFREG;
    i2->i_size = 0;
    printf("Allocated new inode\n");
    print_inode( i2, inode_num+1, xinu_fs );

    struct ext2_dir_entry_2 *dirent = ext2_dirent_alloc( xinu_fs, i1 );
    dirent->inode = 2;
    dirent->next_dirent = 0;
    dirent->name_len = 4;
    dirent->filetype = EXT2_FT_REG_FILE;
    char testName[255] = "test";
    memcpy(dirent->name, testName, 255);
    printf("Allocated new dir_entry_2 test\n");
    print_dirent( dirent );

    char path[8] = "./test";
    char buffer[14] = "Writing! Yay!";
    char bufferL[9] = "Go long!";
    uint32 bytes_written;
    ext2_write_status stat = ext2_write_file_by_path( xinu_fs, path, buffer,
                                                      &bytes_written, 0, 13 );
    printf("bytes_written = %d stat = %d\n", bytes_written, stat);
    char buffer2[12*1024];
 //   stat = ext2_write_file_by_path( xinu_fs, path, buffer2,
  //                                                    &bytes_written, 13, (12*1024)-1 );
    printf("bytes_written = %d stat = %d\n", bytes_written, stat);
//    stat = ext2_write_file_by_path( xinu_fs, path, bufferL,
//                                                      &bytes_written, (12*1024)+12, 8 );
    printf("bytes_written = %d stat = %d\n", bytes_written, stat);
    int read = 0;
    char readBuf[30];
    read = ext2_read_dirent( xinu_fs, dirent, readBuf, 0, 29);
    printf("Read %d bytes readBuf = %s\n", read, readBuf);
//    read = ext2_read_dirent( xinu_fs, dirent, readBuf, (12*1024)+12, 10);
//    printf("Read %d bytes readBuf = %s\n", read, readBuf);
#endif
    return 0;
}
Exemplo n.º 4
0
int main(int argc, char **argv)
{

	int rc;
	char *endptr;
	char *dev_path;
	service_id_t service_id;
	ext2_filesystem_t filesystem;
	int arg_flags;
	uint32_t inode = 0;
	uint32_t inode_data = 0;
	
	arg_flags = 0;
	
	if (argc < 2) {
		printf(NAME ": Error, argument missing.\n");
		syntax_print();
		return 1;
	}
	
	/* Skip program name */
	--argc; ++argv;
	
	if (argc > 0 && str_cmp(*argv, "--no-check") == 0) {
		--argc; ++argv;
		arg_flags |= ARG_NO_CHECK;
	}
	
	if (argc > 0 && str_cmp(*argv, "--superblock") == 0) {
		--argc; ++argv;
		arg_flags |= ARG_SUPERBLOCK;
	}
	
	if (argc > 0 && str_cmp(*argv, "--block-groups") == 0) {
		--argc; ++argv;
		arg_flags |= ARG_BLOCK_GROUPS;
	}
	
	if (argc > 0 && str_cmp(*argv, "--inode") == 0) {
		--argc; ++argv;
		if (argc == 0) {
			printf(NAME ": Argument expected for --inode\n");
			return 2;
		}
		
		inode = strtol(*argv, &endptr, 10);
		if (*endptr != '\0') {
			printf(NAME ": Error, invalid argument for --inode.\n");
			syntax_print();
			return 1;
		}
		
		arg_flags |= ARG_INODE;
		--argc; ++argv;
		
		if (argc > 0 && str_cmp(*argv, "--blocks") == 0) {
			--argc; ++argv;
			arg_flags |= ARG_INODE_BLOCKS;
		}
		
		if (argc > 0 && str_cmp(*argv, "--data") == 0) {
			--argc; ++argv;
			if (argc == 0) {
				printf(NAME ": Argument expected for --data\n");
				return 2;
			}
			
			inode_data = strtol(*argv, &endptr, 10);
			if (*endptr != '\0') {
				printf(NAME ": Error, invalid argument for --data.\n");
				syntax_print();
				return 1;
			}
			
			arg_flags |= ARG_INODE_DATA;
			--argc; ++argv;
		}
		
		if (argc > 0 && str_cmp(*argv, "--list") == 0) {
			--argc; ++argv;
			arg_flags |= ARG_INODE_LIST;
		}
	}

	if (argc < 1) {
		printf(NAME ": Error, argument missing.\n");
		syntax_print();
		return 1;
	}
	else if (argc > 1) {
		printf(NAME ": Error, unexpected argument.\n");
		syntax_print();
		return 1;
	}
	assert(argc == 1);
	
	/* Display common things by default */
	if ((arg_flags & ARG_ALL) == 0) {
		arg_flags = ARG_COMMON;
	}

	dev_path = *argv;

	rc = loc_service_get_id(dev_path, &service_id, 0);
	if (rc != EOK) {
		printf(NAME ": Error resolving device `%s'.\n", dev_path);
		return 2;
	}

	rc = ext2_filesystem_init(&filesystem, service_id);
	if (rc != EOK)  {
		printf(NAME ": Error initializing libext2.\n");
		return 3;
	}
	
	rc = ext2_filesystem_check_sanity(&filesystem);
	if (rc != EOK) {
		printf(NAME ": Filesystem did not pass sanity check.\n");
		if (!(arg_flags & ARG_NO_CHECK)) {
			return 3;
		}
	}
	
	if (arg_flags & ARG_SUPERBLOCK) {
		print_superblock(filesystem.superblock);
	}
	
	if (arg_flags & ARG_BLOCK_GROUPS) {
		print_block_groups(&filesystem);
	}
	
	if (arg_flags & ARG_INODE) {
		print_inode_by_number(&filesystem, inode, arg_flags & ARG_INODE_DATA,
		    inode_data, arg_flags & ARG_INODE_LIST,
		    arg_flags & ARG_INODE_BLOCKS);
	}

	ext2_filesystem_fini(&filesystem);

	return 0;
}
Exemplo n.º 5
0
int main(int argc, char const *argv[]) {
	// Check for arguments
	if (argc < 8) {
		exit_error_f(
			"Usage:\n"
			"%s DEV BLOCK_SIZE JB_TRANSACTIONS HASH_TYPE SALT HMAC_TYPE SECRET lazy|nolazy\n"
			"%s MINT_DEV DATA_DEV BLOCK_SIZE JB_TRANSACTIONS HASH_TYPE SALT HMAC_TYPE SECRET lazy|nolazy\n",
			argv[0], argv[0]);
	}
	const char *dev, *dev2, *hash_type, *hmac_type, *salt_str, *secret_str;
	uint32_t block_size, journal_blocks;
	bool zero;

	if (!strcmp(argv[argc - 1], "lazy")) {
		zero = false;
	} else if (!strcmp(argv[argc - 1], "nolazy")) {
		zero = true;
	} else {
		exit_error_f("Unsupported optional argument: %s", argv[argc - 1]);
	}

	bool two_disks = (argc == 10);

	dev = argv[1];
	dev2 = argv[2];
	hash_type = argv[4 + two_disks];
	salt_str = argv[5 + two_disks];
	hmac_type = argv[6 + two_disks];
	secret_str = argv[7 + two_disks];

	// Open destination device
	int file, file2;
	if ((file = open(dev, O_RDWR)) < 0) {
		exit_error_f("Could not open: '%s' for writing, %s", dev, strerror(errno));
	}

	// Get size
	// TODO: size of file in 512 chunks?
	struct stat file_stats, file_stats2;
	if (fstat(file, &file_stats) != 0) {
		exit_error_f("Could not get file stats for: '%s', %s", dev, strerror(errno));
	}

	if (!(S_ISREG(file_stats.st_mode) || S_ISBLK(file_stats.st_mode))) {
		exit_error_f("File is neither a regular file nor block device");
	}

	if (two_disks) {
		if ((file2 = open(dev2, O_RDWR)) < 0) {
			exit_error_f("Could not open: '%s' for writing, %s", dev2, strerror(errno));
		}

		// Get size
		// TODO: size of file in 512 chunks?
		if (fstat(file2, &file_stats2) != 0) {
			exit_error_f("Could not get file stats for: '%s', %s", dev2, strerror(errno));
		}

		if (!(S_ISREG(file_stats2.st_mode) || S_ISBLK(file_stats2.st_mode))) {
			exit_error_f("File is neither a regular file nor block device");
		}
	}

	// Get block size
	if (sscanf(argv[2 + two_disks], "%u", &block_size) != 1) {
		exit_error_f("Invalid block size: '%s'", argv[2 + two_disks]);
	}
	if (block_size < 512) {
		exit_error_f("Invalid block size: '%u' < 512", block_size);
	}

	// Remainder check
	if (S_ISREG(file_stats.st_mode) && file_stats.st_size % block_size != 0) {
		warn("File is not a multiple of block_size: %d. %ju bytes left over",
			block_size, file_stats.st_size % block_size);
	}
	if (two_disks && S_ISREG(file_stats2.st_mode)
			&& file_stats2.st_size % block_size != 0) {
		warn("File is not a multiple of block_size: %d. %ju bytes left over",
			block_size, file_stats.st_size % block_size);
	}

	// Number of journal blocks
	if (sscanf(argv[3 + two_disks], "%u", &journal_blocks) != 1) {
		exit_error_f("Invalid journal blocks number: '%s'", argv[3 + two_disks]);
	}

	OpenSSL_add_all_digests();

	// Block hash algorithm
	EVP_MD_CTX *mdctx_hash = EVP_MD_CTX_create();
	const EVP_MD *md_hash;
	md_hash = EVP_get_digestbyname(hash_type);
	if (!md_hash) {
		exit_error_f("Unsupported hash type: %s", hash_type);
	}
	uint32_t hash_bytes = EVP_MD_size(md_hash);

	// Hmac algorithm
	const EVP_MD *md_hmac;
	md_hmac = EVP_get_digestbyname(hmac_type);
	if (!md_hmac) {
		exit_error_f("Unsupported hmac type: %s", hmac_type);
	}

	// Parse and check salt
	char salt[128];
	if (strlen(salt_str) % 2 != 0) {
		exit_error_f("Invalid hex salt: length not a multiple of 2");
	}
	if (strlen(salt_str) > 256) {
		exit_error_f("Salt is too long. %lu > %d", strlen(salt_str), 256);
	}
	if (hex_to_bytes(salt_str, strlen(salt_str), (char*)salt) != 0) {
		exit_error_f("Invalid hex salt: '%s'", salt_str);
	}

	// Parse and check secrets
	char secret[hash_bytes];
	if (strlen(secret_str) % 2 != 0) {
		exit_error_f("Invalid hex secret: length not a multiple of 2");
	}
	if (hex_to_bytes(secret_str, strlen(secret_str), (char*)secret) != 0) {
		exit_error_f("Invalid hex inner pad: '%s'", secret_str);
	}

	// Calculate data size, hash block size, journal size
	// TODO: uh...this is 64 bits...
	uint64_t data_blocks = 0;
	uint32_t hash_blocks = 0;
	uint32_t jb_blocks = 0;
	uint32_t pad_blocks = 0;
	uint32_t *blocks_per_level = malloc(sizeof(uint32_t) * DM_MINTEGRITY_MAX_LEVELS);
	uint32_t levels = 0;
	uint64_t blocks, blocks2;

	if (S_ISREG(file_stats.st_mode)) {
		blocks = file_stats.st_size / block_size;
	} else if (S_ISBLK(file_stats.st_mode)) {
		if(ioctl(file, BLKGETSIZE64, &blocks) != 0){
			exit_error_f("ioctl for block size failed: %s", strerror(errno));
		}
		blocks = blocks / block_size;
	}

	if (two_disks) {
		if (S_ISREG(file_stats2.st_mode)) {
			blocks2 = file_stats2.st_size / block_size;
		} else if (S_ISBLK(file_stats2.st_mode)) {
			if(ioctl(file2, BLKGETSIZE64, &blocks2) != 0){
				exit_error_f("ioctl for block size failed: %s", strerror(errno));
			}
			blocks2 = blocks2 / block_size;
		}
	}

	// Fanout
	uint8_t fls, pls = 0;
	uint32_t fanout = block_size / hash_bytes;
	while (fanout > 0) {
		if ((fanout & 1) == 1) {
			fls = pls;
		}
		pls ++;
		fanout = fanout >> 1;
	}
	fanout = 1 << fls;

	// Use up entire block device
	if (!two_disks) {
		compute_block_numbers(blocks, block_size, fanout, journal_blocks, &data_blocks,
			&hash_blocks, &jb_blocks, &pad_blocks, &levels, blocks_per_level, hash_bytes);
	} else {
		jb_blocks = journal_blocks;
		data_blocks = blocks2;
		compute_hash_blocks(blocks2, fanout, &levels, &hash_blocks, blocks_per_level);
		if (hash_blocks + journal_blocks > blocks2) {
			exit_error_f("Need: %u hash + journal blocks, but %s only has %ju",
				hash_blocks + journal_blocks, dev, blocks);
		}
	}
	
	// Result info
	info("Blocks: %ju = Superblock: 1, Data: %ju, Hash: %u, JB: %u, Pad: %u, Levels: %u",
			blocks, data_blocks, hash_blocks, jb_blocks, pad_blocks, levels);

	// Calculate each hash block level
	char **hash_levels = (char**)malloc(sizeof(char*) * levels);
	char hash_output[EVP_MAX_MD_SIZE];
	uint32_t hash_length;
	char *zero_block = (char*)malloc(block_size);
	char *temp_block = (char*)malloc(block_size);
	bzero(zero_block, block_size);

	char buf[128];
	// Data hash
	hash(md_hash, mdctx_hash, zero_block, block_size, salt,
		strlen(salt_str) / 2, hash_output, &hash_length);

	// Now loop through each level
	for (uint32_t i = 0; i < levels; i++) {
		hash_levels[i] = (char*)malloc(block_size);
		// Fill block with hashes - padding is zeros
		bzero(hash_levels[i], block_size);
		for (uint32_t f = 0; f < fanout; f++) {
			for (int b = 0; b < hash_bytes; b++) {
				hash_levels[i][f * (block_size / (1 << fls)) + b] = hash_output[b];
			}
		}
		// Compute hash of this level for next iteration/root
		hash(md_hash, mdctx_hash, hash_levels[i], block_size, salt,
			strlen(salt_str) / 2, hash_output, &hash_length);
	}

	// Write out hash superblock
	struct mint_superblock *msb = malloc(sizeof(struct mint_superblock));
	// Zero out everything
	bzero(msb, sizeof(struct mint_superblock));
	// Magic
	msb->magic = 0x796c694c;
	// Version
	msb->version = 1;
	// Make a new uuid!
	uuid_t uuid;
	uuid_generate(uuid);
	// TODO: is there a better way of doing this?
	memcpy(&msb->uuid, &uuid, 16);
	// Copy hash algorithm name
	strcpy(msb->hash_algorithm, hash_type);
	// Copy hmac algorithm name
	strcpy(msb->hmac_algorithm, hmac_type);
	// Block size!
	msb->block_size = block_size;
	// Set block numbers
	msb->data_blocks = data_blocks;
	msb->hash_blocks = hash_blocks;
	msb->jb_blocks = jb_blocks;
	// Set salt size
	msb->salt_size = strlen(salt_str) / 2;
	// Copy salt
	memcpy(msb->salt, salt, msb->salt_size);
	// Set root hash
	memcpy(msb->root, hash_output, hash_length);
	// Write it out!
	if (write(file, msb, sizeof(struct mint_superblock)) < 0) {
		exit_error_f("Failed to write MSB: %s", strerror(errno));
	}
	if (write(file, zero_block, block_size - 512) < 0) {
		exit_error_f("Failed to write MSB pad: %s", strerror(errno));
	}

	// Big block buffer
	uint32_t multiple = 1024;
	char *big_block = (char*)malloc(block_size * multiple);
	bzero(big_block, block_size * multiple);

	// Write out hash block levels
	uint8_t p = 0;
	info("Writing hash blocks...");
	uint32_t h_written = 1;
	for (int i = levels - 1; i >= 0; i--) {
		// Copy into big buffer
		for (uint32_t m = 0; m < multiple; m++) {
			memcpy(big_block + m * block_size, hash_levels[i], block_size);
		}
		// Write out big buffer
		for (uint32_t j = 0; j < blocks_per_level[i] / multiple; j++) {
			h_written += multiple;
			p = progress(h_written, hash_blocks, 79, p);
			if(write(file, big_block, block_size * multiple) < 0){
				exit_error_f("Failed to write hash block: %u, %s",
					h_written - 1, strerror(errno));
			}
		}
		for (uint32_t j = 0; j < blocks_per_level[i] % multiple; j++) {
			p = progress(h_written++, hash_blocks, 79, p);
			if(write(file, hash_levels[i], block_size) < 0){
				exit_error_f("Failed to write hash block: %u, %s",
					h_written - 1, strerror(errno));
			}
		}
	}
	fprintf(stderr, "\n");

	// Initialize journal
	struct mint_journal_superblock *mjsb = (struct mint_journal_superblock*)
		malloc(sizeof(struct mint_journal_superblock));
	bzero(mjsb, sizeof(struct mint_journal_superblock));
	// Magic
	mjsb->header.magic = MJ_MAGIC;
	// Superblock
	mjsb->header.type = TYPE_MJSB;
	// Number of blocks
	mjsb->blocks = jb_blocks;
	// Head, tail, and fill are 0
	mjsb->head = 0;
	mjsb->tail = 0;
	mjsb->fill = 0;
	mjsb->sequence = 0;
	// Clean
	mjsb->state = 0;

	info("Writing journal...");
	if (write(file, mjsb, sizeof(struct mint_journal_superblock)) < 0) {
		exit_error_f("Failed to write journal superblock:, %s",
		strerror(errno));
	}
	if (write(file, zero_block, block_size - 512) < 0) {
		exit_error_f("Failed to write journal superblock pad: %s", strerror(errno));
	}

	struct mint_journal_header *mjh = (struct mint_journal_header*)
		malloc(sizeof(struct mint_journal_header));
	bzero(mjh, sizeof(struct mint_journal_header));
	// Magic
	mjh->magic = MJ_MAGIC;
	// Nothing block
	mjh->type = TYPE_MJNB;

	// Copy headers into start of every block
	bzero(big_block, block_size * multiple);
	for (uint64_t i = 0; i < multiple; i++) {
		memcpy(big_block + i * block_size, mjh, sizeof(struct mint_journal_header));
	}
	p = 0;
	for (uint64_t i = 0; i < (jb_blocks - 1) / multiple; i++) {
		if(write(file, big_block, block_size * multiple) < 0){
			exit_error_f("Failed to write journal block: %ju, %s", i,
				strerror(errno));
		}
		p = progress(i * multiple + 1, jb_blocks, 79, p);
	}
	for (uint64_t i = 0; i < (jb_blocks - 1) % multiple; i++) {
		if(write(file, big_block, block_size) < 0){
			exit_error_f("Failed to write journal block: %ju, %s", i,
				strerror(errno));
		}
		p = progress(jb_blocks - ((jb_blocks - 1) % multiple) + i + 2, jb_blocks, 79, p);
	}
	fprintf(stderr, "\n");

	// Zero out data
	if (zero) {
		int f = two_disks ? file2 : file;
		bzero(big_block, block_size * multiple);
		info("Writing data blocks...");
		p = 0;
		for (uint64_t i = 0; i < data_blocks / multiple; i++) {
			if(write(f, big_block, block_size * multiple) < 0){
				exit_error_f("Failed to write data block: %ju, %s", i,
					strerror(errno));
			}
			p = progress(i * multiple, data_blocks, 79, p);
		}
		for (uint64_t i = 0; i < data_blocks % multiple; i++) {
			if(write(f, zero_block, block_size) < 0){
				exit_error_f("Failed to write data block: %ju, %s", i,
					strerror(errno));
			}
			p = progress(data_blocks - (data_blocks % multiple) + i + 1,
				data_blocks, 79, p);
		}
		fprintf(stderr, "\n");
	} else {
		info("Skipping disk zeroing...");
	}

	close(file);
	if (two_disks) {
		close(file2);
	}

	print_superblock(msb);
	bytes_to_hex(msb->root, hash_bytes, buf);
	printf("dmsetup create meow --table \"%u %ju mintegrity %s%s%s %u %u %u %ju "
		"%s %s %s %s %s%s\"\n",
		0,             // Start is 0
		data_blocks * (block_size / 512),   // Size of device given to device mapper
		// Mintegrity options
		dev,           // String of block device
		two_disks ? " " : "", two_disks ? dev2 : "",
		block_size,    // Block size
		hash_blocks,   // Number of hash blocks
		jb_blocks,     // Number of journaling blocks
		data_blocks,   // Number of data blocks
		hash_type,     // Hash type
		buf,           // Root digest to verity
		salt_str,      // Salt
		hmac_type,     // Hash type for hmac
		secret_str,    // Hmac secret
		zero ? "" : " lazy"
		);

	free(mjh);
	free(mjsb);
	free(msb);
	free(blocks_per_level);
	free(zero_block);
	free(big_block);
	free(temp_block);
	for (int i = 0; i < levels; i++) {
		free(hash_levels[i]);
	}
	free(hash_levels);
	EVP_MD_CTX_destroy(mdctx_hash);
	return 0;
}