rem: dprintf("free cluster count from fsinfo block invalid %x\n",free_count);
add: dprintf("free cluster count from fsinfo block invalid %lx\n", free_count);
context: static status_t mount_fat_disk(const char *path, nspace_id nsid,		const int flags, nspace** newVol, int fs_flags, int op_sync_mode){	nspace		*vol = NULL;	uint8		buf[512];	int			i;	device_geometry geo;	status_t err;	*newVol = NULL;	if ((vol = (nspace*)calloc(sizeof(nspace), 1)) == NULL) {		dprintf("dosfs error: out of memory\n");		return ENOMEM;	}		vol->magic = NSPACE_MAGIC;	vol->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;	vol->fs_flags = fs_flags;	// open read-only for now	if ((err = (vol->fd = open(path, O_RDONLY))) < 0) {		dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err));		goto error0;	}		// get device characteristics	if (ioctl(vol->fd, B_GET_GEOMETRY, &geo) < 0) {		struct stat st;		if ((fstat(vol->fd, &st) >= 0) &&		    S_ISREG(st.st_mode)) {			/* support mounting disk images */			geo.bytes_per_sector = 0x200;			geo.sectors_per_track = 1;			geo.cylinder_count = st.st_size / 0x200;			geo.head_count = 1;			geo.read_only = !(st.st_mode & S_IWUSR);			geo.removable = true;		} else {			dprintf("dosfs: error getting device geometry\n");			goto error0;		}	}	if ((geo.bytes_per_sector != 0x200) && (geo.bytes_per_sector != 0x400) && (geo.bytes_per_sector != 0x800)) {		dprintf("dosfs: unsupported device block size (%lu)\n", geo.bytes_per_sector);		goto error0;	}	if (geo.removable) {		DPRINTF(0, ("%s is removable\n", path));		vol->flags |= B_FS_IS_REMOVABLE;	}	if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) {		DPRINTF(0, ("%s is read-only\n", path));		vol->flags |= B_FS_IS_READONLY;	} else {		// reopen it with read/write permissions		close(vol->fd);		if ((err = (vol->fd = open(path, O_RDWR))) < 0) {			dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err));			goto error0;		}		if ((vol->flags & B_FS_IS_REMOVABLE) && (vol->fs_flags & FS_FLAGS_LOCK_DOOR))			lock_removable_device(vol->fd, true);	}	// see if we need to go into op sync mode	vol->fs_flags &= ~FS_FLAGS_OP_SYNC;	switch(op_sync_mode) {		case 1:			if((vol->flags & B_FS_IS_REMOVABLE) == 0) {				// we're not removable, so skip op_sync				break;			}		case 2:			dprintf("dosfs: mounted with op_sync enabled\n");			vol->fs_flags |= FS_FLAGS_OP_SYNC;			break;		case 0:		default:			;	}	// read in the boot sector	if ((err = read_pos(vol->fd, 0, (void*)buf, 512)) != 512) {		dprintf("dosfs: error reading boot sector\n");		goto error;	}	// only check boot signature on hard disks to account for broken mtools	// behavior	if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))		goto error;	if (!memcmp(buf+3, "NTFS    ", 8) || !memcmp(buf+3, "HPFS    ", 8)) {		dprintf("%4.4s, not FAT\n", buf+3);		goto error;	}	// first fill in the universal fields from the bpb	vol->bytes_per_sector = read16(buf,0xb);	if ((vol->bytes_per_sector != 0x200) && (vol->bytes_per_sector != 0x400) && (vol->bytes_per_sector != 0x800)) {		dprintf("dosfs error: unsupported bytes per sector (%lu)\n",				vol->bytes_per_sector);		goto error;	}		vol->sectors_per_cluster = i = buf[0xd];	if ((i != 1) && (i != 2) && (i != 4) && (i != 8) && 		(i != 0x10) && (i != 0x20) && (i != 0x40) && (i != 0x80)) {		dprintf("dosfs: sectors/cluster = %d\n", i);		goto error;	}	vol->reserved_sectors = read16(buf,0xe);	vol->fat_count = buf[0x10];	if ((vol->fat_count == 0) || (vol->fat_count > 8)) {		dprintf("dosfs: unreasonable fat count (%lu)\n", vol->fat_count);		goto error;	}	vol->media_descriptor = buf[0x15];	// check media descriptor versus known types	if ((buf[0x15] != 0xF0) && (buf[0x15] < 0xf8)) {		dprintf("dosfs error: invalid media descriptor byte\n");		goto error;	}		vol->vol_entry = -2;	// for now, assume there is no volume entry	memset(vol->vol_label, ' ', 11);	// now become more discerning citizens	vol->sectors_per_fat = read16(buf,0x16);	if (vol->sectors_per_fat == 0) {		// fat32 land		vol->fat_bits = 32;		vol->sectors_per_fat = read32(buf,0x24);		vol->total_sectors = read32(buf,0x20);				vol->fsinfo_sector = read16(buf, 0x30);		if ((vol->fsinfo_sector != 0xffff) && (vol->fsinfo_sector >= vol->reserved_sectors)) {			dprintf("dosfs: fsinfo sector too large (%x)\n", vol->fsinfo_sector);			goto error;		}		vol->fat_mirrored = !(buf[0x28] & 0x80);		vol->active_fat = (vol->fat_mirrored) ? (buf[0x28] & 0xf) : 0;		vol->data_start = vol->reserved_sectors + vol->fat_count*vol->sectors_per_fat;		vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster;		vol->root_vnode.cluster = read32(buf,0x2c);		if (vol->root_vnode.cluster >= vol->total_clusters) {			dprintf("dosfs: root vnode cluster too large (%lx)\n", vol->root_vnode.cluster);			goto error;		}	} else {		// fat12 & fat16		if (vol->fat_count != 2) {			dprintf("dosfs error: claims %ld fat tables\n", vol->fat_count);			goto error;		}		vol->root_entries_count = read16(buf,0x11);		if (vol->root_entries_count % (vol->bytes_per_sector / 0x20)) {			dprintf("dosfs error: invalid number of root entries\n");			goto error;		}		vol->fsinfo_sector = 0xffff;		vol->total_sectors = read16(buf,0x13); // partition size		if (vol->total_sectors == 0)			vol->total_sectors = read32(buf,0x20);		{			/*			  Zip disks that were formatted at iomega have an incorrect number			  of sectors.  They say that they have 196576 sectors but they			  really only have 196192.  This check is a work-around for their			  brain-deadness.			*/  			unsigned char bogus_zip_data[] = {				0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,				0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00			};						if (memcmp(buf+0x0b, bogus_zip_data, sizeof(bogus_zip_data)) == 0 &&				vol->total_sectors == 196576 &&				((off_t)geo.sectors_per_track *				 (off_t)geo.cylinder_count *				 (off_t)geo.head_count) == 196192) {				vol->total_sectors = 196192;			}		}		if (buf[0x26] == 0x29) {			// fill in the volume label			if (memcmp(buf+0x2b, "           ", 11)) {				memcpy(vol->vol_label, buf+0x2b, 11);				vol->vol_entry = -1;			}		}		vol->fat_mirrored = true;		vol->active_fat = 0;		vol->root_start = vol->reserved_sectors + vol->fat_count * vol->sectors_per_fat;		vol->root_sectors = vol->root_entries_count * 0x20 / vol->bytes_per_sector;		vol->root_vnode.cluster = 1;		vol->root_vnode.end_cluster = 1;		vol->root_vnode.st_size = vol->root_sectors * vol->bytes_per_sector;		vol->data_start = vol->root_start + vol->root_sectors;		vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster;					// XXX: uncertain about border cases; win32 sdk says cutoffs are at		//      at ff6/ff7 (or fff6/fff7), but that doesn't make much sense		if (vol->total_clusters > 0xff1)			vol->fat_bits = 16;		else			vol->fat_bits = 12;	}	/* check that the partition is large enough to contain the file system */	if (vol->total_sectors > geo.sectors_per_track * geo.cylinder_count *			geo.head_count) {		dprintf("dosfs: volume extends past end of partition\n");		err = B_PARTITION_TOO_SMALL;		goto error;	}	// perform sanity checks on the FAT			// the media descriptor in active FAT should match the one in the BPB	if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat), (void *)buf, 0x200)) != 0x200) {		dprintf("dosfs: error reading FAT\n");		goto error;	}	if (buf[0] != vol->media_descriptor) {		dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", buf[0], vol->media_descriptor);		goto error;	}	if (vol->fat_mirrored) {		uint32 i;		uint8 buf2[512];		for (i=0;i<vol->fat_count;i++) {			if (i != vol->active_fat) {				DPRINTF(1, ("checking fat #%ld\n", i));				buf2[0] = ~buf[0];				if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->sectors_per_fat*i), (void *)buf2, 0x200)) != 0x200) {					dprintf("dosfs: error reading FAT %ld\n", i);					goto error;				}				if (buf2[0] != vol->media_descriptor) {					dprintf("dosfs error: media descriptor mismatch in fat # %ld (%x != %x)\n", i, buf2[0], vol->media_descriptor);					goto error;				}#if 0							// checking for exact matches of fats is too				// restrictive; allow these to go through in				// case the fat is corrupted for some reason				if (memcmp(buf, buf2, 0x200)) {					dprintf("dosfs error: fat %d doesn't match active fat (%d)\n", i, vol->active_fat);					goto error;				}#endif			}		}	}	// now we are convinced of the drive's validity	vol->id = nsid;	strncpy(vol->device,path,256);	// this will be updated later if fsinfo exists	vol->last_allocated = 2;	vol->beos_vnid = INVALID_VNID_BITS_MASK;	{		void *handle;		handle = load_driver_settings("dos");		vol->respect_disk_image =				get_driver_boolean_parameter(handle, "respect", true, true);		unload_driver_settings(handle);	}	// initialize block cache	if (init_cache_for_device(vol->fd, (off_t)vol->total_sectors) < 0) {		dprintf("error initializing block cache\n");		goto error;	}		// as well as the vnode cache	if (init_vcache(vol) != B_OK) {		dprintf("error initializing vnode cache\n");		goto error1;	}	// and the dlist cache	if (dlist_init(vol) != B_OK) {		dprintf("error initializing dlist cache\n");		goto error2;	}	if (vol->flags & B_FS_IS_READONLY)		vol->free_clusters = 0;	else {		uint32 free_count, last_allocated;		err = get_fsinfo(vol, &free_count, &last_allocated);		if (err >= 0) {			if (free_count < vol->total_clusters) 				vol->free_clusters = free_count;			else {				dprintf("free cluster count from fsinfo block invalid %x\n",free_count);				err = -1;			}			if (last_allocated < vol->total_clusters) 				vol->last_allocated = last_allocated; //update to a closer match		}		if (err < 0) {			if ((err = count_free_clusters(vol)) < 0) {				dprintf("error counting free clusters (%s)\n", strerror(err));				goto error3;			}			vol->free_clusters = err;		}	}	DPRINTF(0, ("built at %s on %s\n", build_time, build_date));	DPRINTF(0, ("mounting %s (id %lx, device %x, media descriptor %x)\n", vol->device, vol->id, vol->fd, vol->media_descriptor));	DPRINTF(0, ("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster));	DPRINTF(0, ("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors));	DPRINTF(0, ("%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));	DPRINTF(0, ("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start));	DPRINTF(0, ("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters));	DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector));	DPRINTF(0, ("last allocated cluster = %lx\n", vol->last_allocated));	if (vol->fat_bits == 32) {		// now that the block cache has been initialised, we can figure		// out the length of the root directory with count_clusters()		vol->root_vnode.st_size = count_clusters(vol, vol->root_vnode.cluster) * vol->bytes_per_sector * vol->sectors_per_cluster;		vol->root_vnode.end_cluster = get_nth_fat_entry(vol, vol->root_vnode.cluster, vol->root_vnode.st_size / vol->bytes_per_sector / vol->sectors_per_cluster - 1);	}	// initialize root vnode	vol->root_vnode.magic = VNODE_MAGIC;	vol->root_vnode.vnid = vol->root_vnode.dir_vnid = GENERATE_DIR_CLUSTER_VNID(vol->root_vnode.cluster,vol->root_vnode.cluster);	vol->root_vnode.sindex = vol->root_vnode.eindex = 0xffffffff;	vol->root_vnode.mode = FAT_SUBDIR;	time(&(vol->root_vnode.st_time));	vol->root_vnode.mime = NULL;	vol->root_vnode.dirty = false;	dlist_add(vol, vol->root_vnode.vnid);	// find volume label (supercedes any label in the bpb)	{		struct diri diri;		uint8 *buffer;		buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri);		for (;buffer;buffer=diri_next_entry(&diri)) {			if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf) && (buffer[0] != 0xe5)) {				vol->vol_entry = diri.current_index;				memcpy(vol->vol_label, buffer, 11);				break;			}		}		diri_free(&diri);	}	DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));	DPRINTF(0, ("volume label [%11.11s] (%lx)\n", vol->vol_label, vol->vol_entry));	// steal a trick from bfs	if (!memcmp(vol->vol_label, "__RO__     ", 11)) {		vol->flags |= B_FS_IS_READONLY;	}		*newVol = vol;	return B_NO_ERROR;error3:	dlist_uninit(vol);error2:	uninit_vcache(vol);error1:	remove_cached_device_blocks(vol->fd, NO_WRITES);error:	if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE) && (vol->fs_flags & FS_FLAGS_LOCK_DOOR))		lock_removable_device(vol->fd, false);error0:	close(vol->fd);	free(vol);	return (err >= B_NO_ERROR) ? EINVAL : err;}