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