newfs_msdos.c revision 1.3 1 /* $NetBSD: newfs_msdos.c,v 1.3 1997/10/17 17:47:41 drochner 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.3 1997/10/17 17:47:41 drochner 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
119 static void
120 usage()
121 {
122 extern char *__progname;
123
124 (void) fprintf(stderr,
125 "Usage: %s [-f <kbytes>] [-L <label>] <device>\n", __progname);
126 exit(1);
127 }
128
129 /*
130 * Try to deduce a format appropriate for our disk.
131 * This is a bit tricky. If the argument is a regular file, we can
132 * lseek() to its end and get the size reported. If it's a device
133 * however, lseeking doesn't report us any useful number. Instead,
134 * we try to seek just to the end of the device and try reading a
135 * block there. In the case where we've hit exactly the device
136 * boundary, we get a zero read, and thus have found the size.
137 * Since our knowledge of distinct formats is limited anyway, this
138 * is not a big deal at all.
139 */
140 static size_t
141 findformat(fd)
142 int fd;
143 {
144 struct stat sb;
145 off_t offs;
146
147
148 if (fstat(fd, &sb) == -1)
149 err(1, "Cannot fstat disk"); /* Cannot happen */
150
151 if (S_ISREG(sb.st_mode)) {
152 if (lseek(fd, (off_t) 0, SEEK_END) == -1 ||
153 (offs = lseek(fd, (off_t) 0, SEEK_CUR)) == -1)
154 /* Hmm, hmm. Hard luck. */
155 return 0;
156 return (size_t) (offs / 1024);
157 } else if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
158 char b[512];
159 int rv;
160 struct descrip *dp;
161
162 for (dp = table; dp->kilobytes != 0; dp++) {
163 offs = dp->kilobytes * 1024;
164
165 if (lseek(fd, offs, SEEK_SET) == -1)
166 /* Uh-oh, lseek() is not supposed to fail. */
167 return 0;
168
169 if ((rv = read(fd, b, 512)) == 0)
170 break;
171 /*
172 * XXX The ENOSPC is for the bogus fd(4) driver
173 * return value.
174 */
175 if (rv == -1 && errno != EINVAL && errno != ENOSPC)
176 return 0;
177 /* else: continue */
178 }
179 (void) lseek(fd, (off_t) 0, SEEK_SET);
180 return dp->kilobytes;
181 } else
182 /* Outta luck. */
183 return 0;
184 }
185
186
187 static void
188 setup_boot_sector_from_template(bs, dp)
189 union bootsector *bs;
190 struct descrip *dp;
191 {
192 struct byte_bpb50 bpb;
193 struct extboot *exb = (struct extboot *)bs->bs50.bsExt;
194
195 assert(sizeof(bs->bs50) == 512);
196 assert(sizeof(bootcode) == 512);
197
198 (void) memcpy(&bs->bs50, bootcode, 512);
199
200 putushort(bpb.bpbBytesPerSec, dp->sectsiz);
201 bpb.bpbSecPerClust = dp->clustsiz;
202 putushort(bpb.bpbResSectors, dp->ressecs);
203 bpb.bpbFATs = dp->fatcnt;
204 putushort(bpb.bpbRootDirEnts, dp->rootsiz);
205 putushort(bpb.bpbSectors, dp->totsecs);
206 bpb.bpbMedia = dp->media;
207 putushort(bpb.bpbFATsecs, dp->fatsize);
208 putushort(bpb.bpbSecPerTrack, dp->trksecs);
209 putushort(bpb.bpbHeads, dp->headcnt);
210 putulong(bpb.bpbHiddenSecs, dp->hidnsec);
211 putulong(bpb.bpbHugeSectors, dp->ext_totsecs);
212
213 exb->exDriveNumber = dp->ext_physdrv;
214 exb->exBootSignature = dp->ext_extboot;
215
216 /* assign a "serial number" :) */
217 srandom((unsigned) time((time_t) 0));
218 putulong(exb->exVolumeID, random());
219
220 (void) memcpy(exb->exVolumeLabel, dp->ext_label,
221 MIN(sizeof(dp->ext_label), sizeof(exb->exVolumeLabel)));
222 (void) memcpy(exb->exFileSysType, dp->ext_fsysid,
223 MIN(sizeof(dp->ext_fsysid), sizeof(exb->exFileSysType)));
224 (void) memcpy(bs->bs50.bsBPB, &bpb,
225 MIN(sizeof(bpb), sizeof(bs->bs50.bsBPB)));
226 }
227
228 int
229 main(argc, argv)
230 int argc;
231 char *argv[];
232 {
233 union bootsector bs;
234 struct descrip *dp;
235 struct fat *fat;
236 struct direntry *rootdir;
237 struct tm *tp;
238 time_t now;
239 int c, i, fd, format = 0, rootdirsize, fatsz;
240 const char *label = 0;
241
242 while ((c = getopt(argc, argv, "f:L:")) != -1)
243 switch (c) {
244 case 'f':
245 format = atoi(optarg);
246 break;
247
248 case 'L':
249 label = optarg;
250 break;
251
252 case '?':
253 default:
254 usage();
255 }
256
257 argc -= optind;
258 argv += optind;
259
260 if (argc != 1)
261 usage();
262
263 if ((fd = open(argv[0], O_RDWR | O_EXCL, 0)) == -1)
264 err(1, "Cannot open `%s'", argv[0]);
265
266 /* If no format specified, try to figure it out. */
267 if (format == 0 && (format = findformat(fd)) == 0)
268 errx(1, "Cannot determine size, must use -f format");
269
270 for (dp = table; dp->kilobytes != 0; dp++)
271 if (dp->kilobytes == format)
272 break;
273
274 if (dp->kilobytes == 0)
275 errx(1, "Cannot find format description for %d KB", format);
276
277 /* prepare and write the boot sector */
278 setup_boot_sector_from_template(&bs, dp);
279
280 /* if we've got an explicit label, use it */
281 if (label)
282 (void) strncpy(((struct extboot *)(bs.bs50.bsExt))
283 ->exVolumeLabel, label, 11);
284
285 if (write(fd, &bs, sizeof bs) != sizeof bs)
286 err(1, "Writing boot sector");
287
288 /* now, go on with the FATs */
289 if ((fat = (struct fat *) malloc(dp->sectsiz * dp->fatsize)) == NULL)
290 err(1, "Out of memory (FAT table)");
291
292 (void) memset(fat, 0, dp->sectsiz * dp->fatsize);
293
294 fat->media = dp->media;
295 fat->padded = 0xff;
296 fat->contents[0] = 0xff;
297 if (dp->totsecs > 20740 ||
298 (dp->totsecs == 0 && dp->ext_totsecs > 20740))
299 /* 16-bit FAT */
300 fat->contents[1] = 0xff;
301
302 fatsz = dp->sectsiz * dp->fatsize;
303 for (i = 0; i < dp->fatcnt; i++)
304 if (write(fd, fat, fatsz) != fatsz)
305 err(1, "Writing FAT %d", i);
306
307 free(fat);
308
309 /* finally, build the root dir */
310 rootdirsize = dp->rootsiz * sizeof(struct direntry);
311 rootdirsize = roundup(rootdirsize, dp->clustsiz * dp->sectsiz);
312
313 if ((rootdir = (struct direntry *) malloc(rootdirsize)) == NULL)
314 err(1, "Out of memory (root directory)");
315
316 (void) memset(rootdir, 0, rootdirsize);
317
318 /* set up a volume label inside the root dir :) */
319 if (label)
320 (void) strncpy(rootdir->deName, label, 11);
321 else
322 (void) memcpy(rootdir->deName, dp->ext_label, 11);
323
324 rootdir->deAttributes = ATTR_VOLUME;
325 now = time((time_t) 0);
326 tp = localtime(&now);
327 rootdir->deCTime[0] = tp->tm_sec / 2;
328 rootdir->deCTime[0] |= (tp->tm_min & 7) << 5;
329 rootdir->deCTime[1] = ((tp->tm_min >> 3) & 7);
330 rootdir->deCTime[1] |= tp->tm_hour << 3;
331 rootdir->deCDate[0] = tp->tm_mday;
332 rootdir->deCDate[0] |= ((tp->tm_mon + 1) & 7) << 5;
333 rootdir->deCDate[1] = ((tp->tm_mon + 1) >> 3) & 1;
334 rootdir->deCDate[1] |= (tp->tm_year - 80) << 1;
335
336 if (write(fd, rootdir, rootdirsize) != rootdirsize)
337 err(1, "Writing root directory");
338
339 (void) close(fd);
340
341 return 0;
342 }
343