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 "dosfs.h"
8
9#include "system_dependencies.h"
10
11#include "attr.h"
12#include "dir.h"
13#include "dlist.h"
14#include "fat.h"
15#include "file.h"
16#include "iter.h"
17#include "mkdos.h"
18#include "util.h"
19#include "vcache.h"
20
21
22extern const char *build_time, *build_date;
23
24/* debug levels */
25int debug_attr = 0, debug_dir = 0, debug_dlist = 0, debug_dosfs = 0,
26		debug_encodings = 0, debug_fat = 0, debug_file = 0,
27		debug_iter = 0, debug_vcache = 0;
28
29#define DPRINTF(a,b) if (debug_dosfs > (a)) dprintf b
30
31static status_t get_fsinfo(nspace *vol, uint32 *free_count, uint32 *last_allocated);
32
33
34#if DEBUG
35
36int32 instances = 0;
37
38static int
39debug_fat_nspace(int argc, char **argv)
40{
41	int i;
42	for (i = 1; i < argc; i++) {
43		nspace *vol = (nspace *)strtoul(argv[i], NULL, 0);
44		if (vol == NULL)
45			continue;
46
47		kprintf("fat nspace @ %p\n", vol);
48		kprintf("id: %" B_PRIdDEV ", fd: %d, device: %s, flags %" B_PRIu32 "\n",
49			vol->id, vol->fd, vol->device, vol->flags);
50		kprintf("bytes/sector = %" B_PRIu32 ", sectors/cluster = %" B_PRIu32
51			", reserved sectors = %" B_PRIu32 "\n", vol->bytes_per_sector,
52			vol->sectors_per_cluster, vol->reserved_sectors);
53		kprintf("%" B_PRIu32 " fats, %" B_PRIu32 " root entries, %" B_PRIu32
54			" total sectors, %" B_PRIu32 " sectors/fat\n", vol->fat_count,
55			vol->root_entries_count, vol->total_sectors, vol->sectors_per_fat);
56		kprintf("media descriptor %" B_PRIu8 ", fsinfo sector %" B_PRIu16
57			", %" B_PRIu32 " clusters, %" B_PRIu32 " free\n",
58			vol->media_descriptor, vol->fsinfo_sector, vol->total_clusters,
59			vol->free_clusters);
60		kprintf("%" B_PRIu8 "-bit fat, mirrored %s, active %" B_PRIu8 "\n",
61			vol->fat_bits, vol->fat_mirrored ? "yes" : "no", vol->active_fat);
62		kprintf("root start %" B_PRIu32 ", %" B_PRIu32
63			" root sectors, root vnode @ %p\n", vol->root_start,
64			vol->root_sectors, &(vol->root_vnode));
65		kprintf("label entry %" B_PRIu32 ", label %s\n", vol->vol_entry,
66			vol->vol_label);
67		kprintf("data start %" B_PRIu32 ", last allocated %" B_PRIu32 "\n",
68			vol->data_start, vol->last_allocated);
69		kprintf("last fake vnid %" B_PRIdINO ", vnid cache %" B_PRIu32
70			" entries @ (%p %p)\n", vol->vcache.cur_vnid,
71			vol->vcache.cache_size, vol->vcache.by_vnid, vol->vcache.by_loc);
72		kprintf("dlist entries: %" B_PRIu32 "/%" B_PRIu32 " @ %p\n",
73			vol->dlist.entries, vol->dlist.allocated, vol->dlist.vnid_list);
74
75		dump_vcache(vol);
76		dlist_dump(vol);
77	}
78	return B_OK;
79}
80
81
82static int
83debug_dvnode(int argc, char **argv)
84{
85	int i;
86
87	if (argc < 2) {
88		kprintf("dvnode vnode\n");
89		return B_OK;
90	}
91
92	for (i = 1; i < argc; i++) {
93		vnode *n = (vnode *)strtoul(argv[i], NULL, 0);
94		if (!n) continue;
95
96		kprintf("vnode @ %p", n);
97
98		kprintf(" (%s)", n->filename);
99		kprintf("\nvnid %" B_PRIdINO ", dir vnid %" B_PRIdINO "\n", n->vnid,
100			n->dir_vnid);
101		kprintf("iteration %" B_PRIu32 ", si=%" B_PRIu32 ", ei=%" B_PRIu32
102			", cluster=%" B_PRIu32 "\n", n->iteration, n->sindex, n->eindex,
103			n->cluster);
104		kprintf("mode %#" B_PRIx32 ", size %" B_PRIdOFF ", time %" B_PRIdTIME
105			", crtime %" B_PRIdTIME "\n", n->mode, n->st_size, n->st_time,
106			n->st_crtim);
107		kprintf("end cluster = %" B_PRIu32 "\n", n->end_cluster);
108		if (n->mime) kprintf("mime type %s\n", n->mime);
109	}
110
111	return B_OK;
112}
113
114
115static int
116debug_dc2s(int argc, char **argv)
117{
118	int i;
119	nspace *vol;
120
121	if (argc < 3) {
122		kprintf("dc2s nspace cluster\n");
123		return B_OK;
124	}
125
126	vol = (nspace *)strtoul(argv[1], NULL, 0);
127	if (vol == NULL)
128		return B_OK;
129
130	for (i=2;i<argc;i++) {
131		uint32 cluster = strtoul(argv[i], NULL, 0);
132		kprintf("cluster %" B_PRIu32 " = block %" B_PRIdOFF "\n", cluster,
133			vol->data_start + (off_t)(cluster - 2) * vol->sectors_per_cluster);
134	}
135
136	return B_OK;
137}
138
139#endif
140
141
142static void
143dosfs_trim_spaces(char *label)
144{
145	uint8 index;
146	for (index = 10; index > 0; index--) {
147		if (label[index] == ' ')
148			label[index] = 0;
149		else
150			break;
151	}
152}
153
154
155static bool
156dosfs_read_label(bool fat32, uint8 *buffer, char *label)
157{
158	uint8 check = fat32 ? 0x42 : 0x29;
159	uint8 offset = fat32 ? 0x47 : 0x2b;
160
161	if (buffer[check] == 0x29
162		&& memcmp(buffer + offset, "           ", 11) != 0) {
163		memcpy(label, buffer + offset, 11);
164		dosfs_trim_spaces(label);
165		return true;
166	}
167
168	return false;
169}
170
171
172static nspace*
173volume_init(int fd, uint8* buf,
174	const int flags, int fs_flags,
175	device_geometry *geo)
176{
177	uint8 media_buf[512];
178	int i;
179	status_t err;
180
181	nspace* vol = (nspace *)calloc(sizeof(nspace), 1);
182	if (vol == NULL) {
183		dprintf("dosfs error: out of memory\n");
184		return NULL;
185	}
186
187	vol->flags = flags;
188	vol->fs_flags = fs_flags;
189	vol->fd = fd;
190
191	// only check boot signature on hard disks to account for broken mtools
192	// behavior
193	if ((buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) && buf[0x15] == 0xf8)
194		goto error;
195
196	if (!memcmp(buf + 3, "NTFS    ", 8) || !memcmp(buf + 3, "HPFS    ", 8)) {
197		dprintf("dosfs error: %4.4s, not FAT\n", buf + 3);
198		goto error;
199	}
200
201	// first fill in the universal fields from the bpb
202	vol->bytes_per_sector = read16(buf, 0xb);
203	if (vol->bytes_per_sector != 0x200 && vol->bytes_per_sector != 0x400
204		&& vol->bytes_per_sector != 0x800 && vol->bytes_per_sector != 0x1000) {
205		dprintf("dosfs error: unsupported bytes per sector (%" B_PRIu32 ")\n",
206			vol->bytes_per_sector);
207		goto error;
208	}
209
210	vol->sectors_per_cluster = i = buf[0xd];
211	if (i != 1 && i != 2 && i != 4 && i != 8
212		&& i != 0x10 && i != 0x20 && i != 0x40 && i != 0x80) {
213		dprintf("dosfs error: unsupported sectors per cluster (%d)\n", i);
214		goto error;
215	}
216
217	vol->reserved_sectors = read16(buf, 0xe);
218
219	vol->fat_count = buf[0x10];
220	if (vol->fat_count == 0 || vol->fat_count > 8) {
221		dprintf("dosfs error: unreasonable fat count (%" B_PRIu32 ")\n",
222			vol->fat_count);
223		goto error;
224	}
225
226	vol->media_descriptor = buf[0x15];
227	// check media descriptor versus known types
228	if (buf[0x15] != 0xf0 && buf[0x15] < 0xf8) {
229		dprintf("dosfs error: invalid media descriptor byte\n");
230		goto error;
231	}
232
233	vol->vol_entry = -2;	// for now, assume there is no volume entry
234	strcpy(vol->vol_label, "no name");
235
236	// now become more discerning citizens
237	vol->sectors_per_fat = read16(buf, 0x16);
238	if (vol->sectors_per_fat == 0) {
239		// fat32 land
240		vol->fat_bits = 32;
241		vol->sectors_per_fat = read32(buf, 0x24);
242		vol->total_sectors = read32(buf, 0x20);
243
244		vol->fsinfo_sector = read16(buf, 0x30);
245		if (vol->fsinfo_sector != 0xffff
246			&& vol->fsinfo_sector >= vol->reserved_sectors) {
247			dprintf("dosfs error: fsinfo sector too large (0x%x)\n",
248				vol->fsinfo_sector);
249			goto error;
250		}
251
252		vol->fat_mirrored = !(buf[0x28] & 0x80);
253		vol->active_fat = !vol->fat_mirrored ? (buf[0x28] & 0xf) : 0;
254
255		vol->data_start = vol->reserved_sectors + vol->fat_count
256			* vol->sectors_per_fat;
257		vol->total_clusters = (vol->total_sectors - vol->data_start)
258			/ vol->sectors_per_cluster;
259
260		vol->root_vnode.cluster = read32(buf, 0x2c);
261		if (vol->root_vnode.cluster >= vol->total_clusters) {
262			dprintf("dosfs error: root vnode cluster too large (0x%" B_PRIu32
263				")\n", vol->root_vnode.cluster);
264			goto error;
265		}
266
267		if (dosfs_read_label(true, buf, vol->vol_label))
268			vol->vol_entry = -1;
269	} else {
270		// fat12 & fat16
271		if (vol->fat_count != 2) {
272			dprintf("dosfs error: claims %" B_PRIu32 " fat tables\n",
273				vol->fat_count);
274			goto error;
275		}
276
277		vol->root_entries_count = read16(buf, 0x11);
278		if (vol->root_entries_count % (vol->bytes_per_sector / 0x20)) {
279			dprintf("dosfs error: invalid number of root entries\n");
280			goto error;
281		}
282
283		vol->fsinfo_sector = 0xffff;
284		vol->total_sectors = read16(buf, 0x13); // partition size
285		if (vol->total_sectors == 0)
286			vol->total_sectors = read32(buf, 0x20);
287
288		if (geo != NULL) {
289			/*
290				Zip disks that were formatted at iomega have an incorrect number
291				of sectors.  They say that they have 196576 sectors but they
292				really only have 196192.  This check is a work-around for their
293				brain-deadness.
294			*/
295			unsigned char bogus_zip_data[] = {
296				0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
297				0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00
298			};
299
300			if (memcmp(buf + 0x0b, bogus_zip_data, sizeof(bogus_zip_data)) == 0
301				&& vol->total_sectors == 196576
302				&& ((off_t)geo->sectors_per_track * (off_t)geo->cylinder_count
303					* (off_t)geo->head_count) == 196192) {
304				vol->total_sectors = 196192;
305			}
306		}
307
308		vol->fat_mirrored = true;
309		vol->active_fat = 0;
310
311		vol->root_start = vol->reserved_sectors + vol->fat_count
312			* vol->sectors_per_fat;
313		vol->root_sectors = vol->root_entries_count * 0x20
314			/ vol->bytes_per_sector;
315		vol->root_vnode.cluster = 1;
316		vol->root_vnode.end_cluster = 1;
317		vol->root_vnode.st_size = vol->root_sectors * vol->bytes_per_sector;
318
319		vol->data_start = vol->root_start + vol->root_sectors;
320		vol->total_clusters = (vol->total_sectors - vol->data_start)
321			/ vol->sectors_per_cluster;
322
323		// XXX: uncertain about border cases; win32 sdk says cutoffs are at
324		//      at ff6/ff7 (or fff6/fff7), but that doesn't make much sense
325		if (vol->total_clusters > 0xff1)
326			vol->fat_bits = 16;
327		else
328			vol->fat_bits = 12;
329
330		if (dosfs_read_label(false, buf, vol->vol_label))
331			vol->vol_entry = -1;
332	}
333
334	// perform sanity checks on the FAT
335
336	// the media descriptor in active FAT should match the one in the BPB
337	if ((err = read_pos(vol->fd, vol->bytes_per_sector * (vol->reserved_sectors
338			+ vol->active_fat * vol->sectors_per_fat),
339			(void *)media_buf, 0x200)) != 0x200) {
340		dprintf("dosfs error: error reading FAT\n");
341		goto error;
342	}
343
344	if (media_buf[0] != vol->media_descriptor) {
345		dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", media_buf[0],
346			vol->media_descriptor);
347		goto error;
348	}
349
350	if (vol->fat_mirrored) {
351		uint32 i;
352		uint8 mirror_media_buf[512];
353		for (i = 0; i < vol->fat_count; i++) {
354			if (i != vol->active_fat) {
355				DPRINTF(1, ("checking fat #%" B_PRIu32 "\n", i));
356				mirror_media_buf[0] = ~media_buf[0];
357				if ((err = read_pos(vol->fd, vol->bytes_per_sector
358						* (vol->reserved_sectors + vol->sectors_per_fat * i),
359						(void *)mirror_media_buf, 0x200)) != 0x200) {
360					dprintf("dosfs error: error reading FAT %" B_PRIu32 "\n",
361						i);
362					goto error;
363				}
364
365				if (mirror_media_buf[0] != vol->media_descriptor) {
366					dprintf("dosfs error: media descriptor mismatch in fat # "
367						"%" B_PRIu32 " (%" B_PRIu8 " != %" B_PRIu8 ")\n", i,
368						mirror_media_buf[0], vol->media_descriptor);
369					goto error;
370				}
371#if 0
372				// checking for exact matches of fats is too
373				// restrictive; allow these to go through in
374				// case the fat is corrupted for some reason
375				if (memcmp(media_buf, mirror_media_buf, 0x200)) {
376					dprintf("dosfs error: fat %d doesn't match active fat "
377						"(%d)\n", i, vol->active_fat);
378					goto error;
379				}
380#endif
381			}
382		}
383	}
384
385	/* check that the partition is large enough to contain the file system */
386	if (geo != NULL
387			&& vol->total_sectors >
388				geo->sectors_per_track * geo->cylinder_count
389				* geo->head_count) {
390		dprintf("dosfs: volume extends past end of partition, mounting read-only\n");
391		vol->flags |= B_FS_IS_READONLY;
392	}
393
394	// now we are convinced of the drive's validity
395
396	// this will be updated later if fsinfo exists
397	vol->last_allocated = 2;
398	vol->beos_vnid = INVALID_VNID_BITS_MASK;
399
400	// initialize block cache
401	vol->fBlockCache = block_cache_create(vol->fd, vol->total_sectors,
402		vol->bytes_per_sector, (vol->flags & B_FS_IS_READONLY) != 0);
403	if (vol->fBlockCache == NULL) {
404		dprintf("dosfs error: error initializing block cache\n");
405		goto error;
406	}
407
408	// find volume label (supercedes any label in the bpb)
409	{
410		struct diri diri;
411		uint8 *buffer;
412		buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri);
413		for (; buffer; buffer = diri_next_entry(&diri)) {
414			if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf)
415					&& (buffer[0] != 0xe5)) {
416				vol->vol_entry = diri.current_index;
417				memcpy(vol->vol_label, buffer, 11);
418				dosfs_trim_spaces(vol->vol_label);
419				break;
420			}
421		}
422	}
423
424	DPRINTF(0, ("root vnode id = %" B_PRIdINO "\n", vol->root_vnode.vnid));
425	DPRINTF(0, ("volume label [%s] (%" B_PRIu32 ")\n", vol->vol_label,
426		vol->vol_entry));
427
428	return vol;
429
430error:
431	free(vol);
432	return NULL;
433}
434
435
436static void
437volume_uninit(nspace *vol)
438{
439	block_cache_delete(vol->fBlockCache, false);
440	free(vol);
441}
442
443
444static void
445volume_count_free_cluster(nspace *vol)
446{
447	status_t err;
448
449	if (vol->flags & B_FS_IS_READONLY)
450		vol->free_clusters = 0;
451	else {
452		uint32 free_count = 0, last_allocated = 0;
453		err = get_fsinfo(vol, &free_count, &last_allocated);
454		if (err >= 0) {
455			if (free_count < vol->total_clusters)
456				vol->free_clusters = free_count;
457			else {
458				dprintf("dosfs error: free cluster count from fsinfo block "
459					"invalid %" B_PRIu32 "\n", free_count);
460				err = -1;
461			}
462
463			if (last_allocated < vol->total_clusters)
464				vol->last_allocated = last_allocated; //update to a closer match
465		}
466
467		if (err < 0) {
468			if ((err = count_free_clusters(vol)) < 0) {
469				dprintf("dosfs error: error counting free clusters (%s)\n",
470					strerror(err));
471				return;
472			}
473			vol->free_clusters = err;
474		}
475	}
476}
477
478
479static status_t
480mount_fat_disk(const char *path, fs_volume *_vol, const int flags,
481	nspace** newVol, int fs_flags, int op_sync_mode)
482{
483	nspace *vol = NULL;
484	uint8 buf[512];
485	device_geometry geo;
486	status_t err;
487	int fd;
488	int vol_flags;
489
490	vol_flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
491
492	// open read-only for now
493	if ((err = (fd = open(path, O_RDONLY | O_NOCACHE))) < 0) {
494		dprintf("dosfs error: unable to open %s (%s)\n", path, strerror(err));
495		goto error0;
496	}
497
498	// get device characteristics
499	if (ioctl(fd, B_GET_GEOMETRY, &geo, sizeof(device_geometry)) < 0) {
500		struct stat st;
501		if (fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) {
502			/* support mounting disk images */
503			geo.bytes_per_sector = 0x200;
504			geo.sectors_per_track = 1;
505			geo.cylinder_count = st.st_size / 0x200;
506			geo.head_count = 1;
507			geo.read_only = !(st.st_mode & S_IWUSR);
508			geo.removable = true;
509		} else {
510			dprintf("dosfs error: error getting device geometry\n");
511			goto error1;
512		}
513	}
514
515	if (geo.bytes_per_sector != 0x200 && geo.bytes_per_sector != 0x400
516		&& geo.bytes_per_sector != 0x800 && geo.bytes_per_sector != 0x1000) {
517		dprintf("dosfs error: unsupported device block size (%" B_PRIu32 ")\n",
518			geo.bytes_per_sector);
519		goto error1;
520	}
521
522	if (geo.removable) {
523		DPRINTF(0, ("%s is removable\n", path));
524		vol_flags |= B_FS_IS_REMOVABLE;
525	}
526
527	if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) {
528		DPRINTF(0, ("%s is read-only\n", path));
529		vol_flags |= B_FS_IS_READONLY;
530	} else {
531		// reopen it with read/write permissions
532		close(fd);
533		if ((err = (fd = open(path, O_RDWR | O_NOCACHE))) < 0) {
534			dprintf("dosfs error: unable to open %s (%s)\n", path,
535				strerror(err));
536			goto error0;
537		}
538	}
539
540	// see if we need to go into op sync mode
541	fs_flags &= ~FS_FLAGS_OP_SYNC;
542	switch (op_sync_mode) {
543		case 1:
544			if ((vol_flags & B_FS_IS_REMOVABLE) == 0) {
545				// we're not removable, so skip op_sync
546				break;
547			}
548			// supposed to fall through
549
550		case 2:
551			dprintf("dosfs: mounted with op_sync enabled\n");
552			fs_flags |= FS_FLAGS_OP_SYNC;
553			break;
554
555		case 0:
556		default:
557			break;
558	}
559
560	// read in the boot sector
561	if ((err = read_pos(fd, 0, (void *)buf, 512)) != 512) {
562		dprintf("dosfs error: error reading boot sector\n");
563		goto error1;
564	}
565
566	vol = volume_init(fd, buf, vol_flags, fs_flags, &geo);
567	if (vol == NULL) {
568		dprintf("dosfs error: failed to initialize volume\n");
569		err = B_ERROR;
570		goto error1;
571	}
572
573	vol->volume = _vol;
574	vol->id = _vol->id;
575	strncpy(vol->device, path, sizeof(vol->device));
576
577	{
578		void *handle;
579		handle = load_driver_settings("fat");
580		vol->respect_disk_image =
581			get_driver_boolean_parameter(handle, "respect", true, true);
582		unload_driver_settings(handle);
583	}
584
585	// Initialize the vnode cache
586	if (init_vcache(vol) != B_OK) {
587		dprintf("dosfs error: error initializing vnode cache\n");
588		goto error2;
589	}
590
591	// and the dlist cache
592	if (dlist_init(vol) != B_OK) {
593		dprintf("dosfs error: error initializing dlist cache\n");
594		goto error3;
595	}
596
597	volume_count_free_cluster(vol);
598
599	DPRINTF(0, ("built at %s on %s\n", build_time, build_date));
600	DPRINTF(0, ("mounting %s (id %" B_PRIdDEV ", device %u, media descriptor %"
601		B_PRIu8 ")\n", vol->device, vol->id, vol->fd, vol->media_descriptor));
602	DPRINTF(0, ("%" B_PRIu32 " bytes/sector, %" B_PRIu32 " sectors/cluster\n",
603		vol->bytes_per_sector, vol->sectors_per_cluster));
604	DPRINTF(0, ("%" B_PRIu32 " reserved sectors, %" B_PRIu32 " total sectors\n",
605		vol->reserved_sectors, vol->total_sectors));
606	DPRINTF(0, ("%" B_PRIu32 " %" B_PRIu8 "-bit fats, %" B_PRIu32
607		" sectors/fat, %" B_PRIu32 " root entries\n", vol->fat_count,
608		vol->fat_bits, vol->sectors_per_fat, vol->root_entries_count));
609	DPRINTF(0, ("root directory starts at sector %" B_PRIu32 " (cluster %"
610		B_PRIu32 "), data at sector %" B_PRIu32 "\n", vol->root_start,
611		vol->root_vnode.cluster, vol->data_start));
612	DPRINTF(0, ("%" B_PRIu32 " total clusters, %" B_PRIu32 " free\n",
613		vol->total_clusters, vol->free_clusters));
614	DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %" B_PRIu16 "\n",
615		vol->fat_mirrored ? "on" : "off", vol->fsinfo_sector));
616	DPRINTF(0, ("last allocated cluster = %" B_PRIu32 "\n",
617		vol->last_allocated));
618
619	if (vol->fat_bits == 32) {
620		// now that the block cache has been initialised, we can figure
621		// out the length of the root directory with count_clusters()
622		vol->root_vnode.st_size = count_clusters(vol, vol->root_vnode.cluster)
623			* vol->bytes_per_sector * vol->sectors_per_cluster;
624		vol->root_vnode.end_cluster = get_nth_fat_entry(vol,
625			vol->root_vnode.cluster, vol->root_vnode.st_size
626			/ vol->bytes_per_sector / vol->sectors_per_cluster - 1);
627	}
628
629	// initialize root vnode
630	vol->root_vnode.vnid = vol->root_vnode.dir_vnid = GENERATE_DIR_CLUSTER_VNID(
631		vol->root_vnode.cluster, vol->root_vnode.cluster);
632	vol->root_vnode.sindex = vol->root_vnode.eindex = 0xffffffff;
633	vol->root_vnode.mode = FAT_SUBDIR;
634	time(&(vol->root_vnode.st_time));
635	vol->root_vnode.st_crtim = vol->root_vnode.st_time;
636	vol->root_vnode.mime = NULL;
637	vol->root_vnode.dirty = false;
638	dlist_add(vol, vol->root_vnode.vnid);
639
640
641	DPRINTF(0, ("root vnode id = %" B_PRIdINO "\n", vol->root_vnode.vnid));
642	DPRINTF(0, ("volume label [%s] (%" B_PRIu32 ")\n", vol->vol_label,
643		vol->vol_entry));
644
645	*newVol = vol;
646	return B_NO_ERROR;
647
648error3:
649	uninit_vcache(vol);
650error2:
651	volume_uninit(vol);
652error1:
653	close(fd);
654error0:
655	return err >= B_NO_ERROR ? EINVAL : err;
656}
657
658
659//	#pragma mark - Scanning
660
661
662typedef struct identify_cookie {
663	uint32 bytes_per_sector;
664	uint32 total_sectors;
665	char name[12];
666} identify_cookie;
667
668
669static float
670dosfs_identify_partition(int fd, partition_data *partition, void **_cookie)
671{
672	uint8 buf[512];
673	int i;
674	uint32 bytes_per_sector;
675	uint32 fatCount;
676	uint32 total_sectors;
677	uint32 sectors_per_fat;
678	char name[12];
679	identify_cookie *cookie;
680
681	// read in the boot sector
682	if (read_pos(fd, 0, buf, 512) != 512)
683		return -1;
684
685	// only check boot signature on hard disks to account for broken mtools
686	// behavior
687	if ((buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) && buf[0x15] == 0xf8)
688		return -1;
689	if (!memcmp(buf + 3, "NTFS    ", 8) || !memcmp(buf + 3, "HPFS    ", 8))
690		return -1;
691
692	// first fill in the universal fields from the bpb
693	bytes_per_sector = read16(buf, 0xb);
694	if (bytes_per_sector != 0x200 && bytes_per_sector != 0x400
695		&& bytes_per_sector != 0x800 && bytes_per_sector != 0x1000) {
696		return -1;
697	}
698
699	// must be a power of two
700	i = buf[0xd];
701	if (i != 1 && i != 2 && i != 4 && i != 8 && i != 0x10 && i != 0x20
702		&& i != 0x40 && i != 0x80)
703		return -1;
704
705	fatCount = buf[0x10];
706	if (fatCount == 0 || fatCount > 8)
707		return -1;
708
709	// check media descriptor versus known types
710	if (buf[0x15] != 0xf0 && buf[0x15] < 0xf8)
711		return -1;
712
713	name[0] = 0;
714	sectors_per_fat = read16(buf, 0x16);
715	if (sectors_per_fat == 0) {
716		total_sectors = read32(buf, 0x20);
717		dosfs_read_label(true, buf, name);
718	} else {
719		total_sectors = read16(buf, 0x13);
720			// partition size
721		if (total_sectors == 0)
722			total_sectors = read32(buf, 0x20);
723
724		dosfs_read_label(false, buf, name);
725	}
726
727	// find volume label (supercedes any label in the bpb)
728	{
729		nspace *vol;
730		vol = volume_init(fd, buf, 0, 0, NULL);
731		if (vol != NULL)
732		{
733			strlcpy(name, vol->vol_label, 12);
734			volume_uninit(vol);
735		}
736	}
737
738	cookie = (identify_cookie *)malloc(sizeof(identify_cookie));
739	if (!cookie)
740		return -1;
741
742	cookie->bytes_per_sector = bytes_per_sector;
743	cookie->total_sectors = total_sectors;
744	sanitize_name(name, 12);
745	strlcpy(cookie->name, name, 12);
746	*_cookie = cookie;
747
748	return 0.8f;
749}
750
751
752static status_t
753dosfs_scan_partition(int fd, partition_data *partition, void *_cookie)
754{
755	identify_cookie *cookie = (identify_cookie *)_cookie;
756	partition->status = B_PARTITION_VALID;
757	partition->flags |= B_PARTITION_FILE_SYSTEM;
758	partition->content_size = cookie->total_sectors * cookie->bytes_per_sector;
759	partition->block_size = cookie->bytes_per_sector;
760	partition->content_name = strdup(cookie->name);
761	if (partition->content_name == NULL)
762		return B_NO_MEMORY;
763
764	return B_OK;
765}
766
767
768static void
769dosfs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
770{
771	identify_cookie *cookie = (identify_cookie *)_cookie;
772	free(cookie);
773}
774
775
776//	#pragma mark -
777
778
779static status_t
780dosfs_mount(fs_volume *_vol, const char *device, uint32 flags,
781	const char *args, ino_t *_rootID)
782{
783	int	result;
784	nspace *vol;
785	void *handle;
786	int op_sync_mode = 0;
787	int fs_flags = 0;
788
789	handle = load_driver_settings("fat");
790	if (handle != NULL) {
791		debug_attr = strtoul(get_driver_parameter(handle, "debug_attr", "0", "0"), NULL, 0);
792		debug_dir = strtoul(get_driver_parameter(handle, "debug_dir", "0", "0"), NULL, 0);
793		debug_dlist = strtoul(get_driver_parameter(handle, "debug_dlist", "0", "0"), NULL, 0);
794		debug_dosfs = strtoul(get_driver_parameter(handle, "debug_dosfs", "0", "0"), NULL, 0);
795		debug_encodings = strtoul(get_driver_parameter(handle, "debug_encodings", "0", "0"), NULL, 0);
796		debug_fat = strtoul(get_driver_parameter(handle, "debug_fat", "0", "0"), NULL, 0);
797		debug_file = strtoul(get_driver_parameter(handle, "debug_file", "0", "0"), NULL, 0);
798		debug_iter = strtoul(get_driver_parameter(handle, "debug_iter", "0", "0"), NULL, 0);
799		debug_vcache = strtoul(get_driver_parameter(handle, "debug_vcache", "0", "0"), NULL, 0);
800
801		op_sync_mode = strtoul(get_driver_parameter(handle, "op_sync_mode", "0", "0"), NULL, 0);
802		if (op_sync_mode < 0 || op_sync_mode > 2)
803			op_sync_mode = 0;
804		if (strcasecmp(get_driver_parameter(handle, "lock_device", "true", "true"), "false") == 0) {
805			dprintf("dosfs: mounted with lock_device = false\n");
806		} else {
807			dprintf("dosfs: mounted with lock_device = true\n");
808			fs_flags |= FS_FLAGS_LOCK_DOOR;
809		}
810
811		unload_driver_settings(handle);
812	}
813
814	/* args is a command line option; dosfs doesn't use any so we can ignore it */
815
816	// Try and mount volume as a FAT volume
817	if ((result = mount_fat_disk(device, _vol, flags, &vol, fs_flags,
818			op_sync_mode)) == B_NO_ERROR) {
819		char name[32];
820
821		*_rootID = vol->root_vnode.vnid;
822		_vol->private_volume = (void *)vol;
823		_vol->ops = &gFATVolumeOps;
824
825		// You MUST do this. Create the vnode for the root.
826		result = publish_vnode(_vol, *_rootID, (void*)&(vol->root_vnode),
827			&gFATVnodeOps, make_mode(vol, &vol->root_vnode), 0);
828		if (result != B_NO_ERROR) {
829			dprintf("error creating new vnode (%s)\n", strerror(result));
830			goto error;
831		}
832		sprintf(name, "fat lock %" B_PRIdDEV, vol->id);
833		recursive_lock_init_etc(&(vol->vlock), name, MUTEX_FLAG_CLONE_NAME);
834
835#if DEBUG
836		if (atomic_add(&instances, 1) == 0) {
837			add_debugger_command("fat", debug_fat_nspace, "dump a fat nspace structure");
838			add_debugger_command("dvnode", debug_dvnode, "dump a fat vnode structure");
839			add_debugger_command("dfvnid", debug_dfvnid, "find a vnid in the vnid cache");
840			add_debugger_command("dfloc", debug_dfloc, "find a loc in the vnid cache");
841			add_debugger_command("dc2s", debug_dc2s, "calculate sector for cluster");
842		}
843#endif
844	}
845
846	return result;
847
848error:
849	block_cache_delete(vol->fBlockCache, false);
850	dlist_uninit(vol);
851	uninit_vcache(vol);
852	free(vol);
853	return EINVAL;
854}
855
856
857static void
858update_fsinfo(nspace *vol)
859{
860	if (vol->fat_bits == 32 && vol->fsinfo_sector != 0xffff
861		&& (vol->flags & B_FS_IS_READONLY) == 0) {
862		uchar *buffer;
863		status_t status = block_cache_get_writable_etc(vol->fBlockCache,
864				vol->fsinfo_sector, 0, vol->bytes_per_sector, -1,
865				(void**)&buffer);
866		if (status == B_OK) {
867			if ((read32(buffer,0) == 0x41615252) && (read32(buffer,0x1e4) == 0x61417272) && (read16(buffer,0x1fe) == 0xaa55)) {
868				//number of free clusters
869				buffer[0x1e8] = (vol->free_clusters & 0xff);
870				buffer[0x1e9] = ((vol->free_clusters >> 8) & 0xff);
871				buffer[0x1ea] = ((vol->free_clusters >> 16) & 0xff);
872				buffer[0x1eb] = ((vol->free_clusters >> 24) & 0xff);
873				//cluster number of most recently allocated cluster
874				buffer[0x1ec] = (vol->last_allocated & 0xff);
875				buffer[0x1ed] = ((vol->last_allocated >> 8) & 0xff);
876				buffer[0x1ee] = ((vol->last_allocated >> 16) & 0xff);
877				buffer[0x1ef] = ((vol->last_allocated >> 24) & 0xff);
878			} else {
879				dprintf("update_fsinfo: fsinfo block has invalid magic number\n");
880				block_cache_set_dirty(vol->fBlockCache, vol->fsinfo_sector,
881					false, -1);
882			}
883			block_cache_put(vol->fBlockCache, vol->fsinfo_sector);
884		} else {
885			dprintf("update_fsinfo: error getting fsinfo sector %x: %s\n",
886				vol->fsinfo_sector, strerror(status));
887		}
888	}
889}
890
891
892static status_t
893get_fsinfo(nspace *vol, uint32 *free_count, uint32 *last_allocated)
894{
895	uchar *buffer;
896	status_t result;
897
898	if ((vol->fat_bits != 32) || (vol->fsinfo_sector == 0xffff))
899		return B_ERROR;
900
901	result = block_cache_get_etc(vol->fBlockCache, vol->fsinfo_sector, 0,
902		vol->bytes_per_sector, (const void**)&buffer);
903	if (result != B_OK) {
904		dprintf("get_fsinfo: error getting fsinfo sector %x: %s\n",
905			vol->fsinfo_sector, strerror(result));
906		return result;
907	}
908
909	if ((read32(buffer,0) == 0x41615252) && (read32(buffer,0x1e4) == 0x61417272) && (read16(buffer,0x1fe) == 0xaa55)) {
910		*free_count = read32(buffer,0x1e8);
911		*last_allocated = read32(buffer,0x1ec);
912		result = B_OK;
913	} else {
914		dprintf("get_fsinfo: fsinfo block has invalid magic number\n");
915		result = B_ERROR;
916	}
917
918	block_cache_put(vol->fBlockCache, vol->fsinfo_sector);
919	return result;
920}
921
922
923static status_t
924dosfs_unmount(fs_volume *_vol)
925{
926	int result = B_NO_ERROR;
927
928	nspace* vol = (nspace*)_vol->private_volume;
929
930	RecursiveLocker lock(vol->vlock);
931
932	DPRINTF(0, ("dosfs_unmount volume %" B_PRIdDEV "\n", vol->id));
933
934	update_fsinfo(vol);
935
936	// Unlike in BeOS, we need to put the reference to our root node ourselves
937	put_vnode(_vol, vol->root_vnode.vnid);
938	block_cache_delete(vol->fBlockCache, true);
939
940#if DEBUG
941	if (atomic_add(&instances, -1) == 1) {
942		remove_debugger_command("fat", debug_fat_nspace);
943		remove_debugger_command("dvnode", debug_dvnode);
944		remove_debugger_command("dfvnid", debug_dfvnid);
945		remove_debugger_command("dfloc", debug_dfloc);
946		remove_debugger_command("dc2s", debug_dc2s);
947	}
948#endif
949
950	dlist_uninit(vol);
951	uninit_vcache(vol);
952
953	result = close(vol->fd);
954	lock.Unlock();
955	recursive_lock_destroy(&(vol->vlock));
956	free(vol);
957
958#if USE_DMALLOC
959	check_mem();
960#endif
961
962	return result;
963}
964
965
966// dosfs_read_fs_stat - Fill in fs_info struct for device.
967static status_t
968dosfs_read_fs_stat(fs_volume *_vol, struct fs_info * fss)
969{
970	nspace* vol = (nspace*)_vol->private_volume;
971
972	RecursiveLocker lock(vol->vlock);
973
974	DPRINTF(1, ("dosfs_read_fs_stat called\n"));
975
976	// fss->dev and fss->root filled in by kernel
977
978	// File system flags.
979	fss->flags = vol->flags;
980
981	// FS block size.
982	fss->block_size = vol->bytes_per_sector * vol->sectors_per_cluster;
983
984	// IO size - specifies buffer size for file copying
985	fss->io_size = 65536;
986
987	// Total blocks
988	fss->total_blocks = vol->total_clusters;
989
990	// Free blocks
991	fss->free_blocks = vol->free_clusters;
992
993	// Device name.
994	strncpy(fss->device_name, vol->device, sizeof(fss->device_name));
995
996	if (vol->vol_entry > -2)
997		strlcpy(fss->volume_name, vol->vol_label, sizeof(fss->volume_name));
998	else {
999		// No name defined, let userspace decide of one
1000		fss->volume_name[0] = 0;
1001	}
1002
1003	sanitize_name(fss->volume_name, 12);
1004
1005	// File system name
1006	strcpy(fss->fsh_name, "fat");
1007
1008	return B_OK;
1009}
1010
1011
1012static status_t
1013dosfs_write_fs_stat(fs_volume *_vol, const struct fs_info * fss, uint32 mask)
1014{
1015	status_t result = B_ERROR;
1016	nspace* vol = (nspace*)_vol->private_volume;
1017
1018	RecursiveLocker lock(vol->vlock);
1019
1020	DPRINTF(0, ("dosfs_write_fs_stat called\n"));
1021
1022	/* if it's a r/o file system, then don't allow volume renaming */
1023	if ((vol->flags & B_FS_IS_READONLY) == 0)
1024		return EROFS;
1025
1026	if (mask & FS_WRITE_FSINFO_NAME) {
1027		// sanitize name
1028		char name[11];
1029		int i,j;
1030		memset(name, ' ', 11);
1031		DPRINTF(1, ("wfsstat: setting name to %s\n", fss->volume_name));
1032		for (i=j=0;(i<11)&&(fss->volume_name[j]);j++) {
1033			char c = fss->volume_name[j];
1034			if ((c >= 'a') && (c <= 'z')) c += 'A' - 'a';
1035			// spaces acceptable in volume names
1036			if (strchr(sAcceptable, c) || (c == ' '))
1037				name[i++] = c;
1038		}
1039		if (i == 0) // bad name, kiddo
1040			return EINVAL;
1041		DPRINTF(1, ("wfsstat: sanitized to [%11.11s]\n", name));
1042
1043		if (vol->vol_entry == -1) {
1044			// stored in the bpb
1045			uchar *buffer;
1046			result = block_cache_get_writable_etc(vol->fBlockCache, 0,
1047				0, vol->bytes_per_sector, -1, (void**)&buffer);
1048			if (result != B_OK)
1049				return result;
1050
1051			if ((vol->sectors_per_fat == 0 && (buffer[0x42] != 0x29
1052					|| strncmp((const char *)buffer + 0x47, vol->vol_label, 11)
1053						!= 0))
1054				|| (vol->sectors_per_fat != 0 && (buffer[0x26] != 0x29
1055					|| strncmp((const char *)buffer + 0x2b, vol->vol_label, 11)
1056						== 0))) {
1057				dprintf("dosfs_wfsstat: label mismatch\n");
1058				block_cache_set_dirty(vol->fBlockCache, 0, false, -1);
1059				result = B_ERROR;
1060			} else {
1061				memcpy(buffer + 0x2b, name, 11);
1062				result = B_OK;
1063			}
1064			block_cache_put(vol->fBlockCache, 0);
1065		} else if (vol->vol_entry >= 0) {
1066			struct diri diri;
1067			uint8 *buffer;
1068			buffer = diri_init(vol, vol->root_vnode.cluster, vol->vol_entry, &diri);
1069
1070			// check if it is the same as the old volume label
1071			if (buffer == NULL || strncmp((const char *)buffer, vol->vol_label, 11) == 0) {
1072				dprintf("dosfs_wfsstat: label mismatch\n");
1073				return B_ERROR;
1074			}
1075
1076			diri_make_writable(&diri);
1077			memcpy(buffer, name, 11);
1078			result = B_OK;
1079		} else {
1080			uint32 index;
1081			result = create_volume_label(vol, name, &index);
1082			if (result == B_OK)
1083				vol->vol_entry = index;
1084		}
1085
1086		if (result == B_OK) {
1087			memcpy(vol->vol_label, name, 11);
1088			dosfs_trim_spaces(vol->vol_label);
1089		}
1090	}
1091
1092	if (vol->fs_flags & FS_FLAGS_OP_SYNC)
1093		_dosfs_sync(vol);
1094
1095	return result;
1096}
1097
1098
1099#if 0
1100static status_t
1101dosfs_ioctl(fs_volume *_vol, fs_vnode *_node, void *cookie, uint32 code,
1102	void *buf, size_t len)
1103{
1104	status_t result = B_OK;
1105	nspace *vol = (nspace *)_vol->private_volume;
1106	vnode *node = (vnode *)_node->private_node;
1107
1108	TOUCH(cookie); TOUCH(len);
1109
1110	LOCK_VOL(vol);
1111
1112	switch (code) {
1113		case 100000 :
1114			dprintf("built at %s on %s\n", build_time, build_date);
1115			dprintf("vol info: %s (device %x, media descriptor %x)\n", vol->device, vol->fd, vol->media_descriptor);
1116			dprintf("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster);
1117			dprintf("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors);
1118			dprintf("%lx %d-bit fats, %lx sectors/fat, %lx root entries\n", vol->fat_count, vol->fat_bits, vol->sectors_per_fat, vol->root_entries_count);
1119			dprintf("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start);
1120			dprintf("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters);
1121			dprintf("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector);
1122			dprintf("last allocated cluster = %lx\n", vol->last_allocated);
1123			dprintf("root vnode id = %Lx\n", vol->root_vnode.vnid);
1124			dprintf("volume label [%s]\n", vol->vol_label);
1125			break;
1126
1127		case 100001 :
1128			dprintf("vnode id %Lx, dir vnid = %Lx\n", node->vnid, node->dir_vnid);
1129			dprintf("si = %lx, ei = %lx\n", node->sindex, node->eindex);
1130			dprintf("cluster = %lx (%lx), mode = %lx, size = %Lx\n", node->cluster, vol->data_start + vol->sectors_per_cluster * (node->cluster - 2), node->mode, node->st_size);
1131			dprintf("mime = %s\n", node->mime ? node->mime : "(null)");
1132			dump_fat_chain(vol, node->cluster);
1133			break;
1134
1135		case 100002 : {
1136			struct diri diri;
1137			uint8 *buffer;
1138			uint32 i;
1139			for (i=0,buffer=diri_init(vol,node->cluster, 0, &diri);buffer;buffer=diri_next_entry(&diri),i++) {
1140				if (buffer[0] == 0) break;
1141				dprintf("entry %lx:\n", i);
1142				dump_directory(buffer);
1143			}
1144			break;
1145		}
1146
1147		case 100003 :
1148			dprintf("vcache validation not yet implemented\n");
1149			dprintf("validating vcache for %lx\n", vol->id);
1150			validate_vcache(vol);
1151			dprintf("validation complete for %lx\n", vol->id);
1152			break;
1153
1154		case 100004 :
1155			dprintf("dumping vcache for %lx\n", vol->id);
1156			dump_vcache(vol);
1157			break;
1158
1159		case 100005 :
1160			dprintf("dumping dlist for %lx\n", vol->id);
1161			dlist_dump(vol);
1162			break;
1163
1164		default :
1165			DPRINTF(0, ("dosfs_ioctl: vol %" B_PRIdDEV ", vnode %" B_PRIdINO
1166				" code = %" B_PRIu32 "\n", vol->id, node->vnid, code));
1167			result = B_DEV_INVALID_IOCTL;
1168			break;
1169	}
1170
1171	UNLOCK_VOL(vol);
1172
1173	return result;
1174}
1175#endif
1176
1177
1178status_t
1179_dosfs_sync(nspace *vol)
1180{
1181	update_fsinfo(vol);
1182	block_cache_sync(vol->fBlockCache);
1183
1184	return B_OK;
1185}
1186
1187
1188static status_t
1189dosfs_sync(fs_volume *_vol)
1190{
1191	nspace *vol = (nspace *)_vol->private_volume;
1192
1193	DPRINTF(0, ("dosfs_sync called on volume %" B_PRIdDEV "\n", vol->id));
1194
1195	RecursiveLocker lock(vol->vlock);
1196	return _dosfs_sync(vol);
1197}
1198
1199
1200static status_t
1201dosfs_fsync(fs_volume *_vol, fs_vnode *_node)
1202{
1203	nspace *vol = (nspace *)_vol->private_volume;
1204	vnode *node = (vnode *)_node->private_node;
1205	status_t err = B_OK;
1206
1207	RecursiveLocker lock(vol->vlock);
1208
1209	if (node->cache)
1210		err = file_cache_sync(node->cache);
1211
1212	return err;
1213}
1214
1215
1216//	#pragma mark -
1217
1218
1219static uint32
1220dosfs_get_supported_operations(partition_data* partition, uint32 mask)
1221{
1222	dprintf("dosfs_get_supported_operations\n");
1223	// TODO: We should at least check the partition size.
1224	return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1225		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1226		| B_DISK_SYSTEM_SUPPORTS_WRITING;
1227}
1228
1229
1230//	#pragma mark -
1231
1232
1233static status_t
1234dos_std_ops(int32 op, ...)
1235{
1236	dprintf("dos_std_ops()\n");
1237	switch (op) {
1238		case B_MODULE_INIT:
1239			return B_OK;
1240		case B_MODULE_UNINIT:
1241			return B_OK;
1242
1243		default:
1244			return B_ERROR;
1245	}
1246}
1247
1248
1249fs_volume_ops gFATVolumeOps = {
1250	&dosfs_unmount,
1251	&dosfs_read_fs_stat,
1252	&dosfs_write_fs_stat,
1253	&dosfs_sync,
1254	&dosfs_read_vnode,
1255
1256	/* index directory & index operations */
1257	NULL,	//&fs_open_index_dir,
1258	NULL,	//&fs_close_index_dir,
1259	NULL,	//&fs_free_index_dir_cookie,
1260	NULL,	//&fs_read_index_dir,
1261	NULL,	//&fs_rewind_index_dir,
1262
1263	NULL,	//&fs_create_index,
1264	NULL,	//&fs_remove_index,
1265	NULL,	//&fs_stat_index,
1266
1267	/* query operations */
1268	NULL,	//&fs_open_query,
1269	NULL,	//&fs_close_query,
1270	NULL,	//&fs_free_query_cookie,
1271	NULL,	//&fs_read_query,
1272	NULL,	//&fs_rewind_query,
1273};
1274
1275
1276fs_vnode_ops gFATVnodeOps = {
1277	/* vnode operations */
1278	&dosfs_walk,
1279	&dosfs_get_vnode_name,
1280	&dosfs_release_vnode,
1281	&dosfs_remove_vnode,
1282
1283	/* VM file access */
1284	&dosfs_can_page,
1285	&dosfs_read_pages,
1286	&dosfs_write_pages,
1287
1288	NULL,	// io()
1289	NULL,	// cancel_io()
1290
1291	&dosfs_get_file_map,
1292
1293	NULL,	// fs_ioctl()
1294	NULL,	// fs_set_flags,
1295	NULL,	// fs_select
1296	NULL,	// fs_deselect
1297	&dosfs_fsync,
1298
1299	NULL,
1300	NULL,	// fs_create_symlink,
1301
1302	NULL,	// fs_link,
1303	&dosfs_unlink,
1304	&dosfs_rename,
1305
1306	&dosfs_access,
1307	&dosfs_rstat,
1308	&dosfs_wstat,
1309	NULL,	// fs_preallocate,
1310
1311	/* file operations */
1312	&dosfs_create,
1313	&dosfs_open,
1314	&dosfs_close,
1315	&dosfs_free_cookie,
1316	&dosfs_read,
1317	&dosfs_write,
1318
1319	/* directory operations */
1320	&dosfs_mkdir,
1321	&dosfs_rmdir,
1322	&dosfs_opendir,
1323	&dosfs_closedir,
1324	&dosfs_free_dircookie,
1325	&dosfs_readdir,
1326	&dosfs_rewinddir,
1327
1328	/* attribute directory operations */
1329	&dosfs_open_attrdir,
1330	&dosfs_close_attrdir,
1331	&dosfs_free_attrdir_cookie,
1332	&dosfs_read_attrdir,
1333	&dosfs_rewind_attrdir,
1334
1335	/* attribute operations */
1336	NULL,	// fs_create_attr,
1337	&dosfs_open_attr,
1338	&dosfs_close_attr,
1339	&dosfs_free_attr_cookie,
1340	&dosfs_read_attr,
1341	&dosfs_write_attr,
1342
1343	&dosfs_read_attr_stat,
1344	NULL,	// fs_write_attr_stat,
1345	NULL,	// fs_rename_attr,
1346	NULL,	// fs_remove_attr,
1347};
1348
1349
1350static file_system_module_info sFATFileSystem = {
1351	{
1352		"file_systems/fat" B_CURRENT_FS_API_VERSION,
1353		0,
1354		dos_std_ops,
1355	},
1356
1357	"fat",					// short_name
1358	"FAT32 File System",	// pretty_name
1359
1360	// DDM flags
1361	0
1362//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
1363//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
1364//	| B_DISK_SYSTEM_SUPPORTS_RESIZING
1365//	| B_DISK_SYSTEM_SUPPORTS_MOVING
1366//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
1367//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
1368	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1369	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1370//	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING
1371//	| B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED
1372//	| B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
1373//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
1374//	| B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED
1375//	| B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED
1376//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED
1377//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED
1378	| B_DISK_SYSTEM_SUPPORTS_WRITING
1379	,
1380
1381	// scanning
1382	dosfs_identify_partition,
1383	dosfs_scan_partition,
1384	dosfs_free_identify_partition_cookie,
1385	NULL,	// free_partition_content_cookie()
1386
1387	&dosfs_mount,
1388
1389	/* capability querying operations */
1390	&dosfs_get_supported_operations,
1391
1392	NULL,	// validate_resize
1393	NULL,	// validate_move
1394	NULL,	// validate_set_content_name
1395	NULL,	// validate_set_content_parameters
1396	NULL,	// validate_initialize,
1397
1398	/* shadow partition modification */
1399	NULL,	// shadow_changed
1400
1401	/* writing */
1402	NULL,	// defragment
1403	NULL,	// repair
1404	NULL,	// resize
1405	NULL,	// move
1406	NULL,	// set_content_name
1407	NULL,	// set_content_parameters
1408	dosfs_initialize,
1409	dosfs_uninitialize
1410};
1411
1412module_info *modules[] = {
1413	(module_info *)&sFATFileSystem,
1414	NULL,
1415};
1416