/*
* 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);
}