xref: /haiku/src/add-ons/kernel/file_systems/fat/mkdos.cpp (revision db6fcb750a1afb5fdc752322972adf6044d3b4c4)
1 /*
2 
3 mkdos shell tool
4 
5 Initialize FAT16 or FAT32 partitions, FAT12 floppy disks not supported
6 
7 Copyright (c) 2015, François Revol <revol@free.fr>
8 Copyright (c) 2002 Marcus Overhagen <marcus@overhagen.de>, OpenBeOS project
9 
10 Permission is hereby granted, free of charge, to any person obtaining a copy of
11 this software and associated documentation files (the "Software"), to deal in
12 the Software without restriction, including without limitation the rights to
13 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 of the Software, and to permit persons to whom the Software is furnished to do
15 so, subject to the following conditions:
16 
17 The above copyright notice and this permission notice shall be included in all
18 copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 THE SOFTWARE.
27 
28 */
29 #include <ByteOrder.h>
30 #include <Drivers.h>
31 #include <driver_settings.h>
32 #include <KernelExport.h>
33 #include <OS.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <unistd.h>
41 #define MKDOS
42 #include "mkdos.h"
43 
44 #define WITH_FLOPPY_SUPPORT
45 
46 void PrintUsage();
47 void CreateVolumeLabel(void *sector, const char *label);
48 status_t Initialize(int fatbits, const char *device, const char *label, bool noprompt, bool testmode);
49 status_t parse_initialize_parameters(const char* parameterString,
50 	initialize_parameters& parameters);
51 
52 
53 status_t
54 check_volume_name(const char* name)
55 {
56 	if (name == NULL || strlen(name) >= 11
57 		|| strchr(name, '/') != NULL) {
58 		return B_BAD_VALUE;
59 	}
60 
61 	return B_OK;
62 }
63 
64 
65 status_t
66 parse_initialize_parameters(const char* parameterString,
67 	initialize_parameters& parameters)
68 {
69 	parameters.flags = 0;
70 	parameters.verbose = false;
71 
72 	void *handle = parse_driver_settings_string(parameterString);
73 	if (handle == NULL)
74 		return B_ERROR;
75 
76 //	if (get_driver_boolean_parameter(handle, "noindex", false, true))
77 //		parameters.flags |= VOLUME_NO_INDICES;
78 	if (get_driver_boolean_parameter(handle, "verbose", false, true))
79 		parameters.verbose = true;
80 
81 	const char *string = get_driver_parameter(handle, "fat",
82 		NULL, NULL);
83 	uint32 fatBits = 0;
84 	if (string != NULL)
85 		fatBits = strtoul(string, NULL, 0);
86 
87 	delete_driver_settings(handle);
88 
89 	if (fatBits != 0 && fatBits != 12 && fatBits != 16 && fatBits != 32) {
90 		printf("mkdos error: fat must be 12, 16, or 32 bits\n");
91 		return B_BAD_VALUE;
92 	}
93 
94 	parameters.fatBits = fatBits;
95 
96 	return B_OK;
97 }
98 
99 
100 
101 status_t
102 dosfs_initialize(int fd, partition_id partitionID, const char* name,
103 	const char* parameterString, off_t partitionSize, disk_job_id job)
104 {
105 	dprintf("dosfs_initialize(%d, , '%s', '%s', %" B_PRIdOFF ")\n",
106 		fd, name, parameterString, partitionSize);
107 	if (sizeof(bootsector1216) != 512 || sizeof(bootsector32) != 512
108 		|| sizeof(fsinfosector32) != 512) {
109 		dprintf("dosfs: compilation error: struct alignment wrong\n");
110 		return B_BAD_VALUE;
111 	}
112 
113 	// check name
114 	status_t status = check_volume_name(name);
115 	if (status != B_OK)
116 		return status;
117 
118 	// parse parameters
119 	initialize_parameters parameters;
120 	status = parse_initialize_parameters(parameterString, parameters);
121 	if (status != B_OK)
122 		return status;
123 
124 	update_disk_device_job_progress(job, 0);
125 
126 	int fatbits = parameters.fatBits;
127 	const char *label = name;
128 
129 	// initialize the volume
130 //	Volume volume(NULL);
131 //	status = volume.Initialize(fd, name, parameters.blockSize,
132 //		parameters.flags);
133 
134 	if (fatbits != 0 && fatbits != 12 && fatbits != 16 && fatbits != 32) {
135 		dprintf("dosfs Error: don't know how to create a %d bit fat\n",fatbits);
136 		return B_ERROR;
137 	}
138 
139 
140 
141 	bool isRawDevice;
142 	bool hasBiosGeometry;
143 	bool hasDeviceGeometry;
144 	bool hasPartitionInfo;
145 	device_geometry biosGeometry;
146 	device_geometry deviceGeometry;
147 	partition_info 	partitionInfo;
148 
149 	isRawDevice = 0;//0 != strstr(device, "/raw");
150 	hasBiosGeometry = B_OK == ioctl(fd, B_GET_BIOS_GEOMETRY, &biosGeometry, sizeof(biosGeometry));
151 	hasDeviceGeometry = B_OK == ioctl(fd, B_GET_GEOMETRY, &deviceGeometry, sizeof(deviceGeometry));
152 	hasPartitionInfo = B_OK == ioctl(fd, B_GET_PARTITION_INFO, &partitionInfo, sizeof(partitionInfo));
153 
154 	if (!isRawDevice && !hasBiosGeometry && !hasDeviceGeometry && !hasPartitionInfo)
155 		isRawDevice = true;
156 
157 	if (hasBiosGeometry) {
158 		dprintf("dosfs: bios geometry: %" B_PRIu32 " heads, "
159 			"%" B_PRIu32 " cylinders, "
160 			"%" B_PRIu32 " sectors/track, "
161 			"%" B_PRIu32 " bytes/sector\n",
162 			biosGeometry.head_count,
163 			biosGeometry.cylinder_count,
164 			biosGeometry.sectors_per_track,
165 			biosGeometry.bytes_per_sector);
166 	}
167 	if (hasBiosGeometry) {
168 		dprintf("dosfs: device geometry: %" B_PRIu32 " heads, "
169 			"%" B_PRIu32 " cylinders, "
170 			"%" B_PRIu32 " sectors/track, "
171 			"%" B_PRIu32 " bytes/sector\n",
172 			deviceGeometry.head_count,
173 			deviceGeometry.cylinder_count,
174 			deviceGeometry.sectors_per_track,
175 			deviceGeometry.bytes_per_sector);
176 	}
177 	if (hasPartitionInfo) {
178 		dprintf("dosfs: partition info: start at %" B_PRIdOFF " bytes "
179 			"(%" B_PRIdOFF " sectors), "
180 			"%" B_PRIdOFF " KB, "
181 			"%" B_PRIdOFF " MB, "
182 			"%" B_PRIdOFF " GB\n",
183 			partitionInfo.offset,
184 			partitionInfo.offset / 512,
185 			partitionInfo.offset / 1024,
186 			partitionInfo.offset / (1024 * 1024),
187 			partitionInfo.offset / (1024 * 1024 * 1024));
188 		dprintf("dosfs: partition info: size %" B_PRIdOFF " bytes, "
189 			"%" B_PRIdOFF " KB, "
190 			"%" B_PRIdOFF " MB, "
191 			"%" B_PRIdOFF " GB\n",
192 			partitionInfo.size,
193 			partitionInfo.size / 1024,
194 			partitionInfo.size / (1024 * 1024),
195 			partitionInfo.size / (1024 * 1024 * 1024));
196 	}
197 
198 	if (!isRawDevice && !hasPartitionInfo) {
199 		dprintf("dosfs Warning: couldn't get partition information\n");
200 	}
201 	if ((hasBiosGeometry && biosGeometry.bytes_per_sector != 512)
202 		||	(hasDeviceGeometry && deviceGeometry.bytes_per_sector != 512)) {
203 		dprintf("dosfs Error: geometry block size not 512 bytes\n");
204 		return B_ERROR;
205 	} else if (hasPartitionInfo && partitionInfo.logical_block_size != 512) {
206 		dprintf("dosfs: partition logical block size is not 512, "
207 			"it's %" B_PRId32 " bytes\n",
208 			partitionInfo.logical_block_size);
209 	}
210 
211 	if (hasDeviceGeometry && deviceGeometry.read_only) {
212 		dprintf("dosfs Error: this is a read-only device\n");
213 		return B_ERROR;
214 	}
215 	if (hasDeviceGeometry && deviceGeometry.write_once) {
216 		dprintf("dosfs Error: this is a write-once device\n");
217 		return B_ERROR;
218 	}
219 	uint64 size = 0;
220 
221 	if (hasPartitionInfo) {
222 		size = partitionInfo.size;
223 	} else if (hasDeviceGeometry) {
224 		size = uint64(deviceGeometry.bytes_per_sector) * deviceGeometry.sectors_per_track * deviceGeometry.cylinder_count * deviceGeometry.head_count;
225 	} else if (hasBiosGeometry) {
226 		size = uint64(biosGeometry.bytes_per_sector) * biosGeometry.sectors_per_track * biosGeometry.cylinder_count * biosGeometry.head_count;
227 	} else {
228 		// maybe it's just a file
229 		struct stat stat;
230 		if (fstat(fd, &stat) < 0) {
231 			dprintf( "dosfs Error: couldn't get device partition or geometry information, nor size\n");
232 			return B_ERROR;
233 		}
234 		size = stat.st_size;
235 	}
236 
237 	// TODO still valid on Haiku ?
238 	/*if (isRawDevice && size > FLOPPY_MAX_SIZE) {
239 		dprintf("Error: device too large for floppy, or raw devices not supported\n");
240 		close(fd);
241 		return B_ERROR;
242 	}*/
243 
244 	dprintf("dosfs: size = %" B_PRIu64 " bytes "
245 		"(%" B_PRIu64 " sectors), "
246 		"%" B_PRIu64 " KB, "
247 		"%" B_PRIu64 " MB, "
248 		"%" B_PRIu64 " GB\n",
249 		size,
250 		size / 512,
251 		size / 1024,
252 		size / (1024 * 1024),
253 		size / (1024 * 1024 * 1024));
254 
255 	if (fatbits == 0) {
256 		//auto determine fat type
257 		if (isRawDevice && size <= FLOPPY_MAX_SIZE && (size / FAT12_CLUSTER_MAX_SIZE) < FAT12_MAX_CLUSTER_COUNT) {
258 			fatbits = 12;
259 		} else if ((size / CLUSTER_MAX_SIZE) < FAT16_MAX_CLUSTER_COUNT) {
260 			fatbits = 16;
261 		} else if ((size / CLUSTER_MAX_SIZE) < FAT32_MAX_CLUSTER_COUNT) {
262 			fatbits = 32;
263 		}
264 	}
265 
266 	if (fatbits == 0) {
267 		dprintf("dosfs Error: device too large for 32 bit fat\n");
268 		return B_ERROR;
269 	}
270 
271 	int sectorPerCluster;
272 
273 	sectorPerCluster = 0;
274 	if (fatbits == 12) {
275 		sectorPerCluster = 0;
276 		if (size <= 4182016LL)
277 			sectorPerCluster = 2;	// XXX don't know the correct value
278 		if (size <= 2091008LL)
279 			sectorPerCluster = 1;	// XXX don't know the correct value
280 	} else if (fatbits == 16) {
281 		// special BAD_CLUSTER value is 0xFFF7,
282 		// but this should work anyway, since space required by
283 		// two FATs will make maximum cluster count smaller.
284 		// at least, this is what I think *should* happen
285 		sectorPerCluster = 0;				//larger than 2 GB must fail
286 		if (size <= (2048 * 1024 * 1024LL))	// up to 2GB, use 32k clusters
287 			sectorPerCluster = 64;
288 		if (size <= (1024 * 1024 * 1024LL))	// up to 1GB, use 16k clusters
289 			sectorPerCluster = 32;
290 		if (size <= (512 * 1024 * 1024LL))	// up to 512MB, use 8k clusters
291 			sectorPerCluster = 16;
292 		if (size <= (256 * 1024 * 1024LL))	// up to 256MB, use 4k clusters
293 			sectorPerCluster = 8;
294 		if (size <= (128 * 1024 * 1024LL))	// up to 128MB, use 2k clusters
295 			sectorPerCluster = 4;
296 		if (size <= (16 * 1024 * 1024LL))	// up to 16MB, use 2k clusters
297 			sectorPerCluster = 2;
298 		if (size <= 4182016LL)				// smaller than fat32 must fail
299 			sectorPerCluster = 0;
300 	} if (fatbits == 32) {
301 		sectorPerCluster = 64;				// default is 32k clusters
302 		if (size <= (32 * 1024 * 1024 * 1024LL))	// up to 32GB, use 16k clusters
303 			sectorPerCluster = 32;
304 		if (size <= (16 * 1024 * 1024 * 1024LL))	// up to 16GB, use 8k clusters
305 			sectorPerCluster = 16;
306 		if (size <= (8 * 1024 * 1024 * 1024LL))		// up to 8GB, use 4k clusters
307 			sectorPerCluster = 8;
308 		if (size <= (532480 * 512LL))				// up to 260 MB, use 0.5k clusters
309 			sectorPerCluster = 1;
310 		if (size <= (66600 * 512LL))		// smaller than 32.5 MB must fail
311 			sectorPerCluster = 0;
312 	}
313 
314 	if (sectorPerCluster == 0) {
315 		dprintf("dosfs Error: failed to determine sector per cluster value, partition too large for %d bit fat\n",fatbits);
316 		return B_ERROR;
317 	}
318 
319 	int reservedSectorCount = 0; // avoid compiler warning
320 	int rootEntryCount = 0; // avoid compiler warning
321 	int numFATs;
322 	int sectorSize;
323 	uint8 biosDriveId;
324 
325 	// get bios drive-id, or use 0x80
326 	if (B_OK != ioctl(fd, B_GET_BIOS_DRIVE_ID, &biosDriveId, sizeof(biosDriveId))) {
327 		biosDriveId = 0x80;
328 	} else {
329 		dprintf("dosfs: bios drive id: 0x%02x\n", (int)biosDriveId);
330 	}
331 
332 	// default parameters for the bootsector
333 	numFATs = 2;
334 	sectorSize = 512;
335 	if (fatbits == 12 || fatbits == 16)
336 		reservedSectorCount = 1;
337 	if (fatbits == 32)
338 		reservedSectorCount = 32;
339 	if (fatbits == 12)
340 		rootEntryCount = 128; // XXX don't know the correct value
341 	if (fatbits == 16)
342 		rootEntryCount = 512;
343 	if (fatbits == 32)
344 		rootEntryCount = 0;
345 
346 	// Determine FATSize
347 	// calculation done as MS recommends
348 	uint64 dskSize = size / sectorSize;
349 	uint32 rootDirSectors = ((rootEntryCount * 32) + (sectorSize - 1)) / sectorSize;
350 	uint64 tmpVal1 = dskSize - (reservedSectorCount + rootDirSectors);
351 	uint64 tmpVal2 = (256 * sectorPerCluster) + numFATs;
352 	if (fatbits == 32)
353 		tmpVal2 = tmpVal2 / 2;
354 	uint32 FATSize = (tmpVal1 + (tmpVal2 - 1)) / tmpVal2;
355 	// FATSize should now contain the size of *one* FAT, measured in sectors
356 	// RootDirSectors should now contain the size of the fat12/16 root directory, measured in sectors
357 
358 	dprintf("dosfs: fatbits = %d, clustersize = %d\n", fatbits, sectorPerCluster * 512);
359 	dprintf("dosfs: FAT size is %" B_PRIu32 " sectors\n", FATSize);
360 	dprintf("dosfs: disk label: %s\n", label);
361 
362 
363 
364 	if (status < B_OK) {
365 		dprintf("dosfs: Initializing volume failed: %s\n", strerror(status));
366 		return status;
367 	}
368 
369 	char bootsector[512];
370 	memset(bootsector,0x00,512);
371 	memcpy(bootsector + BOOTJMP_START_OFFSET, bootjmp, sizeof(bootjmp));
372 	memcpy(bootsector + BOOTCODE_START_OFFSET, bootcode, sizeof(bootcode));
373 
374 	if (fatbits == 32) {
375 		bootsector32 *bs = (bootsector32 *)bootsector;
376 		uint16 temp16;
377 		uint32 temp32;
378 		memcpy(bs->BS_OEMName,"Haiku   ",8);
379 		bs->BPB_BytsPerSec = B_HOST_TO_LENDIAN_INT16(sectorSize);
380 		bs->BPB_SecPerClus = sectorPerCluster;
381 		bs->BPB_RsvdSecCnt = B_HOST_TO_LENDIAN_INT16(reservedSectorCount);
382 		bs->BPB_NumFATs = numFATs;
383 		bs->BPB_RootEntCnt = B_HOST_TO_LENDIAN_INT16(rootEntryCount);
384 		bs->BPB_TotSec16 = B_HOST_TO_LENDIAN_INT16(0);
385 		bs->BPB_Media = 0xF8;
386 		bs->BPB_FATSz16 = B_HOST_TO_LENDIAN_INT16(0);
387 		temp16 = hasBiosGeometry ? biosGeometry.sectors_per_track : 63;
388 		bs->BPB_SecPerTrk = B_HOST_TO_LENDIAN_INT16(temp16);
389 		temp16 = hasBiosGeometry ? biosGeometry.head_count : 255;
390 		bs->BPB_NumHeads = B_HOST_TO_LENDIAN_INT16(temp16);
391 		temp32 = hasPartitionInfo ? (partitionInfo.size / 512) : 0;
392 		bs->BPB_HiddSec = B_HOST_TO_LENDIAN_INT32(temp32);
393 		temp32 = size / 512;
394 		bs->BPB_TotSec32 = B_HOST_TO_LENDIAN_INT32(temp32);
395 		bs->BPB_FATSz32 = B_HOST_TO_LENDIAN_INT32(FATSize);
396 		bs->BPB_ExtFlags = B_HOST_TO_LENDIAN_INT16(0);
397 		bs->BPB_FSVer = B_HOST_TO_LENDIAN_INT16(0);
398 		bs->BPB_RootClus = B_HOST_TO_LENDIAN_INT32(FAT32_ROOT_CLUSTER);
399 		bs->BPB_FSInfo = B_HOST_TO_LENDIAN_INT16(FSINFO_SECTOR_NUM);
400 		bs->BPB_BkBootSec = B_HOST_TO_LENDIAN_INT16(BACKUP_SECTOR_NUM);
401 		memset(bs->BPB_Reserved,0,12);
402 		bs->BS_DrvNum = biosDriveId;
403 		bs->BS_Reserved1 = 0x00;
404 		bs->BS_BootSig = 0x29;
405 		*(uint32*)bs->BS_VolID = (uint32)system_time();
406 		memcpy(bs->BS_VolLab,"NO NAME    ",11);
407 		memcpy(bs->BS_FilSysType,"FAT32   ",8);
408 		bs->signature = B_HOST_TO_LENDIAN_INT16(0xAA55);
409 	} else {
410 		bootsector1216 *bs = (bootsector1216 *)bootsector;
411 		uint16 temp16;
412 		uint32 temp32;
413 		uint32 sectorcount = size / 512;
414 		memcpy(bs->BS_OEMName, "Haiku   ", 8);
415 		bs->BPB_BytsPerSec = B_HOST_TO_LENDIAN_INT16(sectorSize);
416 		bs->BPB_SecPerClus = sectorPerCluster;
417 		bs->BPB_RsvdSecCnt = B_HOST_TO_LENDIAN_INT16(reservedSectorCount);
418 		bs->BPB_NumFATs = numFATs;
419 		bs->BPB_RootEntCnt = B_HOST_TO_LENDIAN_INT16(rootEntryCount);
420 		temp16 = (sectorcount <= 65535) ? sectorcount : 0;
421 		bs->BPB_TotSec16 = B_HOST_TO_LENDIAN_INT16(temp16);
422 		bs->BPB_Media = 0xF8;
423 		bs->BPB_FATSz16 = B_HOST_TO_LENDIAN_INT16(FATSize);
424 		temp16 = hasBiosGeometry ? biosGeometry.sectors_per_track : 63;
425 		bs->BPB_SecPerTrk = B_HOST_TO_LENDIAN_INT16(temp16);
426 		temp16 = hasBiosGeometry ? biosGeometry.head_count : 255;
427 		bs->BPB_NumHeads = B_HOST_TO_LENDIAN_INT16(temp16);
428 		temp32 = hasPartitionInfo ? (partitionInfo.size / 512) : 0;
429 		bs->BPB_HiddSec = B_HOST_TO_LENDIAN_INT32(temp32);
430 		temp32 = (sectorcount <= 65535) ? 0 : sectorcount;
431 		bs->BPB_TotSec32 = B_HOST_TO_LENDIAN_INT32(temp32);
432 		bs->BS_DrvNum = biosDriveId;
433 		bs->BS_Reserved1 = 0x00;
434 		bs->BS_BootSig = 0x29;
435 		*(uint32*)bs->BS_VolID = (uint32)system_time();
436 		memcpy(bs->BS_VolLab,"NO NAME    ",11);
437 		memcpy(bs->BS_FilSysType,(fatbits == 12) ? "FAT12   " : "FAT16   ",8);
438 		bs->signature = B_HOST_TO_LENDIAN_INT16(0xAA55);
439 	}
440 
441 	// Disk layout:
442 	// 0) reserved sectors, this includes the bootsector, fsinfosector and bootsector backup
443 	// 1) FAT
444 	// 2) root directory (not on fat32)
445 	// 3) file & directory data
446 
447 	ssize_t written;
448 
449 	// initialize everything with zero first
450 	// avoid doing 512 byte writes here, they are slow
451 	dprintf("dosfs: Writing FAT\n");
452 	char * zerobuffer = (char *)malloc(65536);
453 	memset(zerobuffer,0,65536);
454 	int64 bytes_to_write = 512LL * (reservedSectorCount + (numFATs * FATSize) + rootDirSectors);
455 	int64 pos = 0;
456 	while (bytes_to_write > 0) {
457 		ssize_t writesize = min_c(bytes_to_write, 65536);
458 		written = write_pos(fd, pos, zerobuffer, writesize);
459 		if (written != writesize) {
460 			dprintf("dosfs Error: write error near sector %" B_PRId64 "\n",
461 				pos / 512);
462 			free(zerobuffer);
463 			return B_ERROR;
464 		}
465 		bytes_to_write -= writesize;
466 		pos += writesize;
467 	}
468 	free(zerobuffer);
469 
470 	//write boot sector
471 	dprintf("dosfs: Writing boot block\n");
472 	written = write_pos(fd, BOOT_SECTOR_NUM * 512, bootsector, 512);
473 	if (written != 512) {
474 		dprintf("dosfs Error: write error at sector %d\n", BOOT_SECTOR_NUM);
475 		return B_ERROR;
476 	}
477 
478 	if (fatbits == 32) {
479 		written = write_pos(fd, BACKUP_SECTOR_NUM * 512, bootsector, 512);
480 		if (written != 512) {
481 			dprintf("dosfs Error: write error at sector %d\n", BACKUP_SECTOR_NUM);
482 			return B_ERROR;
483 		}
484 	}
485 
486 	//write first fat sector
487 	dprintf("dosfs: Writing first FAT sector\n");
488 	uint8 sec[512];
489 	memset(sec,0,512);
490 	if (fatbits == 12) {
491 		//FAT[0] contains media byte in lower 8 bits, all other bits set to 1
492 		//FAT[1] contains EOF marker
493 		sec[0] = 0xF8;
494 		sec[1] = 0xFF;
495 		sec[2] = 0xFF;
496 	} else if (fatbits == 16) {
497 		//FAT[0] contains media byte in lower 8 bits, all other bits set to 1
498 		sec[0] = 0xF8;
499 		sec[1] = 0xFF;
500 		//FAT[1] contains EOF marker
501 		sec[2] = 0xFF;
502 		sec[3] = 0xFF;
503 	} else if (fatbits == 32) {
504 		//FAT[0] contains media byte in lower 8 bits, all other bits set to 1
505 		sec[0] = 0xF8;
506 		sec[1] = 0xFF;
507 		sec[2] = 0xFF;
508 		sec[3] = 0xFF;
509 		//FAT[1] contains EOF marker
510 		sec[4] = 0xFF;
511 		sec[5] = 0xFF;
512 		sec[6] = 0xFF;
513 		sec[7] = 0x0F;
514 		//FAT[2] contains EOF marker, used to terminate root directory
515 		sec[8] = 0xFF;
516 		sec[9] = 0xFF;
517 		sec[10] = 0xFF;
518 		sec[11] = 0x0F;
519 	}
520 	written = write_pos(fd, reservedSectorCount * 512, sec, 512);
521 	if (written != 512) {
522 		dprintf("dosfs Error: write error at sector %d\n", reservedSectorCount);
523 		return B_ERROR;
524 	}
525 	if (numFATs > 1) {
526 		written = write_pos(fd, (reservedSectorCount + FATSize) * 512,sec,512);
527 		if (written != 512) {
528 			dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n",
529 				reservedSectorCount + FATSize);
530 			return B_ERROR;
531 		}
532 	}
533 
534 	//write fsinfo sector
535 	if (fatbits == 32) {
536 		dprintf("dosfs: Writing boot info\n");
537 		//calculate total sector count first
538 		uint64 free_count = size / 512;
539 		//now account for already by metadata used sectors
540 		free_count -= reservedSectorCount + (numFATs * FATSize) + rootDirSectors;
541 		//convert from sector to clustercount
542 		free_count /= sectorPerCluster;
543 		//and account for 1 already used cluster of root directory
544 		free_count -= 1;
545 		fsinfosector32 fsinfosector;
546 		memset(&fsinfosector,0x00,512);
547 		fsinfosector.FSI_LeadSig 	= B_HOST_TO_LENDIAN_INT32(0x41615252);
548 		fsinfosector.FSI_StrucSig 	= B_HOST_TO_LENDIAN_INT32(0x61417272);
549 		fsinfosector.FSI_Free_Count = B_HOST_TO_LENDIAN_INT32((uint32)free_count);
550 		fsinfosector.FSI_Nxt_Free 	= B_HOST_TO_LENDIAN_INT32(3);
551 		fsinfosector.FSI_TrailSig 	= B_HOST_TO_LENDIAN_INT32(0xAA550000);
552 		written = write_pos(fd, FSINFO_SECTOR_NUM * 512, &fsinfosector, 512);
553 		if (written != 512) {
554 			dprintf("dosfs Error: write error at sector %d\n", FSINFO_SECTOR_NUM);
555 			return B_ERROR;
556 		}
557 	}
558 
559 	//write volume label into root directory
560 	dprintf("dosfs: Writing root directory\n");
561 	if (fatbits == 12 || fatbits == 16) {
562 		uint8 data[512];
563 		memset(data, 0, 512);
564 		CreateVolumeLabel(data, label);
565 		uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize);
566 		written = write_pos(fd, rootDirSector * 512, data, 512);
567 		if (written != 512) {
568 			dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n",
569 				rootDirSector);
570 			return B_ERROR;
571 		}
572 	} else if (fatbits == 32) {
573 		int size = 512 * sectorPerCluster;
574 		uint8 *cluster = (uint8*)malloc(size);
575 		memset(cluster, 0, size);
576 		CreateVolumeLabel(cluster, label);
577 		uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize) + rootDirSectors;
578 		written = write_pos(fd, rootDirSector * 512, cluster, size);
579 		free(cluster);
580 		if (written != size) {
581 			dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n",
582 				rootDirSector);
583 			return B_ERROR;
584 		}
585 	}
586 
587 	ioctl(fd, B_FLUSH_DRIVE_CACHE);
588 
589 
590 
591 	// rescan partition
592 	status = scan_partition(partitionID);
593 	if (status != B_OK)
594 		return status;
595 
596 	update_disk_device_job_progress(job, 1);
597 
598 	// print some info, if desired
599 	if (parameters.verbose) {
600 //		disk_super_block super = volume.SuperBlock();
601 
602 		dprintf("dosfs: Disk was initialized successfully.\n");
603 /*
604 		dprintf("\tname: \"%s\"\n", super.name);
605 		dprintf("\tnum blocks: %" B_PRIdOFF "\n", super.NumBlocks());
606 		dprintf("\tused blocks: %" B_PRIdOFF "\n", super.UsedBlocks());
607 		dprintf("\tblock size: %u bytes\n", (unsigned)super.BlockSize());
608 		dprintf("\tnum allocation groups: %d\n",
609 			(int)super.AllocationGroups());
610 		dprintf("\tallocation group size: %ld blocks\n",
611 			1L << super.AllocationGroupShift());
612 		dprintf("\tlog size: %u blocks\n", super.log_blocks.Length());
613 */
614 	}
615 
616 	return B_OK;
617 }
618 
619 
620 status_t
621 dosfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
622 	uint32 blockSize, disk_job_id job)
623 {
624 	if (blockSize == 0)
625 		return B_BAD_VALUE;
626 
627 	update_disk_device_job_progress(job, 0.0);
628 
629 	// just overwrite the superblock
630 	// XXX: we might want to keep the loader part ?
631 	char bootsector[512];
632 	memset(bootsector,0x00,512);
633 
634 	if (write_pos(fd, 512, bootsector, sizeof(512)) < 0)
635 		return errno;
636 
637 	update_disk_device_job_progress(job, 1.0);
638 
639 	return B_OK;
640 }
641 
642 
643 //	#pragma mark -
644 
645 
646 #if 0
647 int
648 main(int argc, char *argv[])
649 {
650 	if (sizeof(bootsector1216) != 512 || sizeof(bootsector32) != 512 || sizeof(fsinfosector32) != 512) {
651 		dprintf("compilation error: struct alignment wrong\n");
652 		return 1;
653 	}
654 
655 	const char *device = NULL;
656 	const char *label = NULL;
657 	bool noprompt = false;
658 	bool test = false;
659 	int fat = 0;
660 
661 	while (1) {
662 		int c;
663 		int option_index = 0;
664 		static struct option long_options[] =
665 		{
666 		 	{"noprompt", no_argument, 0, 'n'},
667 			{"test", no_argument, 0, 't'},
668 			{"fat", required_argument, 0, 'f'},
669 			{0, 0, 0, 0}
670 		};
671 
672 		c = getopt_long (argc, argv, "ntf:", long_options, &option_index);
673 		if (c == -1)
674 			break;
675 
676 		switch (c) {
677 			case 'n':
678 				noprompt = true;
679 		 		break;
680 
681 			case 't':
682 				test = true;
683 				break;
684 
685 			case 'f':
686 				fat = strtol(optarg, NULL, 10);
687 				if (fat == 0)
688 					fat = -1;
689 				break;
690 
691 			default:
692 		        printf("\n");
693 				PrintUsage();
694 				return 1;
695 		}
696 	}
697 
698 	if (optind < argc)
699 		device = argv[optind];
700 	if ((optind + 1) < argc)
701 		label = argv[optind + 1];
702 
703 	if (fat != 0 && fat != 12 && fat != 16 && fat != 32) {
704 		printf("mkdos error: fat must be 12, 16, or 32 bits\n");
705 		PrintUsage();
706 		return 1;
707 	}
708 
709 	if (device == NULL) {
710 		printf("mkdos error: you must specify a device or partition or image\n");
711         printf("             such as /dev/disk/ide/ata/1/master/0/0_0\n");
712 		PrintUsage();
713 		return 1;
714 	}
715 
716 	if (label == NULL) {
717 		label = "no name";
718 	}
719 
720 	if (noprompt)
721 		printf("will not prompt for confirmation\n");
722 
723 	if (test)
724 		printf("test mode enabled (no writes will occur)\n");
725 
726 	status_t s;
727 	s = Initialize(fat, device, label, noprompt, test);
728 
729 	if (s != 0) {
730 		printf("Initializing failed!\n");
731 	}
732 
733 	return (s == B_OK) ? 0 : 1;
734 }
735 #endif
736 
737 
738 void CreateVolumeLabel(void *sector, const char *label)
739 {
740 	// create a volume name directory entry in the 512 byte sector
741 	// XXX convert from UTF8, and check for valid characters
742 	// XXX this could be changed to use long file name entrys,
743 	// XXX but the dosfs would have to be updated, too
744 
745 	dirent *d = (dirent *)sector;
746 	memset(d, 0, sizeof(*d));
747 	memset(d->Name, 0x20, 11);
748 	memcpy(d->Name, label, min_c(11, strlen(label)));
749 	d->Attr = 0x08;
750 }
751