Index: dosfs.c
===================================================================
--- dosfs.c	(revision 35142)
+++ dosfs.c	(working copy)
@@ -160,6 +160,7 @@
 	}
 }
 
+
 static bool
 dosfs_read_label(bool fat32, uint8 *buffer, char *label)
 {
@@ -177,110 +178,25 @@
 }
 
 
-static int
-lock_removable_device(int fd, bool state)
+static nspace*
+volume_init(int fd, uint8* buf,
+	const int flags, int fs_flags,
+	device_geometry *geo)
 {
-	return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state));
-}
-
-
-static status_t
-mount_fat_disk(const char *path, fs_volume *_vol, const int flags,
-	nspace** newVol, int fs_flags, int op_sync_mode)
-{
 	nspace *vol = NULL;
-	uint8 buf[512];
+	uint8 media_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 B_NO_MEMORY;
+		return NULL;
 	}
 
-	vol->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
+	vol->flags = flags;
 	vol->fs_flags = fs_flags;
+	vol->fd = fd;
 
-	// open read-only for now
-	if ((err = (vol->fd = open(path, O_RDONLY | O_NOCACHE))) < 0) {
-		dprintf("dosfs error: 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: error getting device geometry\n");
-			goto error0;
-		}
-	}
-
-	if (geo.bytes_per_sector != 0x200 && geo.bytes_per_sector != 0x400
-		&& geo.bytes_per_sector != 0x800 && geo.bytes_per_sector != 0x1000) {
-		dprintf("dosfs error: 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 | O_NOCACHE))) < 0) {
-			dprintf("dosfs error: 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;
-			}
-			// supposed to fall through
-
-		case 2:
-			dprintf("dosfs: mounted with op_sync enabled\n");
-			vol->fs_flags |= FS_FLAGS_OP_SYNC;
-			break;
-
-		case 0:
-		default:
-			break;
-	}
-
-	// read in the boot sector
-	if ((err = read_pos(vol->fd, 0, (void *)buf, 512)) != 512) {
-		dprintf("dosfs error: 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)
@@ -376,22 +292,22 @@
 		if (vol->total_sectors == 0)
 			vol->total_sectors = read32(buf, 0x20);
 
-		{
+		if (geo != NULL) {
 			/*
-			  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.
+				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) {
+				&& ((off_t)geo->sectors_per_track * (off_t)geo->cylinder_count
+					* (off_t)geo->head_count) == 196192) {
 				vol->total_sectors = 196192;
 			}
 		}
@@ -422,54 +338,46 @@
 			vol->vol_entry = -1;
 	}
 
-	/* 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) {
+			(void *)media_buf, 0x200)) != 0x200) {
 		dprintf("dosfs error: error reading FAT\n");
 		goto error;
 	}
 
-	if (buf[0] != vol->media_descriptor) {
-		dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", buf[0],
+	if (media_buf[0] != vol->media_descriptor) {
+		dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", media_buf[0],
 			vol->media_descriptor);
 		goto error;
 	}
 
 	if (vol->fat_mirrored) {
 		uint32 i;
-		uint8 buf2[512];
+		uint8 mirror_media_buf[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];
+				mirror_media_buf[0] = ~media_buf[0];
 				if ((err = read_pos(vol->fd, vol->bytes_per_sector
 						* (vol->reserved_sectors + vol->sectors_per_fat * i),
-						(void *)buf2, 0x200)) != 0x200) {
+						(void *)mirror_media_buf, 0x200)) != 0x200) {
 					dprintf("dosfs error: error reading FAT %ld\n", i);
 					goto error;
 				}
 
-				if (buf2[0] != vol->media_descriptor) {
+				if (mirror_media_buf[0] != vol->media_descriptor) {
 					dprintf("dosfs error: media descriptor mismatch in fat # "
-						"%ld (%x != %x)\n", i, buf2[0], vol->media_descriptor);
+						"%ld (%x != %x)\n", i, mirror_media_buf[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)) {
+				if (memcmp(media_buf, mirror_media_buf, 0x200)) {
 					dprintf("dosfs error: fat %d doesn't match active fat "
 						"(%d)\n", i, vol->active_fat);
 					goto error;
@@ -479,23 +387,12 @@
 		}
 	}
 
+
 	// now we are convinced of the drive's validity
 
-	vol->volume = _vol;
-	vol->id = _vol->id;
-	strncpy(vol->device, path, sizeof(vol->device));
-
 	// 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("fat");
-		vol->respect_disk_image =
-				get_driver_boolean_parameter(handle, "respect", true, true);
-		unload_driver_settings(handle);
-	}
 
 	// initialize block cache
 	vol->fBlockCache = block_cache_create(vol->fd, vol->total_sectors,
@@ -505,18 +402,52 @@
 		goto error;
 	}
 
-	// as well as the vnode cache
-	if (init_vcache(vol) != B_OK) {
-		dprintf("dosfs error: error initializing vnode cache\n");
-		goto error1;
-	}
+	// 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);
+				dosfs_trim_spaces(vol->vol_label);
+				break;
+			}
+		}
 
-	// and the dlist cache
-	if (dlist_init(vol) != B_OK) {
-		dprintf("dosfs error: error initializing dlist cache\n");
-		goto error2;
+		diri_free(&diri);
 	}
 
+	DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));
+	DPRINTF(0, ("volume label [%s] (%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;
+
+	return vol;
+
+error:
+	free(vol);
+	return NULL;
+}
+
+
+static void
+volume_uninit(nspace *vol)
+{
+	block_cache_delete(vol->fBlockCache, false);
+	free(vol);
+}
+
+
+static void
+volume_count_free_cluster(nspace *vol)
+{
+	status_t err;
+
 	if (vol->flags & B_FS_IS_READONLY)
 		vol->free_clusters = 0;
 	else {
@@ -539,20 +470,164 @@
 			if ((err = count_free_clusters(vol)) < 0) {
 				dprintf("dosfs error: error counting free clusters (%s)\n",
 					strerror(err));
-				goto error3;
+				return;
 			}
 			vol->free_clusters = err;
 		}
 	}
+}
 
+
+static int
+lock_removable_device(int fd, bool state)
+{
+	return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state));
+}
+
+
+static status_t
+mount_fat_disk(const char *path, fs_volume *_vol, const int flags,
+	nspace** newVol, int fs_flags, int op_sync_mode)
+{
+	nspace *vol = NULL;
+	uint8 buf[512];
+	device_geometry geo;
+	status_t err;
+	int fd;
+	int vol_flags;
+
+	vol_flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
+ 
+	// open read-only for now
+	if ((err = (fd = open(path, O_RDONLY | O_NOCACHE))) < 0) {
+		dprintf("dosfs error: unable to open %s (%s)\n", path, strerror(err));
+		goto error0;
+	}
+
+	// get device characteristics
+	if (ioctl(fd, B_GET_GEOMETRY, &geo) < 0) {
+		struct stat st;
+		if (fstat(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: error getting device geometry\n");
+			goto error1;
+		}
+	}
+
+	if (geo.bytes_per_sector != 0x200 && geo.bytes_per_sector != 0x400
+		&& geo.bytes_per_sector != 0x800 && geo.bytes_per_sector != 0x1000) {
+		dprintf("dosfs error: unsupported device block size (%lu)\n",
+			geo.bytes_per_sector);
+		goto error1;
+	}
+
+	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(fd);
+		if ((err = (fd = open(path, O_RDWR | O_NOCACHE))) < 0) {
+			dprintf("dosfs error: unable to open %s (%s)\n", path,
+				strerror(err));
+			goto error0;
+		}
+
+		if ((vol_flags & B_FS_IS_REMOVABLE)
+			&& (fs_flags & FS_FLAGS_LOCK_DOOR))
+			lock_removable_device(fd, true);
+	}
+
+	// see if we need to go into op sync mode
+	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;
+			}
+			// supposed to fall through
+
+		case 2:
+			dprintf("dosfs: mounted with op_sync enabled\n");
+			fs_flags |= FS_FLAGS_OP_SYNC;
+			break;
+
+		case 0:
+		default:
+			break;
+	}
+
+	// read in the boot sector
+	if ((err = read_pos(fd, 0, (void *)buf, 512)) != 512) {
+		dprintf("dosfs error: error reading boot sector\n");
+		goto error1;
+	}
+	
+	vol = volume_init(fd, buf, vol_flags, fs_flags, &geo);
+
+	/* 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 error2;
+	}
+
+	vol->volume = _vol;
+	vol->id = _vol->id;
+	strncpy(vol->device, path, sizeof(vol->device));
+
+	{
+		void *handle;
+		handle = load_driver_settings("fat");
+		vol->respect_disk_image =
+			get_driver_boolean_parameter(handle, "respect", true, true);
+		unload_driver_settings(handle);
+	}
+
+	// Initialize the vnode cache
+	if (init_vcache(vol) != B_OK) {
+		dprintf("dosfs error: error initializing vnode cache\n");
+		goto error2;
+	}
+
+	// and the dlist cache
+	if (dlist_init(vol) != B_OK) {
+		dprintf("dosfs error: error initializing dlist cache\n");
+		goto error3;
+	}
+
+	volume_count_free_cluster(vol);
+
 	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, ("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) {
@@ -575,24 +650,7 @@
 	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);
-				dosfs_trim_spaces(vol->vol_label);
-				break;
-			}
-		}
 
-		diri_free(&diri);
-	}
-
 	DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid));
 	DPRINTF(0, ("volume label [%s] (%lx)\n", vol->vol_label, vol->vol_entry));
 
@@ -604,19 +662,16 @@
 	return B_NO_ERROR;
 
 error3:
-	dlist_uninit(vol);
+	uninit_vcache(vol);
 error2:
-	uninit_vcache(vol);
+	volume_uninit(vol);
 error1:
-	block_cache_delete(vol->fBlockCache, false);
-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);
+		lock_removable_device(fd, false);
 	}
+	close(fd);
 error0:
-	close(vol->fd);
-	free(vol);
 	return err >= B_NO_ERROR ? EINVAL : err;
 }
 
@@ -693,6 +748,17 @@
 		dosfs_read_label(false, buf, name);
 	}
 
+	// find volume label (supercedes any label in the bpb)
+	{
+		nspace *vol;
+		vol = volume_init(fd, buf, 0, 0, NULL);
+		if (vol != NULL)
+		{
+			strlcpy(name, vol->vol_label, 12);
+			volume_uninit(vol);
+		}
+	}
+
 	cookie = (identify_cookie *)malloc(sizeof(identify_cookie));
 	if (!cookie)
 		return -1;
