[PATCH v3 5/7] mkimage: add fatfs image type for FAT partition images

Aswin Murugan aswin.murugan at oss.qualcomm.com
Fri Apr 17 14:09:49 CEST 2026


Add a new mkimage image type 'fatfs' that creates a raw FAT
filesystem partition image from a directory of files.

The image handler (fatimage.c) uses mkfs.vfat (dosfstools)
to format the image and mcopy (mtools) to populate it. The
image size is calculated automatically from the input
directory size plus FAT metadata overhead.

New long options added to mkimage:
  --fat-extra-space <KB>  extra padding (default: 512 KB)
  --fat-type <0|12|16|32> FAT type (0 = auto-detect)
  --fat-volume-id <label> volume label (default: BOOT)
  --fat-mkfs-opts <opts>  extra mkfs.vfat options

Usage:
  mkimage -T fatfs -d <input-dir> \
      --fat-extra-space 512 \
      --fat-volume-id "MYBOOT" \
      --fat-mkfs-opts "-S 512 -a" \
      output.img

Signed-off-by: Aswin Murugan <aswin.murugan at oss.qualcomm.com>
---
 tools/Makefile   |   1 +
 tools/fatimage.c | 495 +++++++++++++++++++++++++++++++++++++++++++++++
 tools/fatimage.h |  25 +++
 tools/mkimage.c  |  21 ++
 tools/mkimage.h  |  15 ++
 5 files changed, 557 insertions(+)
 create mode 100644 tools/fatimage.c
 create mode 100644 tools/fatimage.h

diff --git a/tools/Makefile b/tools/Makefile
index 1a5f425ecda..2f6c9363209 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -112,6 +112,7 @@ ROCKCHIP_OBS = generated/lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
 dumpimage-mkimage-objs := aisimage.o \
 			amlimage.o \
 			atmelimage.o \
+			fatimage.o \
 			$(FIT_OBJS-y) \
 			$(FIT_SIG_OBJS-y) \
 			$(FIT_CIPHER_OBJS-y) \
diff --git a/tools/fatimage.c b/tools/fatimage.c
new file mode 100644
index 00000000000..6a199c70f5b
--- /dev/null
+++ b/tools/fatimage.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FAT Filesystem Image support for mkimage
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries
+ *
+ * This tool creates FAT filesystem images from a directory of files.
+ * It uses system tools (mkfs.vfat and mcopy from mtools) to create
+ * the filesystem image.
+ */
+
+#include "imagetool.h"
+#include "mkimage.h"
+#include "fatimage.h"
+#include <image.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+
+static struct fatimage_params fatparams = {
+	.input_dir = NULL,
+	.extra_space = 512,
+	.fat_type = 0,
+	.volume_id = "BOOT",
+	.mkfs_opts = "-S 512",
+};
+
+/* Cached image size to avoid recalculation between vrec_header and set_header */
+static int cached_image_size_kb = -1;
+
+/**
+ * get_directory_size_kb() - Return the total disk usage of a directory in KB
+ * @path: Path to the directory
+ *
+ * Runs 'du -sk' to measure the on-disk size of all files under @path.
+ *
+ * Return: size in KB on success, -1 on error
+ */
+static int get_directory_size_kb(const char *path)
+{
+	char cmd[1024];
+	FILE *fp;
+	int size_kb = 0;
+
+	snprintf(cmd, sizeof(cmd), "du -sk \"%s\" 2>/dev/null | cut -f1", path);
+
+	fp = popen(cmd, "r");
+	if (!fp) {
+		fprintf(stderr, "Error: Failed to execute du command\n");
+		return -1;
+	}
+
+	if (fscanf(fp, "%d", &size_kb) != 1) {
+		fprintf(stderr, "Error: Failed to read directory size\n");
+		pclose(fp);
+		return -1;
+	}
+
+	pclose(fp);
+	return size_kb;
+}
+
+/**
+ * count_entries() - Recursively count files and subdirectories under a path
+ * @path: Directory to scan
+ * @files: Incremented for each regular file found
+ * @dirs: Incremented for each subdirectory found
+ *
+ * Used to estimate FAT directory entry overhead when sizing the image.
+ *
+ * Return: 0 on success, -1 if the directory cannot be opened
+ */
+static int count_entries(const char *path, int *files, int *dirs)
+{
+	DIR *dir;
+	struct dirent *entry;
+	struct stat st;
+	char full_path[1024];
+
+	dir = opendir(path);
+	if (!dir) {
+		fprintf(stderr, "Error: Cannot open directory %s: %s\n",
+			path, strerror(errno));
+		return -1;
+	}
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0 ||
+		    strcmp(entry->d_name, "..") == 0)
+			continue;
+
+		snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
+
+		if (stat(full_path, &st) < 0) {
+			fprintf(stderr, "Warning: Cannot stat %s: %s\n",
+				full_path, strerror(errno));
+			continue;
+		}
+
+		if (S_ISDIR(st.st_mode)) {
+			(*dirs)++;
+			count_entries(full_path, files, dirs);
+		} else {
+			(*files)++;
+		}
+	}
+
+	closedir(dir);
+	return 0;
+}
+
+/**
+ * calculate_image_size() - Compute the required FAT image size in KB
+ * @input_dir: Directory whose contents will be packed into the image
+ * @extra_space: Additional padding in KB to add beyond the data size
+ * @fat_type: Requested FAT type (0=auto, 12, 16, 32)
+ *
+ * Calculates the minimum image size needed to hold all files in @input_dir
+ * plus FAT metadata overhead (directory entries, two FAT copies) and
+ * @extra_space KB of padding. The result is cached in cached_image_size_kb
+ * so that vrec_header and set_header agree on the same value.
+ *
+ * Return: image size in KB on success, -1 on error
+ */
+static int calculate_image_size(const char *input_dir, int extra_space,
+				 int fat_type)
+{
+	int data_kb;
+	int sectors;
+	int files = 0, dirs = 0;
+	int dir_bytes, fat_bytes;
+	int dir_sectors, fat_sectors;
+	int fat_entry_size;
+	int total_sectors;
+	int blocks;
+
+	if (cached_image_size_kb > 0)
+		return cached_image_size_kb;
+
+	data_kb = get_directory_size_kb(input_dir);
+	if (data_kb < 0)
+		return -1;
+
+	sectors = data_kb * 2;
+
+	if (count_entries(input_dir, &files, &dirs) < 0)
+		return -1;
+
+	dir_bytes = (files + dirs) * DIR_ENTRY_SIZE;
+	dir_bytes += dirs * DIR_ENTRY_SIZE;
+
+	if (fat_type == 32) {
+		fat_entry_size = FAT_ENTRY_SIZE_FAT32;
+		if ((sectors / 2 + extra_space) < 32 * 1024) {
+			fprintf(stderr, "Warning: Image size %d KB may be too small for FAT32\n",
+				(sectors / 2 + extra_space));
+			fprintf(stderr, "         FAT32 works best with images >= 32 MB\n");
+			fprintf(stderr, "         Consider using auto-detect or FAT16 instead\n");
+		}
+	} else if (fat_type == 0) {
+		if ((sectors / 2 + extra_space) > 512 * 1024)
+			fat_entry_size = FAT_ENTRY_SIZE_FAT32;
+		else
+			fat_entry_size = FAT_ENTRY_SIZE_FAT16;
+	} else if (fat_type == 16) {
+		fat_entry_size = FAT_ENTRY_SIZE_FAT16;
+	} else if (fat_type == 12) {
+		fat_entry_size = FAT_ENTRY_SIZE_FAT12;
+	} else {
+		fat_entry_size = FAT_ENTRY_SIZE_FAT16;
+	}
+
+	fat_bytes = sectors * fat_entry_size;
+	fat_bytes += dirs * fat_entry_size;
+
+	dir_sectors = (dir_bytes + SECTOR_SIZE - 1) / SECTOR_SIZE;
+	fat_sectors = ((fat_bytes + SECTOR_SIZE - 1) / SECTOR_SIZE) * 2;
+
+	total_sectors = sectors + dir_sectors + fat_sectors;
+	blocks = (total_sectors / 2) + extra_space;
+
+	printf("FAT image size calculation:\n");
+	printf("  Data size: %d KB (%d sectors)\n", data_kb, sectors);
+	printf("  Files: %d, Directories: %d\n", files, dirs);
+	printf("  Directory overhead: %d sectors\n", dir_sectors);
+	printf("  FAT overhead: %d sectors (2 FATs)\n", fat_sectors);
+	printf("  Extra padding: %d KB\n", extra_space);
+	printf("  Total image size: %d KB\n", blocks);
+
+	cached_image_size_kb = blocks;
+
+	return blocks;
+}
+
+/**
+ * create_fat_image() - Format a FAT image file and populate it with files
+ * @input_dir: Source directory; all contents are copied into the image
+ * @output_file: Path of the FAT image file to create
+ * @blocks: Image size in KB (passed to mkfs.vfat as the block count)
+ * @fat_type: FAT type to use (0=auto, 12, 16, 32)
+ * @volume_id: FAT volume label string
+ * @mkfs_opts: Additional options forwarded to mkfs.vfat
+ *
+ * Calls mkfs.vfat to format @output_file as a FAT filesystem, then uses
+ * mcopy to copy all files from @input_dir into the image root.
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int create_fat_image(const char *input_dir, const char *output_file,
+			    int blocks, int fat_type, const char *volume_id,
+			    const char *mkfs_opts)
+{
+	char cmd[2048];
+	int ret;
+
+	unlink(output_file);
+
+	if (fat_type > 0) {
+		snprintf(cmd, sizeof(cmd),
+			 "mkfs.vfat -F %d %s -n \"%s\" -C \"%s\" %d",
+			 fat_type, mkfs_opts ? mkfs_opts : "",
+			 volume_id, output_file, blocks);
+	} else {
+		if (blocks > 512 * 1024) {
+			snprintf(cmd, sizeof(cmd),
+				 "mkfs.vfat -F 32 %s -n \"%s\" -C \"%s\" %d",
+				 mkfs_opts ? mkfs_opts : "",
+				 volume_id, output_file, blocks);
+		} else {
+			snprintf(cmd, sizeof(cmd),
+				 "mkfs.vfat %s -n \"%s\" -C \"%s\" %d",
+				 mkfs_opts ? mkfs_opts : "",
+				 volume_id, output_file, blocks);
+		}
+	}
+
+	printf("Creating FAT filesystem: %s\n", cmd);
+	ret = system(cmd);
+	if (ret != 0) {
+		fprintf(stderr, "Error: mkfs.vfat failed with code %d\n", ret);
+		fprintf(stderr, "Make sure mkfs.vfat (dosfstools) is installed\n");
+		return -1;
+	}
+
+	snprintf(cmd, sizeof(cmd),
+		 "MTOOLS_SKIP_CHECK=1 mcopy -i \"%s\" -s \"%s\"/* ::/",
+		 output_file, input_dir);
+
+	printf("Copying files: %s\n", cmd);
+	ret = system(cmd);
+	if (ret != 0) {
+		fprintf(stderr, "Error: mcopy failed with code %d\n", ret);
+		fprintf(stderr, "Make sure mcopy (mtools) is installed\n");
+		return -1;
+	}
+
+	printf("FAT image created successfully: %s\n", output_file);
+	return 0;
+}
+
+/**
+ * fatimage_check_params() - Validate mkimage parameters for the fatfs type
+ * @params: mkimage tool parameters (unused; input dir is in fatparams)
+ *
+ * Verifies that an input directory has been specified via fatimage_set_dir()
+ * and that it exists and is a directory.
+ *
+ * Return: 0 if parameters are valid, -1 on error
+ */
+static int fatimage_check_params(struct image_tool_params *params)
+{
+	if (!fatparams.input_dir) {
+		fprintf(stderr, "Error: Input directory not specified\n");
+		fprintf(stderr, "Use -d <directory> to specify input directory\n");
+		return -1;
+	}
+
+	struct stat st;
+	if (stat(fatparams.input_dir, &st) < 0) {
+		fprintf(stderr, "Error: Input directory '%s' does not exist: %s\n",
+			fatparams.input_dir, strerror(errno));
+		return -1;
+	}
+
+	if (!S_ISDIR(st.st_mode)) {
+		fprintf(stderr, "Error: '%s' is not a directory\n",
+			fatparams.input_dir);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * fatimage_check_image_type() - Report whether this handler owns a given type
+ * @type: IH_TYPE_* value to check
+ *
+ * Return: EXIT_SUCCESS if @type == IH_TYPE_FATFS, EXIT_FAILURE otherwise
+ */
+static int fatimage_check_image_type(uint8_t type)
+{
+	if (type == IH_TYPE_FATFS)
+		return EXIT_SUCCESS;
+	return EXIT_FAILURE;
+}
+
+/**
+ * fatimage_verify_header() - Verify that a buffer contains a valid FAT image
+ * @ptr: Pointer to the start of the image data
+ * @image_size: Total size of the image in bytes
+ * @params: mkimage tool parameters (unused)
+ *
+ * Checks for the FAT boot sector signature (0x55AA) at bytes 510-511.
+ *
+ * Return: 0 if the signature is present, -1 otherwise
+ */
+static int fatimage_verify_header(unsigned char *ptr, int image_size,
+				  struct image_tool_params *params)
+{
+	if (image_size < 512)
+		return -1;
+
+	if (ptr[510] != 0x55 || ptr[511] != 0xAA) {
+		fprintf(stderr, "Error: Not a valid FAT filesystem image\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * fatimage_print_header() - Print human-readable FAT image information
+ * @ptr: Pointer to the start of the image data
+ * @params: mkimage tool parameters (unused)
+ *
+ * Prints the boot signature, detected FAT type (FAT32 vs FAT12/16),
+ * and volume label read from the BPB.
+ */
+static void fatimage_print_header(const void *ptr,
+				  struct image_tool_params *params)
+{
+	const unsigned char *buf = ptr;
+
+	printf("FAT Filesystem Image\n");
+	printf("  Boot signature: 0x%02X%02X\n", buf[511], buf[510]);
+
+	if (buf[82] == 'F' && buf[83] == 'A' && buf[84] == 'T' &&
+	    buf[85] == '3' && buf[86] == '2') {
+		printf("  Filesystem: FAT32\n");
+		printf("  Volume label: %.11s\n", &buf[71]);
+	} else {
+		printf("  Filesystem: FAT12/FAT16\n");
+		printf("  Volume label: %.11s\n", &buf[43]);
+	}
+}
+
+/**
+ * fatimage_vrec_header() - Allocate the output buffer for the FAT image
+ * @params: mkimage tool parameters
+ * @tparams: image type parameters; hdr and header_size are set on success
+ *
+ * Called before set_header to pre-allocate a buffer large enough for the
+ * complete FAT image. The size is calculated from the input directory and
+ * cached for reuse by fatimage_set_header().
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int fatimage_vrec_header(struct image_tool_params *params,
+				struct image_type_params *tparams)
+{
+	int blocks;
+	int image_size_bytes;
+
+	blocks = calculate_image_size(fatparams.input_dir,
+				      fatparams.extra_space,
+				      fatparams.fat_type);
+	if (blocks < 0) {
+		fprintf(stderr, "Error: Failed to calculate image size\n");
+		return -1;
+	}
+
+	image_size_bytes = blocks * 1024;
+
+	tparams->hdr = malloc(image_size_bytes);
+	if (!tparams->hdr) {
+		fprintf(stderr, "Error: Failed to allocate %d bytes for FAT image\n",
+			image_size_bytes);
+		return -1;
+	}
+
+	tparams->header_size = image_size_bytes;
+
+	return 0;
+}
+
+/**
+ * fatimage_set_header() - Create the FAT image and load it into the buffer
+ * @ptr: Output buffer (allocated by fatimage_vrec_header)
+ * @sbuf: stat of the output file (unused)
+ * @ifd: file descriptor of the output file (unused)
+ * @params: mkimage tool parameters; imagefile is used for the temp file name
+ *
+ * Creates the FAT image in a temporary file using mkfs.vfat and mcopy,
+ * reads the result into @ptr, then removes the temporary file.
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int fatimage_set_header(void *ptr, struct stat *sbuf, int ifd,
+			       struct image_tool_params *params)
+{
+	int blocks;
+	int ret;
+	char temp_file[256];
+	FILE *fp;
+	size_t bytes_read;
+
+	snprintf(temp_file, sizeof(temp_file), "%s.tmp", params->imagefile);
+
+	blocks = calculate_image_size(fatparams.input_dir,
+				      fatparams.extra_space,
+				      fatparams.fat_type);
+	if (blocks < 0) {
+		fprintf(stderr, "Error: Failed to calculate image size\n");
+		return -1;
+	}
+
+	ret = create_fat_image(fatparams.input_dir, temp_file,
+			       blocks, fatparams.fat_type,
+			       fatparams.volume_id, fatparams.mkfs_opts);
+	if (ret < 0) {
+		fprintf(stderr, "Error: Failed to create FAT image\n");
+		return -1;
+	}
+
+	fp = fopen(temp_file, "rb");
+	if (!fp) {
+		fprintf(stderr, "Error: Failed to open temporary file %s: %s\n",
+			temp_file, strerror(errno));
+		unlink(temp_file);
+		return -1;
+	}
+
+	bytes_read = fread(ptr, 1, blocks * 1024, fp);
+	fclose(fp);
+	unlink(temp_file);
+
+	if (bytes_read != (size_t)(blocks * 1024)) {
+		fprintf(stderr, "Error: Failed to read FAT image (read %zu, expected %d)\n",
+			bytes_read, blocks * 1024);
+		return -1;
+	}
+
+	printf("FAT image created successfully in buffer\n");
+	return 0;
+}
+
+U_BOOT_IMAGE_TYPE(
+	fatfsimage,
+	"FAT Filesystem Image",
+	0,
+	NULL,
+	fatimage_check_params,
+	fatimage_verify_header,
+	fatimage_print_header,
+	fatimage_set_header,
+	NULL,
+	fatimage_check_image_type,
+	NULL,
+	fatimage_vrec_header
+);
+
+void fatimage_set_dir(const char *dir)
+{
+	fatparams.input_dir = strdup(dir);
+}
+
+void fatimage_set_extra_space(int space_kb)
+{
+	fatparams.extra_space = space_kb;
+}
+
+void fatimage_set_fat_type(int type)
+{
+	fatparams.fat_type = type;
+}
+
+void fatimage_set_volume_id(const char *volid)
+{
+	fatparams.volume_id = strdup(volid);
+}
+
+void fatimage_set_mkfs_opts(const char *opts)
+{
+	fatparams.mkfs_opts = strdup(opts);
+}
diff --git a/tools/fatimage.h b/tools/fatimage.h
new file mode 100644
index 00000000000..8822724925c
--- /dev/null
+++ b/tools/fatimage.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * FAT Filesystem Image support for mkimage
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries
+ */
+
+#ifndef _FATIMAGE_H_
+#define _FATIMAGE_H_
+
+#define SECTOR_SIZE 512
+#define DIR_ENTRY_SIZE 32
+#define FAT_ENTRY_SIZE_FAT32 4
+#define FAT_ENTRY_SIZE_FAT16 2
+#define FAT_ENTRY_SIZE_FAT12 2  /* Rounded up from 1.5 bytes */
+
+struct fatimage_params {
+	char *input_dir;
+	int extra_space;      /* Extra padding space in KB (default: 512) */
+	int fat_type;         /* FAT type: 0=auto, 12/16/32 */
+	char *volume_id;      /* Volume label (default: "BOOT") */
+	char *mkfs_opts;      /* Extra mkfs.vfat options (default: "-S 512") */
+};
+
+#endif /* _FATIMAGE_H_ */
diff --git a/tools/mkimage.c b/tools/mkimage.c
index 3c43962807d..9fbc9b52957 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -204,6 +204,10 @@ static const struct option longopts[] = {
 	{ "tfa-bl31-addr", no_argument, NULL, 'Y' },
 	{ "tee-file", no_argument, NULL, 'z' },
 	{ "tee-addr", no_argument, NULL, 'Z' },
+{ "fat-extra-space", required_argument, NULL, OPT_FAT_EXTRA },
+	{ "fat-type", required_argument, NULL, OPT_FAT_TYPE },
+	{ "fat-volume-id", required_argument, NULL, OPT_FAT_VOLID },
+	{ "fat-mkfs-opts", required_argument, NULL, OPT_FAT_MKFS },
 	{ /* sentinel */ },
 };
 
@@ -397,6 +401,18 @@ static void process_args(int argc, char **argv)
 				exit(EXIT_FAILURE);
 			}
 			break;
+		case OPT_FAT_EXTRA:
+			fatimage_set_extra_space(atoi(optarg));
+			break;
+		case OPT_FAT_TYPE:
+			fatimage_set_fat_type(atoi(optarg));
+			break;
+		case OPT_FAT_VOLID:
+			fatimage_set_volume_id(optarg);
+			break;
+		case OPT_FAT_MKFS:
+			fatimage_set_mkfs_opts(optarg);
+			break;
 		default:
 			usage("Invalid option");
 		}
@@ -433,6 +449,11 @@ static void process_args(int argc, char **argv)
 		params.type = type;
 	}
 
+	if (params.type == IH_TYPE_FATFS && params.datafile) {
+		fatimage_set_dir(params.datafile);
+		params.skipcpy = 1;
+	}
+
 	if (!params.imagefile)
 		usage("Missing output filename");
 }
diff --git a/tools/mkimage.h b/tools/mkimage.h
index 5d6bcc9301a..599f92e617f 100644
--- a/tools/mkimage.h
+++ b/tools/mkimage.h
@@ -53,4 +53,19 @@ static inline ulong map_to_sysmem(const void *ptr)
 #define MKIMAGE_DEFAULT_DTC_OPTIONS	"-I dts -O dtb -p 500"
 #define MKIMAGE_MAX_DTC_CMDLINE_LEN	2 * MKIMAGE_MAX_TMPFILE_LEN + 35
 
+/* Long option values for mkimage */
+enum {
+	OPT_FAT_EXTRA = 512,
+	OPT_FAT_TYPE,
+	OPT_FAT_VOLID,
+	OPT_FAT_MKFS,
+};
+
+/* FAT image functions */
+void fatimage_set_dir(const char *dir);
+void fatimage_set_extra_space(int space_kb);
+void fatimage_set_fat_type(int type);
+void fatimage_set_volume_id(const char *volid);
+void fatimage_set_mkfs_opts(const char *opts);
+
 #endif /* _MKIIMAGE_H_ */
-- 
2.34.1



More information about the U-Boot mailing list