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