Files
Ernesto A. Fernández fab9b2109e mkapfs: actually use the fatal() function
The mkapfs has a fatal() function, but I keep forgetting about it and
calling fprintf() and exit() directly. Tidy this up.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-11-05 18:39:12 -03:00

249 lines
5.3 KiB
C

/*
* Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#include <linux/fs.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <apfs/raw.h>
#include "mkapfs.h"
#include "super.h"
#include "version.h"
int fd_main = -1;
int fd_tier2 = -1;
struct parameters *param;
static char *progname;
/**
* usage - Print usage information and exit
*/
static void usage(void)
{
fprintf(stderr,
"usage: %s [-L label] [-U UUID] [-u UUID] [-F tier2] [-B tier2-blocks] [-sv] "
"device [blocks]\n",
progname);
exit(EXIT_FAILURE);
}
/**
* version - Print version information and exit
*/
static void version(void)
{
if (*GIT_COMMIT) {
printf("mkapfs %s\n", GIT_COMMIT);
exit(EXIT_SUCCESS);
} else {
printf("mkapfs - unknown git commit id\n");
exit(EXIT_FAILURE);
}
}
/**
* system_error - Print a system error message and exit
*/
__attribute__((noreturn)) void system_error(void)
{
perror(progname);
exit(EXIT_FAILURE);
}
/**
* fatal - Print a message and exit with an error code
* @message: text to print
*/
__attribute__((noreturn)) void fatal(const char *message)
{
fprintf(stderr, "%s: %s\n", progname, message);
exit(EXIT_FAILURE);
}
/**
* get_device_size - Get the block count for a given device or image
* @device_fd: file descriptor for the device
* @blocksize: the filesystem blocksize
*/
static u64 get_device_size(int device_fd, unsigned int blocksize)
{
struct stat buf;
u64 size;
if (fstat(device_fd, &buf))
system_error();
if ((buf.st_mode & S_IFMT) == S_IFREG)
return buf.st_size / blocksize;
if (ioctl(device_fd, BLKGETSIZE64, &size))
system_error();
return size / blocksize;
}
static u64 get_main_device_size(unsigned int blocksize)
{
return get_device_size(fd_main, blocksize);
}
static u64 get_tier2_device_size(unsigned int blocksize)
{
if (fd_tier2 == -1)
return 0;
return get_device_size(fd_tier2, blocksize);
}
/**
* get_random_uuid - Get a random UUID string in standard format
*
* Returns a pointer to the string.
*/
static char *get_random_uuid(void)
{
char *uuid;
ssize_t ret;
/* Length of a null-terminated UUID standard format string */
uuid = malloc(37);
if (!uuid)
system_error();
/* Linux provides randomly generated UUIDs at /proc */
do {
int uuid_fd;
uuid_fd = open("/proc/sys/kernel/random/uuid", O_RDONLY);
if (uuid_fd == -1)
system_error();
ret = read(uuid_fd, uuid, 36);
if (ret == -1)
system_error();
close(uuid_fd);
} while (ret != 36);
/* Put a null-termination, just in case */
uuid[36] = 0;
return uuid;
}
/**
* complete_parameters - Set all uninitialized parameters to their defaults
*
* Also runs any needed checks on the parameters provided by the user.
*/
static void complete_parameters(void)
{
u64 main_blkcnt_limit, tier2_blkcnt_limit;
if (!param->blocksize)
param->blocksize = APFS_NX_DEFAULT_BLOCK_SIZE;
main_blkcnt_limit = get_main_device_size(param->blocksize);
tier2_blkcnt_limit = get_tier2_device_size(param->blocksize);
if (!param->main_blkcnt)
param->main_blkcnt = main_blkcnt_limit;
if (!param->tier2_blkcnt)
param->tier2_blkcnt = tier2_blkcnt_limit;
if (param->main_blkcnt > main_blkcnt_limit)
fatal("main device is not big enough");
if (param->tier2_blkcnt > tier2_blkcnt_limit)
fatal("tier 2 device is not big enough");
param->block_count = param->main_blkcnt + param->tier2_blkcnt;
if (param->main_blkcnt * param->blocksize < 512 * 1024)
fatal("such tiny containers are not supported");
if (param->tier2_blkcnt && param->tier2_blkcnt * param->blocksize < 512 * 1024) {
/* TODO: is this really a problem for tier 2? */
fatal("tier 2 is too small");
}
/* Every volume must have a label; use the same default as Apple */
if (!param->label || !*param->label)
param->label = "untitled";
/* Make sure the volume label fits, along with its null termination */
if (strlen(param->label) + 1 > APFS_VOLNAME_LEN)
fatal("volume label is too long");
if (!param->main_uuid)
param->main_uuid = get_random_uuid();
if (!param->vol_uuid)
param->vol_uuid = get_random_uuid();
if (fd_tier2 != -1)
param->fusion_uuid = get_random_uuid();
}
int main(int argc, char *argv[])
{
char *filename;
progname = argv[0];
param = calloc(1, sizeof(*param));
if (!param)
system_error();
while (1) {
int opt = getopt(argc, argv, "L:U:u:szvF:B:");
if (opt == -1)
break;
switch (opt) {
case 'L':
param->label = optarg;
break;
case 'U':
param->main_uuid = optarg;
break;
case 'u':
param->vol_uuid = optarg;
break;
case 's':
param->case_sensitive = true;
break;
case 'z':
param->norm_sensitive = true;
break;
case 'v':
version();
case 'F':
fd_tier2 = open(optarg, O_RDWR);
if (fd_tier2 == -1)
system_error();
break;
case 'B':
/* TODO: reject malformed numbers */
param->tier2_blkcnt = atoll(optarg);
break;
default:
usage();
}
}
if (optind == argc - 2) {
filename = argv[optind];
/* TODO: reject malformed numbers? */
param->main_blkcnt = atoll(argv[optind + 1]);
} else if (optind == argc - 1) {
filename = argv[optind];
} else {
usage();
}
fd_main = open(filename, O_RDWR);
if (fd_main == -1)
system_error();
complete_parameters();
make_container();
return 0;
}