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