xref: /netbsd-src/sbin/newfs_msdos/newfs_msdos.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
1 /*	$NetBSD: newfs_msdos.c,v 1.6 2001/02/19 22:56:21 cgd Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Christos Zoulas
5  * Copyright (c) 1995, 1996 Joerg Wunsch
6  *
7  * All rights reserved.
8  *
9  * This program is free software.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Create an MS-DOS (FAT) file system.
34  *
35  * Id: mkdosfs.c,v 1.4 1997/02/22 16:06:38 peter Exp
36  */
37 
38 #include <sys/cdefs.h>
39 #ifndef lint
40 __RCSID("$NetBSD: newfs_msdos.c,v 1.6 2001/02/19 22:56:21 cgd Exp $");
41 #endif /* not lint */
42 
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <time.h>
49 #include <unistd.h>
50 #include <memory.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <assert.h>
55 
56 /* From msdosfs */
57 #include <direntry.h>
58 #include <bpb.h>
59 #include <bootsect.h>
60 
61 #include "bootcode.h"
62 
63 struct descrip {
64 	/* our database key */
65 	unsigned        kilobytes;
66 	/* MSDOS 3.3 BPB fields */
67 	u_short         sectsiz;
68 	u_char          clustsiz;
69 	u_short         ressecs;
70 	u_char          fatcnt;
71 	u_short         rootsiz;
72 	u_short         totsecs;
73 	u_char          media;
74 	u_short         fatsize;
75 	u_short         trksecs;
76 	u_short         headcnt;
77 	u_short         hidnsec;
78 	/* MSDOS 4 BPB extensions */
79 	u_long          ext_totsecs;
80 	u_short         ext_physdrv;
81 	u_char          ext_extboot;
82 	char            ext_label[11];
83 	char            ext_fsysid[8];
84 };
85 
86 static struct descrip table[] = {
87 	/* NB: must be sorted, starting with the largest format! */
88 	/*
89          *  KB  sec cls res fat  rot   tot   med fsz spt hds hid
90          * tot  phs ebt  label       fsysid
91          */
92 	{ 1440, 512,  1,  1,  2, 224, 2880, 0xf0,  9, 18,  2,  0,
93 	     0,   0,  0, "4.4BSD     ", "FAT12   " },
94 	{ 1200, 512,  1,  1,  2, 224, 2400, 0xf9,  7, 15,  2,  0,
95 	     0,   0,  0, "4.4BSD     ", "FAT12   " },
96 	{  720, 512,  2,  1,  2, 112, 1440, 0xf9,  3,  9,  2,  0,
97 	     0,   0,  0, "4.4BSD     ", "FAT12   " },
98 	{  360, 512,  2,  1,  2, 112,  720, 0xfd,  2,  9,  2,  0,
99 	     0,   0,  0, "4.4BSD     ", "FAT12   " },
100 	{    0,   0,  0,  0,  0,   0,    0, 0x00,  0,  0,  0,  0,
101 	     0,   0,  0, "           ", "        " }
102 };
103 
104 struct fat {
105 	u_int8_t media;		/* the media descriptor again */
106 	u_int8_t padded;	/* always 0xff */
107 	u_int8_t contents[1];	/* the `1' is a placeholder only */
108 };
109 
110 
111 int main __P((int, char *[]));
112 
113 static void usage __P((void));
114 static size_t findformat __P((int));
115 static void setup_boot_sector_from_template __P((union bootsector *,
116     struct descrip *));
117 
118 static void
119 usage()
120 {
121 
122 	(void) fprintf(stderr,
123 	    "Usage: %s [-f <kbytes>] [-L <label>] <device>\n", getprogname());
124 	exit(1);
125 }
126 
127 /*
128  *	Try to deduce a format appropriate for our disk.
129  *	This is a bit tricky.  If the argument is a regular file, we can
130  *	lseek() to its end and get the size reported.  If it's a device
131  *	however, lseeking doesn't report us any useful number.  Instead,
132  *	we try to seek just to the end of the device and try reading a
133  *	block there.  In the case where we've hit exactly the device
134  *	boundary, we get a zero read, and thus have found the size.
135  *	Since our knowledge of distinct formats is limited anyway, this
136  *	is not a big deal at all.
137  */
138 static size_t
139 findformat(fd)
140 	int fd;
141 {
142 	struct stat     sb;
143 	off_t offs;
144 
145 
146 	if (fstat(fd, &sb) == -1)
147 		err(1, "Cannot fstat disk");	/* Cannot happen */
148 
149 	if (S_ISREG(sb.st_mode)) {
150 		if (lseek(fd, (off_t) 0, SEEK_END) == -1 ||
151 		    (offs = lseek(fd, (off_t) 0, SEEK_CUR)) == -1)
152 			/* Hmm, hmm.  Hard luck. */
153 			return 0;
154 		(void) lseek(fd, (off_t) 0, SEEK_SET);
155 		return (size_t) (offs / 1024);
156 	} else if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
157 		char            b[512];
158 		int             rv;
159 		struct descrip *dp;
160 
161 		for (dp = table; dp->kilobytes != 0; dp++) {
162 			offs = dp->kilobytes * 1024;
163 
164 			if (lseek(fd, offs, SEEK_SET) == -1)
165 				/* Uh-oh, lseek() is not supposed to fail. */
166 				return 0;
167 
168 			if ((rv = read(fd, b, 512)) == 0)
169 				break;
170 			/*
171 			 * XXX The ENOSPC is for the bogus fd(4) driver
172 			 * return value.
173 			 */
174 			if (rv == -1 && errno != EINVAL && errno != ENOSPC)
175 				return 0;
176 			/* else: continue */
177 		}
178 		(void) lseek(fd, (off_t) 0, SEEK_SET);
179 		return dp->kilobytes;
180 	} else
181 		/* Outta luck. */
182 		return 0;
183 }
184 
185 
186 static void
187 setup_boot_sector_from_template(bs, dp)
188 	union bootsector *bs;
189 	struct descrip *dp;
190 {
191 	struct byte_bpb50 bpb;
192 	struct extboot *exb = (struct extboot *)bs->bs50.bsExt;
193 
194 	assert(sizeof(bs->bs50) == 512);
195 	assert(sizeof(bootcode) == 512);
196 
197 	(void) memcpy(&bs->bs50, bootcode, 512);
198 
199 	putushort(bpb.bpbBytesPerSec, dp->sectsiz);
200 	bpb.bpbSecPerClust = dp->clustsiz;
201 	putushort(bpb.bpbResSectors, dp->ressecs);
202 	bpb.bpbFATs = dp->fatcnt;
203 	putushort(bpb.bpbRootDirEnts, dp->rootsiz);
204 	putushort(bpb.bpbSectors, dp->totsecs);
205 	bpb.bpbMedia = dp->media;
206 	putushort(bpb.bpbFATsecs, dp->fatsize);
207 	putushort(bpb.bpbSecPerTrack, dp->trksecs);
208 	putushort(bpb.bpbHeads, dp->headcnt);
209 	putulong(bpb.bpbHiddenSecs, dp->hidnsec);
210 	putulong(bpb.bpbHugeSectors, dp->ext_totsecs);
211 
212 	exb->exDriveNumber = dp->ext_physdrv;
213 	exb->exBootSignature = dp->ext_extboot;
214 
215 	/* assign a "serial number" :) */
216 	srandom((unsigned) time((time_t) 0));
217 	putulong(exb->exVolumeID, random());
218 
219 	(void) memcpy(exb->exVolumeLabel, dp->ext_label,
220 	    MIN(sizeof(dp->ext_label), sizeof(exb->exVolumeLabel)));
221 	(void) memcpy(exb->exFileSysType, dp->ext_fsysid,
222 	    MIN(sizeof(dp->ext_fsysid), sizeof(exb->exFileSysType)));
223 	(void) memcpy(bs->bs50.bsBPB, &bpb,
224 	    MIN(sizeof(bpb), sizeof(bs->bs50.bsBPB)));
225 }
226 
227 int
228 main(argc, argv)
229 	int argc;
230 	char *argv[];
231 {
232 	union bootsector bs;
233 	struct descrip *dp;
234 	struct fat     *fat;
235 	struct direntry *rootdir;
236 	struct tm      *tp;
237 	time_t          now;
238 	int             c, i, fd, format = 0, rootdirsize, fatsz;
239 	const char     *label = 0;
240 
241 	while ((c = getopt(argc, argv, "f:L:")) != -1)
242 		switch (c) {
243 		case 'f':
244 			format = atoi(optarg);
245 			break;
246 
247 		case 'L':
248 			label = optarg;
249 			break;
250 
251 		case '?':
252 		default:
253 			usage();
254 		}
255 
256 	argc -= optind;
257 	argv += optind;
258 
259 	if (argc != 1)
260 		usage();
261 
262 	if ((fd = open(argv[0], O_RDWR | O_EXCL, 0)) == -1)
263 		err(1, "Cannot open `%s'", argv[0]);
264 
265 	/* If no format specified, try to figure it out. */
266 	if (format == 0  && (format = findformat(fd)) == 0)
267 		errx(1, "Cannot determine size, must use -f format");
268 
269 	for (dp = table; dp->kilobytes != 0; dp++)
270 		if (dp->kilobytes == format)
271 			break;
272 
273 	if (dp->kilobytes == 0)
274 		errx(1, "Cannot find format description for %d KB", format);
275 
276 	/* prepare and write the boot sector */
277 	setup_boot_sector_from_template(&bs, dp);
278 
279 	/* if we've got an explicit label, use it */
280 	if (label)
281 		(void) strncpy(((struct extboot *)(bs.bs50.bsExt))
282 			       ->exVolumeLabel, label, 11);
283 
284 	if (write(fd, &bs, sizeof bs) != sizeof bs)
285 		err(1, "Writing boot sector");
286 
287 	/* now, go on with the FATs */
288 	if ((fat = (struct fat *) malloc(dp->sectsiz * dp->fatsize)) == NULL)
289 		err(1, "Out of memory (FAT table)");
290 
291 	(void) memset(fat, 0, dp->sectsiz * dp->fatsize);
292 
293 	fat->media = dp->media;
294 	fat->padded = 0xff;
295 	fat->contents[0] = 0xff;
296 	if (dp->totsecs > 20740 ||
297 	    (dp->totsecs == 0 && dp->ext_totsecs > 20740))
298 		/* 16-bit FAT */
299 		fat->contents[1] = 0xff;
300 
301 	fatsz =  dp->sectsiz * dp->fatsize;
302 	for (i = 0; i < dp->fatcnt; i++)
303 		if (write(fd, fat, fatsz) != fatsz)
304 			err(1, "Writing FAT %d", i);
305 
306 	free(fat);
307 
308 	/* finally, build the root dir */
309 	rootdirsize = dp->rootsiz * sizeof(struct direntry);
310 	rootdirsize = roundup(rootdirsize, dp->clustsiz * dp->sectsiz);
311 
312 	if ((rootdir = (struct direntry *) malloc(rootdirsize)) == NULL)
313 		err(1, "Out of memory (root directory)");
314 
315 	(void) memset(rootdir, 0, rootdirsize);
316 
317 	/* set up a volume label inside the root dir :) */
318 	if (label)
319 		(void) strncpy(rootdir->deName, label, 11);
320 	else
321 		(void) memcpy(rootdir->deName, dp->ext_label, 11);
322 
323 	rootdir->deAttributes = ATTR_VOLUME;
324 	now = time((time_t) 0);
325 	tp = localtime(&now);
326 	rootdir->deCTime[0] = tp->tm_sec / 2;
327 	rootdir->deCTime[0] |= (tp->tm_min & 7) << 5;
328 	rootdir->deCTime[1] = ((tp->tm_min >> 3) & 7);
329 	rootdir->deCTime[1] |= tp->tm_hour << 3;
330 	rootdir->deCDate[0] = tp->tm_mday;
331 	rootdir->deCDate[0] |= ((tp->tm_mon + 1) & 7) << 5;
332 	rootdir->deCDate[1] = ((tp->tm_mon + 1) >> 3) & 1;
333 	rootdir->deCDate[1] |= (tp->tm_year - 80) << 1;
334 
335 	if (write(fd, rootdir, rootdirsize) != rootdirsize)
336 		err(1, "Writing root directory");
337 
338 	(void) close(fd);
339 
340 	return 0;
341 }
342