// SPDX-License-Identifier: GPL-2.0-or-later

#include "common.h"
#define BUFLEN 128

int
read_uint_from_file(char *dirname, char *filename, unsigned int *i)
{
	FILE *f;
	char fname[BUFLEN];
	int ret = -1;

	snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);

	f = fopen(fname, "r");
	if (!f)
		return ret;

	if (fscanf(f, "%u", i) == 1)
		ret = 0;

	fclose(f);
	return ret;
}

char
*read_string_from_file(const char *dirname, const char *filename, char *buf, size_t bufsz)
{
	FILE *f;
	char fname[BUFLEN];
	int i;

	snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);

	f = fopen(fname, "r");
	if (!f)
		return NULL;

	if (fgets(buf, bufsz, f) == NULL) {
		fclose(f);
		return NULL;
	}

	fclose(f);

	/* make sure the string is \0 terminated */
	buf[bufsz - 1] = '\0';

	/* remove trailing whitespace */
	i = strlen(buf) - 1;
	while (i > 0 && buf[i] <= ' ')
		buf[i--] = '\0';

	return buf;
}

int block_file_identify(FILE *f, uint64_t offset)
{
	uint32_t magic = 0;
	size_t n;

	if (fseeko(f, offset, SEEK_SET) < 0)
		return -1;

	n = fread(&magic, sizeof(magic), 1, f);
	if (magic == cpu_to_le32(0x88b1f))
		return FS_TARGZ;

	if (magic == cpu_to_be32(0xdeadc0de))
		return FS_DEADCODE;

	if (fseeko(f, offset + 0x400, SEEK_SET) < 0)
		return -1;

	n = fread(&magic, sizeof(magic), 1, f);
	if (n != 1)
		return -1;

	if (magic == cpu_to_le32(0xF2F52010))
		return FS_F2FS;

	magic = 0;
	if (fseeko(f, offset + 0x438, SEEK_SET) < 0)
		return -1;

	n = fread(&magic, sizeof(magic), 1, f);
	if (n != 1)
		return -1;

	if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
		return FS_EXT4;

	return FS_NONE;
}

static bool use_f2fs(struct volume *v, uint64_t offset, const char *bdev)
{
	uint64_t size = 0;
	bool ret = false;
	int fd;

	fd = open(bdev, O_RDONLY);
	if (fd < 0)
		return false;

	if (ioctl(fd, BLKGETSIZE64, &size) == 0)
		ret = size - offset > F2FS_MINSIZE;

	close(fd);

	return ret;
}

int block_volume_format(struct volume *v, uint64_t offset, const char *bdev)
{
	int ret = 0;
	char str[128];
	unsigned int skip_blocks = 0;
	int fd;
	__u32 deadc0de;
	size_t sz;

	switch (volume_identify(v)) {
	case FS_DEADCODE:
	case FS_NONE:
		/* skip padding */
		fd = open(v->blk, O_RDONLY);
		if (fd < 0) {
			ret = EIO;
			break;
		}
		do {
			if (lseek(fd, (skip_blocks + 1) * 512, SEEK_SET) == (off_t) -1) {
				ret = EIO;
				break;
			}
			sz = read(fd, &deadc0de, sizeof(deadc0de));
			if (sz != sizeof(deadc0de)) {
				ret = EIO;
				break;
			}
		} while(++skip_blocks <= 512 &&
			(deadc0de == cpu_to_be32(0xdeadc0de) || deadc0de == 0xffffffff));

		close(fd);
		if (ret)
			break;

		/* only try extracting in case gzip header is present */
		if (deadc0de != cpu_to_le32(0x88b1f))
			goto do_format;

		/* fall-through */
	case FS_TARGZ:
		snprintf(str, sizeof(str),
			 "dd if=%s bs=512 skip=%u 2>/dev/null | gzip -cd > /tmp/sysupgrade.tar 2>/dev/null",
			 v->blk, skip_blocks);
		ret = system(str);
		if (ret < 0) {
			ULOG_ERR("failed extracting config backup from %s\n", v->blk);
			break;
		}
do_format:
		ULOG_INFO("overlay filesystem in %s has not been formatted yet\n", v->blk);
		if (use_f2fs(v, offset, bdev))
			snprintf(str, sizeof(str), "mkfs.f2fs -q -f -l rootfs_data %s", v->blk);
		else
			snprintf(str, sizeof(str), "mkfs.ext4 -q -F -L rootfs_data %s", v->blk);

		ret = system(str);
		break;
	default:
		break;
	}

	return ret;
}
