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