C++程序  |  262行  |  6.85 KB

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <assert.h>
#include <string.h>
#include <endian.h>

#include "fatblock.h"
#include "fat.h"
#include "fs.h"
#include "utils.h"

#define DEFAULT_SECTOR_SIZE 512

static void fs_add_extent(struct fs *fs, struct extent *extent,
                          offset_t start, offset_t len, int type)
{
	assert(fs);
	assert(extent);

	extent->start = start;
	extent->len = len;
	extent->type = type;

	extent->next = fs->extents;
	fs->extents = extent;
}

struct extent *fs_find_extent(struct fs *fs, offset_t start, offset_t len,
                              struct extent *last,
                              offset_t *r_start_out,
                              offset_t *e_start_out,
                              offset_t *len_out)
{
	struct extent *e;
	offset_t end;
	offset_t e_start, e_end, e_len, e_rel_start, r_rel_start, rel_len;

	assert(fs);

	end = start + len;

	e = last ? last->next : fs->extents;
	for (; e; e = e->next) {
		e_start = e->start;
		e_len = e->len;
		e_end = e_start + e_len;

		if (start >= e_end)
			continue;

		if (end <= e_start)
			continue;

		if (e_start <= start) {
			r_rel_start = 0;
			e_rel_start = start - e_start;
			if (end <= e_end)
				rel_len = len;
			else
				rel_len = e_end - start;
		} else {
			e_rel_start = 0;
			r_rel_start = e_start - start;
			if (e_end <= end)
				rel_len = e_len;
			else
				rel_len = end - e_start;
		}

		assert(e_rel_start < e_len);
		assert(e_rel_start + rel_len <= e_len);
		assert(r_rel_start < len);
		assert(r_rel_start + rel_len <= len);

		if (r_start_out)
			*r_start_out = r_rel_start;
		if (e_start_out)
			*e_start_out = e_rel_start;
		if (len_out)
			*len_out = rel_len;

		return e;
	}

	return NULL;
}

static void fs_set_fat(struct fs *fs, cluster_t cluster, fat_entry_t entry)
{
	assert(fs);

	fs->fat[cluster] = htole32(entry);
}

int fs_alloc_extent(struct fs *fs, struct extent *extent,
                    offset_t len, int type, cluster_t *first_cluster_out)
{
	assert(fs);
	assert(extent);

	cluster_t clusters_needed, start;
	cluster_t i;

	if (len == 0) {
		extent->start = 0;
		extent->len = 0;
		extent->type = type;
		*first_cluster_out = 0;
		return 0;
	}

	clusters_needed = (len + fs->cluster_size - 1) / fs->cluster_size;

	/* Check for adequate space. */
	if (fs->next_cluster + clusters_needed > fs->num_clusters) {
		WARN("allocating extent: filesystem is full!\n");
		return -1;
	}

	/* Allocate clusters. */
	start = fs->next_cluster;
	fs->next_cluster += clusters_needed;

	/* Update FAT. */
	for (i = 0; i < clusters_needed - 1; i++) {
		fs_set_fat(fs, start + i, start + i + 1);
	}
	fs_set_fat(fs, start + clusters_needed - 1, FAT_ENTRY_EOC);

	*first_cluster_out = start;

	fs_add_extent(fs,
                      extent,
                      fs->data_offset + (offset_t)(start - FAT_CLUSTER_ZERO)
                                        * fs->cluster_size,
                      (offset_t)clusters_needed * fs->cluster_size,
                      type);

	return 0;
}

int fs_init(struct fs *fs, uint16_t cluster_size, offset_t data_size,
	    offset_t *total_size_out)
{
	uint16_t sector_size;
	cluster_t data_clusters;
	sector_t reserved_sectors, fat_sectors, data_sectors, total_sectors;
	sector_t sectors_per_cluster;
	int fat_entries_per_sector;
	fat_entry_t *fat;
	struct fat_boot_sector *bs;
	struct fat_info_sector *is;

	assert(fs);

	sector_size = DEFAULT_SECTOR_SIZE;
	fs->cluster_size = cluster_size;

	sectors_per_cluster = cluster_size / DEFAULT_SECTOR_SIZE;
	fat_entries_per_sector = sector_size / sizeof(fat_entry_t);

	data_clusters = (data_size + cluster_size - 1) / cluster_size;
	data_sectors = data_clusters * sectors_per_cluster;
	fat_sectors = ((data_clusters + 2) + fat_entries_per_sector - 1)
                      / fat_entries_per_sector;
	reserved_sectors = 3;
	total_sectors = reserved_sectors + fat_sectors + data_sectors;

	memset(&fs->boot, 0, sizeof(fs->boot));
	bs = &fs->boot;

	strpadcpy(bs->name, "FATBLOCK", ' ', sizeof(bs->name));
	bs->sector_size = htole16(sector_size);
	bs->sectors_per_cluster = sectors_per_cluster;
	bs->reserved_sectors = htole16(reserved_sectors);
	bs->fats = 1;
	bs->media_desc = FAT_MEDIA_DESC_FIXED;
	/* TODO: Calculate geometry? */
	bs->sectors_per_track = htole16(42);
	bs->heads = htole16(42);
	bs->sectors32 = htole32(total_sectors);
	bs->fat_sectors32 = htole32(fat_sectors);
	/* bs->rootdir_start will be set later. */
	bs->fs_info_sector = htole16(1);
	bs->backup_boot_sector = htole16(2);
	bs->phys_drive = FAT_PHYS_DRIVE_REMOVABLE;
	bs->ext_boot_sig = FAT_EXT_BOOT_SIG;
	bs->serial = 0x42424242;
	strpadcpy(bs->vol_label, "FATBLOCK", ' ', sizeof(bs->vol_label));
	strpadcpy(bs->type, "FAT32", ' ', sizeof(bs->type));
	memcpy(bs->boot_sig, FAT_BOOT_SIG, sizeof(bs->boot_sig));

	memset(&fs->info, 0, sizeof(fs->info));
	is = &fs->info;

	memcpy(is->info_sig1, FAT_INFO_SIG1, sizeof(is->info_sig1));
	memcpy(is->info_sig2, FAT_INFO_SIG2, sizeof(is->info_sig2));
	is->free_clusters = htole32(-1);
	is->last_cluster = htole32(FAT_CLUSTER_ZERO);
	memcpy(is->info_sig3, FAT_INFO_SIG3, sizeof(is->info_sig3));

	fs->num_clusters = FAT_CLUSTER_ZERO + data_clusters;
	fs->next_cluster = FAT_CLUSTER_ZERO;

	fs->fat_size = fat_sectors * sector_size;
	fs->fat = malloc(fs->fat_size);
	if (!fs->fat) {
		WARN("initializing filesystem: couldn't allocate FAT extent: "
		     "out of memory\n");
		return MALLOC_FAIL;
	}
	memset(fs->fat, 0, fs->fat_size);

	fs->data_offset = (reserved_sectors + fat_sectors) * sector_size;

	fs->extents = NULL;
	fs_add_extent(fs, &fs->boot_extent,
                      0, sector_size,
                      EXTENT_TYPE_BOOT);
	fs_add_extent(fs, &fs->info_extent,
                      sector_size, sector_size,
                      EXTENT_TYPE_INFO);
	fs_add_extent(fs, &fs->backup_boot_extent,
                      2 * sector_size, sector_size,
                      EXTENT_TYPE_BOOT);
	fs_add_extent(fs, &fs->fat_extent,
                      reserved_sectors * sector_size, fs->fat_size,
                      EXTENT_TYPE_FAT);

	*total_size_out = (offset_t)total_sectors * sector_size;

	return 0;
}

void fs_set_rootdir_start(struct fs *fs, cluster_t rootdir_start)
{
	assert(fs);

	fs->boot.rootdir_start = htole32(rootdir_start);
}

void fs_update_free_clusters(struct fs *fs)
{
	assert(fs);

	fs->info.free_clusters = htole32(fs->num_clusters - fs->next_cluster);
}