1/*
2	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5
6
7#include "fat.h"
8
9#include "system_dependencies.h"
10
11#include "dosfs.h"
12#include "file.h"
13#include "util.h"
14#include "vcache.h"
15
16
17#define END_FAT_ENTRY 0x0fffffff
18#define BAD_FAT_ENTRY 0x0ffffff1
19
20#define DPRINTF(a,b) if (debug_fat > (a)) dprintf b
21
22
23static status_t
24mirror_fats(nspace *vol, uint32 sector, uint8 *buffer)
25{
26	uint32 i;
27
28	if (!vol->fat_mirrored)
29		return B_OK;
30
31	sector -= vol->active_fat * vol->sectors_per_fat;
32
33	for (i = 0; i < vol->fat_count; i++) {
34		char *blockData;
35		status_t status;
36		if (i == vol->active_fat)
37			continue;
38
39		status = block_cache_get_writable_etc(vol->fBlockCache,
40			sector + i * vol->sectors_per_fat, 0, 1, -1,
41			(void**)&blockData);
42		if (status != B_OK)
43			return status;
44
45		memcpy(blockData, buffer, vol->bytes_per_sector);
46		block_cache_put(vol->fBlockCache, sector + i * vol->sectors_per_fat);
47	}
48
49	return B_OK;
50}
51
52
53static int32
54_count_free_clusters_fat32(nspace *vol)
55{
56	int32 count = 0;
57	const uint8 *block;
58	uint32 fat_sector;
59	uint32 i;
60	uint32 cur_sector;
61
62	cur_sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat;
63
64	for (fat_sector = 0; fat_sector < vol->sectors_per_fat; fat_sector++) {
65		block = (uint8 *)block_cache_get(vol->fBlockCache, cur_sector);
66		if(block == NULL)
67			return B_IO_ERROR;
68
69		for (i = 0; i < vol->bytes_per_sector; i += sizeof(uint32)) {
70			uint32 val = read32(block, i);
71			if ((val & 0x0fffffff) == 0)
72				count++;
73		}
74
75		block_cache_put(vol->fBlockCache, cur_sector);
76		cur_sector++;
77	}
78
79	return count;
80}
81
82// count free: no parameters. returns int32
83// get_entry: cluster #. returns int32 entry/status
84// set_entry: cluster #, value. returns int32 status
85// allocate: # clusters in N, returns int32 status/starting cluster
86
87enum {
88	_IOCTL_COUNT_FREE_,
89	_IOCTL_GET_ENTRY_,
90	_IOCTL_SET_ENTRY_,
91	_IOCTL_ALLOCATE_N_ENTRIES_
92};
93
94static int32
95_fat_ioctl_(nspace *vol, uint32 action, uint32 cluster, uint32 N)
96{
97	int32 result = 0;
98	uint32 n = 0, first = 0, last = 0;
99	uint32 i;
100	uint32 sector;
101	uint32 offset, value = 0; /* quiet warning */
102	uint8 *block1, *block2 = NULL; /* quiet warning */
103	bool readOnly
104		= action != _IOCTL_SET_ENTRY_ && action != _IOCTL_ALLOCATE_N_ENTRIES_;
105
106	// mark end of chain for allocations
107	uint32 endOfChainMarker = (action == _IOCTL_SET_ENTRY_) ? N : 0x0fffffff;
108
109	ASSERT(action >= _IOCTL_COUNT_FREE_
110		&& action <= _IOCTL_ALLOCATE_N_ENTRIES_);
111
112	DPRINTF(3, ("_fat_ioctl_: action %" B_PRIu32 ", cluster %" B_PRIu32
113		", N %" B_PRId32 "\n", action, cluster, N));
114
115	if (action == _IOCTL_COUNT_FREE_) {
116		if(vol->fat_bits == 32)
117			// use a optimized version of the cluster counting algorithms
118			return _count_free_clusters_fat32(vol);
119		else
120			cluster = 2;
121	}
122
123	if (action == _IOCTL_ALLOCATE_N_ENTRIES_)
124		cluster = vol->last_allocated;
125
126	if (action != _IOCTL_COUNT_FREE_) {
127		if (!IS_DATA_CLUSTER(cluster)) {
128			DPRINTF(0, ("_fat_ioctl_ called with invalid cluster (%" B_PRIu32
129				")\n", cluster));
130			return B_BAD_VALUE;
131		}
132	}
133
134	offset = cluster * vol->fat_bits / 8;
135	sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat +
136		offset / vol->bytes_per_sector;
137	offset %= vol->bytes_per_sector;
138
139	if (readOnly) {
140		block1 = (uint8 *)block_cache_get(vol->fBlockCache, sector);
141	} else {
142		block1 = (uint8 *)block_cache_get_writable(vol->fBlockCache, sector,
143			-1);
144	}
145
146	if (block1 == NULL) {
147		DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %" B_PRIu32 ")\n",
148			sector));
149		return B_IO_ERROR;
150	}
151
152	for (i = 0; i < vol->total_clusters; i++) {
153		ASSERT(IS_DATA_CLUSTER(cluster));
154		ASSERT(offset == ((cluster * vol->fat_bits / 8)
155			% vol->bytes_per_sector));
156
157		if (vol->fat_bits == 12) {
158			if (offset == vol->bytes_per_sector - 1) {
159				if (readOnly) {
160					block2 = (uint8 *)block_cache_get(vol->fBlockCache,
161						++sector);
162				} else {
163					block2 = (uint8 *)block_cache_get_writable(vol->fBlockCache,
164						++sector, -1);
165				}
166
167				if (block2 == NULL) {
168					DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %"
169						B_PRIu32 ")\n", sector));
170					result = B_IO_ERROR;
171					sector--;
172					goto bi;
173				}
174			}
175
176			if (action != _IOCTL_SET_ENTRY_) {
177				if (offset == vol->bytes_per_sector - 1)
178					value = block1[offset] + 0x100 * block2[0];
179				else
180					value = block1[offset] + 0x100 * block1[offset + 1];
181
182				if (cluster & 1)
183					value >>= 4;
184				else
185					value &= 0xfff;
186
187				if (value > 0xff0)
188					value |= 0x0ffff000;
189			}
190
191			if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (value == 0))
192				|| action == _IOCTL_SET_ENTRY_) {
193				uint32 andmask, ormask;
194				if (cluster & 1) {
195					ormask = (endOfChainMarker & 0xfff) << 4;
196					andmask = 0xf;
197				} else {
198					ormask = endOfChainMarker & 0xfff;
199					andmask = 0xf000;
200				}
201
202				block1[offset] &= (andmask & 0xff);
203				block1[offset] |= (ormask & 0xff);
204				if (offset == vol->bytes_per_sector - 1) {
205					mirror_fats(vol, sector - 1, block1);
206					block2[0] &= (andmask >> 8);
207					block2[0] |= (ormask >> 8);
208				} else {
209					block1[offset + 1] &= (andmask >> 8);
210					block1[offset + 1] |= (ormask >> 8);
211				}
212			}
213
214			if (offset == vol->bytes_per_sector - 1) {
215				offset = (cluster & 1) ? 1 : 0;
216				block_cache_put(vol->fBlockCache, sector - 1);
217				block1 = block2;
218			} else
219				offset += (cluster & 1) ? 2 : 1;
220
221		} else if (vol->fat_bits == 16) {
222			if (action != _IOCTL_SET_ENTRY_) {
223				value = read16(block1, offset);
224				if (value > 0xfff0)
225					value |= 0x0fff0000;
226			}
227
228			if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (value == 0))
229				|| action == _IOCTL_SET_ENTRY_) {
230				*(uint16 *)&block1[offset]
231					= B_HOST_TO_LENDIAN_INT16(endOfChainMarker);
232			}
233
234			offset += 2;
235		} else if (vol->fat_bits == 32) {
236			if (action != _IOCTL_SET_ENTRY_)
237				value = read32(block1, offset) & 0x0fffffff;
238
239			if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (value == 0))
240				|| action == _IOCTL_SET_ENTRY_) {
241				ASSERT((endOfChainMarker & 0xf0000000) == 0);
242				*(uint32 *)&block1[offset]
243					= B_HOST_TO_LENDIAN_INT32(endOfChainMarker);
244			}
245
246			offset += 4;
247		} else
248			ASSERT(0);
249
250		if (action == _IOCTL_COUNT_FREE_) {
251			if (value == 0)
252				result++;
253		} else if (action == _IOCTL_GET_ENTRY_) {
254			result = value;
255			goto bi;
256		} else if (action == _IOCTL_SET_ENTRY_) {
257			mirror_fats(vol, sector, block1);
258			goto bi;
259		} else if (action == _IOCTL_ALLOCATE_N_ENTRIES_ && value == 0) {
260			vol->free_clusters--;
261			mirror_fats(vol, sector, block1);
262
263			if (n == 0) {
264				ASSERT(first == 0);
265				first = last = cluster;
266			} else {
267				ASSERT(IS_DATA_CLUSTER(first));
268				ASSERT(IS_DATA_CLUSTER(last));
269				// set last cluster to point to us
270
271				result = _fat_ioctl_(vol, _IOCTL_SET_ENTRY_, last, cluster);
272				if (result < 0) {
273					ASSERT(0);
274					goto bi;
275				}
276
277				last = cluster;
278			}
279
280			if (++n == N)
281				goto bi;
282		}
283
284		// iterate cluster and sector if needed
285		if (++cluster == vol->total_clusters + 2) {
286			block_cache_put(vol->fBlockCache, sector);
287
288			cluster = 2;
289			offset = cluster * vol->fat_bits / 8;
290			sector = vol->reserved_sectors + vol->active_fat
291				* vol->sectors_per_fat;
292
293			if (readOnly)
294				block1 = (uint8 *)block_cache_get(vol->fBlockCache, sector);
295			else {
296				block1 = (uint8 *)block_cache_get_writable(vol->fBlockCache,
297					sector, -1);
298			}
299		}
300
301		if (offset >= vol->bytes_per_sector) {
302			block_cache_put(vol->fBlockCache, sector);
303
304			sector++;
305			offset -= vol->bytes_per_sector;
306			ASSERT(sector < vol->reserved_sectors + (vol->active_fat + 1)
307				* vol->sectors_per_fat);
308
309			if (readOnly)
310				block1 = (uint8 *)block_cache_get(vol->fBlockCache, sector);
311			else {
312				block1 = (uint8 *)block_cache_get_writable(vol->fBlockCache,
313					sector, -1);
314			}
315		}
316
317		if (block1 == NULL) {
318			DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %" B_PRIu32
319				")\n", sector));
320			result = B_IO_ERROR;
321			goto bi;
322		}
323	}
324
325bi:
326	if (block1 != NULL)
327		block_cache_put(vol->fBlockCache, sector);
328
329	if (action == _IOCTL_ALLOCATE_N_ENTRIES_) {
330		if (result < 0) {
331			DPRINTF(0, ("pooh. there is a problem. clearing chain (%" B_PRIu32
332				")\n", first));
333			if (first != 0)
334				clear_fat_chain(vol, first, false);
335		} else if (n != N) {
336			DPRINTF(0, ("not enough free entries (%" B_PRId32 "/%" B_PRId32
337				" found)\n", n, N));
338			if (first != 0)
339				clear_fat_chain(vol, first, false);
340			result = B_DEVICE_FULL;
341		} else if (result == 0) {
342			vol->last_allocated = cluster;
343			result = first;
344			ASSERT(IS_DATA_CLUSTER(first));
345		}
346	}
347
348	if (result < B_OK) {
349		DPRINTF(0, ("_fat_ioctl_ error: action = %" B_PRIu32 " cluster = %"
350			B_PRIu32 " N = %" B_PRId32 " (%s)\n", action, cluster, N,
351			strerror(result)));
352	}
353
354	return result;
355}
356
357
358int32
359count_free_clusters(nspace *vol)
360{
361	return _fat_ioctl_(vol, _IOCTL_COUNT_FREE_, 0, 0);
362}
363
364
365static int32
366get_fat_entry(nspace *vol, uint32 cluster)
367{
368	int32 value = _fat_ioctl_(vol, _IOCTL_GET_ENTRY_, cluster, 0);
369
370	if (value < 0)
371		return value;
372
373	if (value == 0 || IS_DATA_CLUSTER(value))
374		return value;
375
376	if (value > 0x0ffffff7)
377		return END_FAT_ENTRY;
378
379	if (value > 0x0ffffff0)
380		return BAD_FAT_ENTRY;
381
382	DPRINTF(0, ("invalid fat entry: %" B_PRIu32 "\n", value));
383	return BAD_FAT_ENTRY;
384}
385
386
387static status_t
388set_fat_entry(nspace *vol, uint32 cluster, int32 value)
389{
390	return _fat_ioctl_(vol, _IOCTL_SET_ENTRY_, cluster, value);
391}
392
393
394//! Traverse n fat entries
395int32
396get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n)
397{
398	while (n--) {
399		cluster = get_fat_entry(vol, cluster);
400
401		if (!IS_DATA_CLUSTER(cluster))
402			break;
403	}
404
405	ASSERT(cluster != 0);
406	return cluster;
407}
408
409
410// count number of clusters in fat chain starting at given cluster
411// should only be used for calculating directory sizes because it doesn't
412// return proper error codes
413uint32
414count_clusters(nspace *vol, int32 cluster)
415{
416	uint32 count = 0;
417
418	DPRINTF(2, ("count_clusters %" B_PRId32 "\n", cluster));
419
420	// not intended for use on root directory
421	if (!IS_DATA_CLUSTER(cluster)) {
422		DPRINTF(0, ("count_clusters called on invalid cluster (%" B_PRId32
423			")\n", cluster));
424		return 0;
425	}
426
427	while (IS_DATA_CLUSTER(cluster)) {
428		count++;
429
430		// break out of circular fat chains in a sketchy manner
431		if (count == vol->total_clusters)
432			return 0;
433
434		cluster = get_fat_entry(vol, cluster);
435	}
436
437	DPRINTF(2, ("count_clusters %" B_PRId32 " = %" B_PRId32 "\n", cluster,
438		count));
439
440	if (cluster == END_FAT_ENTRY)
441		return count;
442
443	dprintf("cluster = %" B_PRId32 "\n", cluster);
444	ASSERT(0);
445	return 0;
446}
447
448
449status_t
450clear_fat_chain(nspace *vol, uint32 cluster, bool discardBlockCache)
451{
452	int32 c;
453	status_t result;
454
455	if (!IS_DATA_CLUSTER(cluster)) {
456		DPRINTF(0, ("clear_fat_chain called on invalid cluster (%" B_PRIu32
457			")\n", cluster));
458		return B_BAD_VALUE;
459	}
460
461	ASSERT(count_clusters(vol, cluster) != 0);
462
463	DPRINTF(2, ("clearing fat chain: %" B_PRIu32, cluster));
464	while (IS_DATA_CLUSTER(cluster)) {
465		if ((c = get_fat_entry(vol, cluster)) < 0) {
466			DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster "
467				"%" B_PRIu32 " (%s)\n", cluster, strerror(c)));
468			return c;
469		}
470
471		if ((result = set_fat_entry(vol, cluster, 0)) != B_OK) {
472			DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster "
473				"%" B_PRIu32 " (%s)\n", cluster, strerror(result)));
474			return result;
475		}
476
477		if (discardBlockCache) {
478			block_cache_discard(vol->fBlockCache,
479				vol->data_start + (cluster - 2) * vol->sectors_per_cluster,
480				vol->sectors_per_cluster);
481		}
482
483		vol->free_clusters++;
484		cluster = c;
485		DPRINTF(2, (", %" B_PRIu32, cluster));
486	}
487	DPRINTF(2, ("\n"));
488
489	if (cluster != END_FAT_ENTRY) {
490		dprintf("clear_fat_chain: fat chain terminated improperly with %"
491			B_PRIu32 "\n", cluster);
492	}
493
494	return 0;
495}
496
497
498status_t
499allocate_n_fat_entries(nspace *vol, int32 n, int32 *start)
500{
501	int32 c;
502
503	ASSERT(n > 0);
504
505	DPRINTF(2, ("allocating %" B_PRId32 " fat entries\n", n));
506
507	c = _fat_ioctl_(vol, _IOCTL_ALLOCATE_N_ENTRIES_, 0, n);
508	if (c < 0)
509		return c;
510
511	ASSERT(IS_DATA_CLUSTER(c));
512	ASSERT(count_clusters(vol, c) == (uint32)n);
513
514	DPRINTF(2, ("allocated %" B_PRId32 " fat entries at %" B_PRId32 "\n", n,
515		c));
516
517	*start = c;
518	return 0;
519}
520
521
522status_t
523set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters,
524	bool discardBlockCache)
525{
526	status_t result;
527	int32 c, n;
528	uint32 i;
529
530	DPRINTF(1, ("set_fat_chain_length: %" B_PRIdINO " to %" B_PRIu32
531		" clusters (%" B_PRIu32 ")\n", node->vnid, clusters, node->cluster));
532
533	if (IS_FIXED_ROOT(node->cluster)
534		|| (!IS_DATA_CLUSTER(node->cluster) && (node->cluster != 0))) {
535		DPRINTF(0, ("set_fat_chain_length called on invalid cluster (%" B_PRIu32
536			")\n", node->cluster));
537		return B_BAD_VALUE;
538	}
539
540	if (clusters == 0) {
541		DPRINTF(1, ("truncating node to zero bytes\n"));
542		if (node->cluster == 0)
543			return B_OK;
544
545		c = node->cluster;
546		if ((result = clear_fat_chain(vol, c, discardBlockCache)) != B_OK)
547			return result;
548
549		node->cluster = 0;
550		node->end_cluster = 0;
551
552		// TODO: don't have to do this this way -- can clean up nicely
553		do {
554			result = vcache_set_entry(vol, node->vnid,
555				GENERATE_DIR_INDEX_VNID(node->dir_vnid, node->sindex));
556			// repeat until memory is freed up
557			if (result != B_OK)
558				snooze(5000LL);
559		} while (result != B_OK);
560
561		/* write to disk so that get_next_dirent doesn't barf */
562		write_vnode_entry(vol, node);
563		return result;
564	}
565
566	if (node->cluster == 0) {
567		DPRINTF(1, ("node has no clusters. adding %" B_PRIu32 " clusters\n",
568			clusters));
569
570		if ((result = allocate_n_fat_entries(vol, clusters, &n)) != B_OK)
571			return result;
572
573		node->cluster = n;
574		node->end_cluster = get_nth_fat_entry(vol, n, clusters - 1);
575
576		// TODO: don't have to do this this way -- can clean up nicely
577		do {
578			result = vcache_set_entry(vol, node->vnid,
579				GENERATE_DIR_CLUSTER_VNID(node->dir_vnid, node->cluster));
580			// repeat until memory is freed up
581			if (result != B_OK)
582				snooze(5000LL);
583		} while (result != B_OK);
584
585		/* write to disk so that get_next_dirent doesn't barf */
586		write_vnode_entry(vol, node);
587		return result;
588	}
589
590	i = (node->st_size + vol->bytes_per_sector * vol->sectors_per_cluster - 1)
591		/ vol->bytes_per_sector / vol->sectors_per_cluster;
592	if (i == clusters)
593		return B_OK;
594
595	if (clusters > i) {
596		// add new fat entries
597		DPRINTF(1, ("adding %" B_PRIu32 " new fat entries\n", clusters - i));
598		if ((result = allocate_n_fat_entries(vol, clusters - i, &n)) != B_OK)
599			return result;
600
601		ASSERT(IS_DATA_CLUSTER(n));
602
603		result = set_fat_entry(vol, node->end_cluster, n);
604		if (result < B_OK) {
605			clear_fat_chain(vol, n, false);
606			return result;
607		}
608
609		node->end_cluster = get_nth_fat_entry(vol, n, clusters - i - 1);
610		return result;
611	}
612
613	// traverse fat chain
614	c = node->cluster;
615	n = get_fat_entry(vol, c);
616	for (i = 1; i < clusters; i++) {
617		if (!IS_DATA_CLUSTER(n))
618			break;
619
620		c = n;
621		n = get_fat_entry(vol, c);
622	}
623
624	ASSERT(i == clusters);
625	ASSERT(n != END_FAT_ENTRY);
626
627	if (i == clusters && n == END_FAT_ENTRY)
628		return B_OK;
629
630	if (n < 0)
631		return n;
632
633	if (n != END_FAT_ENTRY && !IS_DATA_CLUSTER(n))
634		return B_BAD_VALUE;
635
636	// clear trailing fat entries
637	DPRINTF(1, ("clearing trailing fat entries\n"));
638	if ((result = set_fat_entry(vol, c, 0x0fffffff)) != B_OK)
639		return result;
640
641	node->end_cluster = c;
642	return clear_fat_chain(vol, n, discardBlockCache);
643}
644
645
646void
647dump_fat_chain(nspace *vol, uint32 cluster)
648{
649	dprintf("fat chain: %" B_PRIu32, cluster);
650	while (IS_DATA_CLUSTER(cluster)) {
651		cluster = get_fat_entry(vol, cluster);
652		dprintf(" %" B_PRIu32, cluster);
653	}
654
655	dprintf("\n");
656}
657
658/*
659status_t fragment(nspace *vol, uint32 *pattern)
660{
661	uint32 sector, offset, previous_entry, i, val;
662	uchar *buffer;
663	bool dirty = FALSE;
664
665	srand(time(NULL)|1);
666
667	if (vol->fat_bits == 16)
668		previous_entry = 0xffff;
669	else if (vol->fat_bits == 32)
670		previous_entry = 0x0fffffff;
671	else {
672		dprintf("fragment: only for FAT16 and FAT32\n");
673		return ENOSYS;
674	}
675
676	sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat +
677			((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) /
678					vol->bytes_per_sector;
679	offset = ((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) %
680			vol->bytes_per_sector;
681
682	buffer = (uchar *)get_block(vol->fd, sector, vol->bytes_per_sector);
683	if (!buffer) {
684		dprintf("fragment: error getting fat block %lx\n", sector);
685		return EINVAL;
686	}
687
688	val = pattern ? *pattern : rand();
689
690	for (i=vol->total_clusters+1;i>=2;i--) {
691		if (val & (1 << (i & 31))) {
692			if (vol->fat_bits == 16) {
693				if (read16(buffer, offset) == 0) {
694					buffer[offset+0] = (previous_entry     ) & 0xff;
695					buffer[offset+1] = (previous_entry >> 8) & 0xff;
696					previous_entry = i;
697					dirty = TRUE;
698					vol->free_clusters--;
699				}
700			} else {
701				if (read32(buffer, offset) == 0) {
702					buffer[offset+0] = (previous_entry      ) & 0xff;
703					buffer[offset+1] = (previous_entry >>  8) & 0xff;
704					buffer[offset+2] = (previous_entry >> 16) & 0xff;
705					buffer[offset+3] = (previous_entry >> 24) & 0xff;
706					previous_entry = i;
707					dirty = TRUE;
708					vol->free_clusters--;
709				}
710			}
711		}
712
713		if (!offset) {
714			if (dirty) {
715				mark_blocks_dirty(vol->fd, sector, 1);
716				mirror_fats(vol, sector, buffer);
717			}
718			release_block(vol->fd, sector);
719
720			dirty = FALSE;
721			sector--;
722
723			buffer = (uchar *)get_block(vol->fd, sector,
724					vol->bytes_per_sector);
725			if (!buffer) {
726				dprintf("fragment: error getting fat block %lx\n", sector);
727				return EINVAL;
728			}
729		}
730
731		offset = (offset - vol->fat_bits / 8 + vol->bytes_per_sector) %
732				vol->bytes_per_sector;
733
734		if (!pattern && ((i & 31) == 31))
735			val = rand();
736	}
737
738	if (dirty) {
739		mark_blocks_dirty(vol->fd, sector, 1);
740		mirror_fats(vol, sector, buffer);
741	}
742	release_block(vol->fd, sector);
743
744	vol->last_allocated = (rand() % vol->total_clusters) + 2;
745
746	return B_OK;
747}
748*/
749
750