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