xref: /netbsd-src/usr.sbin/makefs/msdos.c (revision 0b920f749aeb2613bd4db5f92939b0537ba58c0a)
1*0b920f74Stkusumi /*	$NetBSD: msdos.c,v 1.25 2024/02/29 08:13:52 tkusumi Exp $	*/
23d364f54Schristos 
33d364f54Schristos /*-
43d364f54Schristos  * Copyright (c) 2013 The NetBSD Foundation, Inc.
53d364f54Schristos  * All rights reserved.
63d364f54Schristos  *
73d364f54Schristos  * This code is derived from software contributed to The NetBSD Foundation
83d364f54Schristos  * by Christos Zoulas.
93d364f54Schristos  *
103d364f54Schristos  * Redistribution and use in source and binary forms, with or without
113d364f54Schristos  * modification, are permitted provided that the following conditions
123d364f54Schristos  * are met:
133d364f54Schristos  * 1. Redistributions of source code must retain the above copyright
143d364f54Schristos  *    notice, this list of conditions and the following disclaimer.
153d364f54Schristos  * 2. Redistributions in binary form must reproduce the above copyright
163d364f54Schristos  *    notice, this list of conditions and the following disclaimer in the
173d364f54Schristos  *    documentation and/or other materials provided with the distribution.
183d364f54Schristos  *
193d364f54Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
203d364f54Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
213d364f54Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223d364f54Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
233d364f54Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
243d364f54Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
253d364f54Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
263d364f54Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
273d364f54Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
283d364f54Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
293d364f54Schristos  * POSSIBILITY OF SUCH DAMAGE.
303d364f54Schristos  */
313d364f54Schristos #if HAVE_NBTOOL_CONFIG_H
323d364f54Schristos #include "nbtool_config.h"
333d364f54Schristos #endif
343d364f54Schristos 
353d364f54Schristos #include <sys/cdefs.h>
363d364f54Schristos #if defined(__RCSID) && !defined(__lint)
37*0b920f74Stkusumi __RCSID("$NetBSD: msdos.c,v 1.25 2024/02/29 08:13:52 tkusumi Exp $");
383d364f54Schristos #endif	/* !__lint */
393d364f54Schristos 
403d364f54Schristos #include <sys/param.h>
413d364f54Schristos 
423d364f54Schristos #if !HAVE_NBTOOL_CONFIG_H
433d364f54Schristos #include <sys/mount.h>
443d364f54Schristos #endif
453d364f54Schristos 
463d364f54Schristos #include <assert.h>
473d364f54Schristos #include <errno.h>
483d364f54Schristos #include <fcntl.h>
493d364f54Schristos #include <stdarg.h>
503d364f54Schristos #include <stdio.h>
513d364f54Schristos #include <stdlib.h>
523d364f54Schristos #include <string.h>
533d364f54Schristos #include <unistd.h>
547e2d9be6Schristos #include <dirent.h>
55e4989541Schristos #include <util.h>
563d364f54Schristos 
577e2d9be6Schristos #include <ffs/buf.h>
58223c7df5Smlelstv #include <fs/msdosfs/bpb.h>
597e2d9be6Schristos #include <fs/msdosfs/denode.h>
60223c7df5Smlelstv #include <fs/msdosfs/msdosfsmount.h>
613d364f54Schristos #include "makefs.h"
627e2d9be6Schristos #include "msdos.h"
633d364f54Schristos #include "mkfs_msdos.h"
643d364f54Schristos 
657e2d9be6Schristos static int msdos_populate_dir(const char *, struct denode *, fsnode *,
667e2d9be6Schristos     fsnode *, fsinfo_t *);
677e2d9be6Schristos 
68223c7df5Smlelstv struct msdos_options_ex {
69223c7df5Smlelstv 	struct msdos_options options;
70223c7df5Smlelstv 	bool utf8;
71223c7df5Smlelstv };
72223c7df5Smlelstv 
733d364f54Schristos void
msdos_prep_opts(fsinfo_t * fsopts)743d364f54Schristos msdos_prep_opts(fsinfo_t *fsopts)
753d364f54Schristos {
76223c7df5Smlelstv 	struct msdos_options_ex *msdos_opt = ecalloc(1, sizeof(*msdos_opt));
77e4989541Schristos 	const option_t msdos_options[] = {
783d364f54Schristos #define AOPT(_opt, _type, _name, _min, _desc) { 			\
793d364f54Schristos 	.letter = _opt,							\
803d364f54Schristos 	.name = # _name,						\
813d364f54Schristos 	.type = _min == -1 ? OPT_STRPTR :				\
823d364f54Schristos 	    (_min == -2 ? OPT_BOOL :					\
833d364f54Schristos 	    (sizeof(_type) == 1 ? OPT_INT8 :				\
843d364f54Schristos 	    (sizeof(_type) == 2 ? OPT_INT16 :				\
853d364f54Schristos 	    (sizeof(_type) == 4 ? OPT_INT32 : OPT_INT64)))),		\
86223c7df5Smlelstv 	.value = &msdos_opt->options._name,				\
873d364f54Schristos 	.minimum = _min,						\
883d364f54Schristos 	.maximum = sizeof(_type) == 1 ? 0xff :				\
893d364f54Schristos 	    (sizeof(_type) == 2 ? 0xffff :				\
903d364f54Schristos 	    (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)),	\
913d364f54Schristos 	.desc = _desc,						\
923d364f54Schristos },
933d364f54Schristos ALLOPTS
943d364f54Schristos #undef AOPT
95223c7df5Smlelstv 		{ 'U', "utf8", &msdos_opt->utf8, OPT_BOOL,
96223c7df5Smlelstv 		  0, 1, "Use UTF8 names" },
973d364f54Schristos 		{ .name = NULL }
983d364f54Schristos 	};
99e4989541Schristos 
100e4989541Schristos 	fsopts->fs_specific = msdos_opt;
101e4989541Schristos 	fsopts->fs_options = copy_opts(msdos_options);
102e4989541Schristos }
103e4989541Schristos 
104e4989541Schristos void
msdos_cleanup_opts(fsinfo_t * fsopts)105e4989541Schristos msdos_cleanup_opts(fsinfo_t *fsopts)
106e4989541Schristos {
107e4989541Schristos 	free(fsopts->fs_specific);
108e4989541Schristos 	free(fsopts->fs_options);
109e4989541Schristos }
110e4989541Schristos 
111e4989541Schristos int
msdos_parse_opts(const char * option,fsinfo_t * fsopts)112e4989541Schristos msdos_parse_opts(const char *option, fsinfo_t *fsopts)
113e4989541Schristos {
114e4989541Schristos 	struct msdos_options *msdos_opt = fsopts->fs_specific;
115e4989541Schristos 	option_t *msdos_options = fsopts->fs_options;
116e4989541Schristos 
117562664d1Schristos 	int rv;
1183d364f54Schristos 
1193d364f54Schristos 	assert(option != NULL);
1203d364f54Schristos 	assert(fsopts != NULL);
1213d364f54Schristos 	assert(msdos_opt != NULL);
1223d364f54Schristos 
1233d364f54Schristos 	if (debug & DEBUG_FS_PARSE_OPTS)
124ca46f897Schristos 		printf("%s: got `%s'\n", __func__, option);
1253d364f54Schristos 
1268459845fSchristos 	rv = set_option(msdos_options, option, NULL, 0);
127562664d1Schristos 	if (rv == -1)
1286981fea5Schristos 		return rv;
1296981fea5Schristos 
130562664d1Schristos 	if (strcmp(msdos_options[rv].name, "volume_id") == 0)
1316981fea5Schristos 		msdos_opt->volume_id_set = 1;
132562664d1Schristos 	else if (strcmp(msdos_options[rv].name, "media_descriptor") == 0)
1336981fea5Schristos 		msdos_opt->media_descriptor_set = 1;
134562664d1Schristos 	else if (strcmp(msdos_options[rv].name, "hidden_sectors") == 0)
1356981fea5Schristos 		msdos_opt->hidden_sectors_set = 1;
136e93222b7Schristos 
137e93222b7Schristos 
138562664d1Schristos 	return 1;
1393d364f54Schristos }
1403d364f54Schristos 
1413d364f54Schristos 
1423d364f54Schristos void
msdos_makefs(const char * image,const char * dir,fsnode * root,fsinfo_t * fsopts)1433d364f54Schristos msdos_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
1443d364f54Schristos {
145223c7df5Smlelstv 	struct msdos_options_ex *msdos_opt = fsopts->fs_specific;
1467e2d9be6Schristos 	struct vnode vp, rootvp;
1477e2d9be6Schristos 	struct timeval	start;
1487e2d9be6Schristos 	struct msdosfsmount *pmp;
149223c7df5Smlelstv 	uint32_t flags;
1507e2d9be6Schristos 
1517e2d9be6Schristos 	assert(image != NULL);
1527e2d9be6Schristos 	assert(dir != NULL);
1537e2d9be6Schristos 	assert(root != NULL);
1547e2d9be6Schristos 	assert(fsopts != NULL);
155273b781bSchristos 
1563bf4d13eSchristos 	fsopts->size = fsopts->maxsize;
157223c7df5Smlelstv 	msdos_opt->options.create_size = MAX(msdos_opt->options.create_size,
1583bf4d13eSchristos 	    fsopts->offset + fsopts->size);
159223c7df5Smlelstv 	msdos_opt->options.offset = fsopts->offset;
160223c7df5Smlelstv 	if (msdos_opt->options.bytes_per_sector == 0) {
161d84c38aeSchristos 		if (fsopts->sectorsize == -1)
1620ea6b665Schristos 			fsopts->sectorsize = 512;
163223c7df5Smlelstv 		msdos_opt->options.bytes_per_sector = fsopts->sectorsize;
164d84c38aeSchristos 	} else if (fsopts->sectorsize == -1) {
165223c7df5Smlelstv 		fsopts->sectorsize = msdos_opt->options.bytes_per_sector;
166223c7df5Smlelstv 	} else if (fsopts->sectorsize != msdos_opt->options.bytes_per_sector) {
167ec5d1d82Stsutsui 		err(EXIT_FAILURE, "inconsistent sectorsize -S %u"
1680ea6b665Schristos 		    "!= -o bytes_per_sector %u",
169223c7df5Smlelstv 		    fsopts->sectorsize, msdos_opt->options.bytes_per_sector);
1700ea6b665Schristos 	}
171ca46f897Schristos 	if (stampst.st_ino) {
172ca46f897Schristos 		msdos_opt->options.timestamp_set = 1;
173ca46f897Schristos 		msdos_opt->options.timestamp = stampst.st_mtime;
174ca46f897Schristos 	}
175273b781bSchristos 
1767e2d9be6Schristos 		/* create image */
1777e2d9be6Schristos 	printf("Creating `%s'\n", image);
1787e2d9be6Schristos 	TIMER_START(start);
179223c7df5Smlelstv 	if (mkfs_msdos(image, NULL, &msdos_opt->options) == -1)
180ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "Image file `%s' not created.", image);
1817e2d9be6Schristos 	TIMER_RESULTS(start, "mkfs_msdos");
1823d364f54Schristos 
183d84c38aeSchristos 	fsopts->fd = open(image, O_RDWR);
184d84c38aeSchristos 	vp.fs = fsopts;
1857e2d9be6Schristos 
186223c7df5Smlelstv 	flags = 0;
187223c7df5Smlelstv 	if (msdos_opt->utf8)
188223c7df5Smlelstv 		flags |= MSDOSFSMNT_UTF8;
189223c7df5Smlelstv 
190223c7df5Smlelstv 	if ((pmp = msdosfs_mount(&vp, flags)) == NULL)
191ec5d1d82Stsutsui 		err(EXIT_FAILURE, "msdosfs_mount");
1927e2d9be6Schristos 
1937e2d9be6Schristos 	if (msdosfs_root(pmp, &rootvp) != 0)
194ec5d1d82Stsutsui 		err(EXIT_FAILURE, "msdosfs_root");
1953d364f54Schristos 
1963d364f54Schristos 	if (debug & DEBUG_FS_MAKEFS)
1973d364f54Schristos 		printf("msdos_makefs: image %s directory %s root %p\n",
1983d364f54Schristos 		    image, dir, root);
1993d364f54Schristos 
2003d364f54Schristos 		/* populate image */
2013d364f54Schristos 	printf("Populating `%s'\n", image);
2023d364f54Schristos 	TIMER_START(start);
2037e2d9be6Schristos 	if (msdos_populate_dir(dir, VTODE(&rootvp), root, root, fsopts) == -1)
204ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "Image file `%s' not populated.", image);
2053d364f54Schristos 	TIMER_RESULTS(start, "msdos_populate_dir");
2063d364f54Schristos 
2077e2d9be6Schristos 	if (debug & DEBUG_FS_MAKEFS)
2087e2d9be6Schristos 		putchar('\n');
2097e2d9be6Schristos 
2103d364f54Schristos 		/* ensure no outstanding buffers remain */
2113d364f54Schristos 	if (debug & DEBUG_FS_MAKEFS)
2123d364f54Schristos 		bcleanup();
2133d364f54Schristos 
2143d364f54Schristos 	printf("Image `%s' complete\n", image);
2157e2d9be6Schristos }
2167e2d9be6Schristos 
2177e2d9be6Schristos static int
msdos_populate_dir(const char * path,struct denode * dir,fsnode * root,fsnode * parent,fsinfo_t * fsopts)2187e2d9be6Schristos msdos_populate_dir(const char *path, struct denode *dir, fsnode *root,
2197e2d9be6Schristos     fsnode *parent, fsinfo_t *fsopts)
2207e2d9be6Schristos {
2217e2d9be6Schristos 	fsnode *cur;
2227e2d9be6Schristos 	char pbuf[MAXPATHLEN];
2237e2d9be6Schristos 
2247e2d9be6Schristos 	assert(dir != NULL);
2257e2d9be6Schristos 	assert(root != NULL);
2267e2d9be6Schristos 	assert(fsopts != NULL);
2277e2d9be6Schristos 
2287e2d9be6Schristos 	for (cur = root->next; cur != NULL; cur = cur->next) {
229*0b920f74Stkusumi 		if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s/%s",
230*0b920f74Stkusumi 		    cur->root, cur->path, cur->name) >= sizeof(pbuf)) {
2317e2d9be6Schristos 			warnx("path %s too long", pbuf);
2327e2d9be6Schristos 			return -1;
2337e2d9be6Schristos 		}
2347e2d9be6Schristos 
2357e2d9be6Schristos 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
2367e2d9be6Schristos 			cur->inode->flags |= FI_ALLOCATED;
2377e2d9be6Schristos 			if (cur != root) {
2387e2d9be6Schristos 				fsopts->curinode++;
2397e2d9be6Schristos 				cur->inode->ino = fsopts->curinode;
2407e2d9be6Schristos 				cur->parent = parent;
2417e2d9be6Schristos 			}
2427e2d9be6Schristos 		}
2437e2d9be6Schristos 
2447e2d9be6Schristos 		if (cur->inode->flags & FI_WRITTEN) {
2457e2d9be6Schristos 			continue;	// hard link
2467e2d9be6Schristos 		}
2477e2d9be6Schristos 		cur->inode->flags |= FI_WRITTEN;
2487e2d9be6Schristos 
2497e2d9be6Schristos 		if (cur->child) {
2507e2d9be6Schristos 			struct denode *de;
251a61457faSchristos 			if ((de = msdosfs_mkdire(pbuf, dir, cur)) == NULL) {
252a61457faSchristos 				warn("msdosfs_mkdire %s", pbuf);
253a61457faSchristos 				return -1;
254a61457faSchristos 			}
255a61457faSchristos 			if (msdos_populate_dir(pbuf, de, cur->child, cur,
256a61457faSchristos 			    fsopts) == -1) {
257a61457faSchristos 				warn("msdos_populate_dir %s", pbuf);
258a61457faSchristos 				return -1;
259a61457faSchristos 			}
2607e2d9be6Schristos 			continue;
2617e2d9be6Schristos 		} else if (!S_ISREG(cur->type)) {
2627e2d9be6Schristos 			warnx("skipping non-regular file %s/%s", cur->path,
2637e2d9be6Schristos 			    cur->name);
2647e2d9be6Schristos 			continue;
2657e2d9be6Schristos 		}
266a61457faSchristos 		if (msdosfs_mkfile(pbuf, dir, cur) == NULL) {
267a61457faSchristos 			warn("msdosfs_mkfile %s", pbuf);
268a61457faSchristos 			return -1;
269a61457faSchristos 		}
2707e2d9be6Schristos 	}
2717e2d9be6Schristos 	return 0;
2723d364f54Schristos }
273