blob: da63964e1e941b44fcd28fe513996dd4b752198c [file] [log] [blame]
/*
libparted
Copyright (C) 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
I can also be contacted at:
Andrew Clausen
18 Shaw St
Ashwood, 3147
Victoria, Australia
*/
#include <config.h>
#include "fat.h"
#include "traverse.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef DISCOVER_ONLY
#define NO_CLUSTER -1
static char tmp_buffer [4096];
int
fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info)
{
return trav_info->buffer_size / sizeof (FatDirEntry);
}
/* returns 1 if there are no more directory entries in the directory being
* traversed, 0 otherwise.
*/
static int
is_last_buffer (FatTraverseInfo* trav_info) {
FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
if (trav_info->is_legacy_root_dir)
return 1;
else
return fat_table_is_eof (fs_info->fat, trav_info->next_buffer);
}
static int
write_root_dir (FatTraverseInfo* trav_info)
{
FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries,
fs_info->root_dir_offset,
fs_info->root_dir_sector_count))
return 0;
if (!ped_geometry_sync (trav_info->fs->geom))
return 0;
trav_info->dirty = 0;
return 1;
}
static int
write_dir_cluster (FatTraverseInfo* trav_info)
{
if (!fat_write_sync_cluster (trav_info->fs,
(void*) trav_info->dir_entries,
trav_info->this_buffer))
return 0;
trav_info->dirty = 0;
return 1;
}
static int
write_dir_buffer (FatTraverseInfo* trav_info)
{
if (trav_info->is_legacy_root_dir)
return write_root_dir (trav_info);
else
return write_dir_cluster (trav_info);
}
static int
read_next_dir_buffer (FatTraverseInfo* trav_info)
{
FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
PED_ASSERT (!trav_info->is_legacy_root_dir, return 0);
trav_info->this_buffer = trav_info->next_buffer;
if (trav_info->this_buffer < 2
|| trav_info->this_buffer >= fs_info->cluster_count + 2) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
"Cluster %ld in directory %s is outside file system!",
(long) trav_info->this_buffer,
trav_info->dir_name);
return 0;
}
trav_info->next_buffer
= fat_table_get (fs_info->fat, trav_info->this_buffer);
return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries,
trav_info->this_buffer);
}
/* FIXME: put into fat_dir_entry_* operations */
void
fat_traverse_mark_dirty (FatTraverseInfo* trav_info)
{
trav_info->dirty = 1;
}
FatTraverseInfo*
fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster,
char* dir_name)
{
FatSpecific* fs_info = FAT_SPECIFIC (fs);
FatTraverseInfo* trav_info;
trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo));
if (!trav_info)
goto error;
trav_info->dir_name = strdup (dir_name);
if (!trav_info->dir_name)
goto error_free_trav_info;
trav_info->fs = fs;
trav_info->is_legacy_root_dir
= (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0);
trav_info->dirty = 0;
trav_info->eof = 0;
trav_info->current_entry = -1;
if (trav_info->is_legacy_root_dir) {
trav_info->buffer_size = 512 * fs_info->root_dir_sector_count;
} else {
trav_info->next_buffer = start_cluster;
trav_info->buffer_size = fs_info->cluster_size;
}
trav_info->dir_entries
= (FatDirEntry*) ped_malloc (trav_info->buffer_size);
if (!trav_info->dir_entries)
goto error_free_dir_name;
if (trav_info->is_legacy_root_dir) {
if (!ped_geometry_read (fs->geom, trav_info->dir_entries,
fs_info->root_dir_offset,
fs_info->root_dir_sector_count))
goto error_free_dir_entries;
} else {
if (!read_next_dir_buffer (trav_info))
goto error_free_dir_entries;
}
return trav_info;
error_free_dir_entries:
ped_free (trav_info->dir_entries);
error_free_dir_name:
ped_free (trav_info->dir_name);
error_free_trav_info:
ped_free (trav_info);
error:
return NULL;
}
int
fat_traverse_complete (FatTraverseInfo* trav_info)
{
if (trav_info->dirty) {
if (!write_dir_buffer (trav_info))
return 0;
}
ped_free (trav_info->dir_entries);
ped_free (trav_info->dir_name);
ped_free (trav_info);
return 1;
}
FatTraverseInfo*
fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent)
{
strcpy (tmp_buffer, trav_info->dir_name);
fat_dir_entry_get_name (parent,
tmp_buffer + strlen (trav_info->dir_name));
strcat (tmp_buffer, "\\");
return fat_traverse_begin (trav_info->fs,
fat_dir_entry_get_first_cluster (parent, trav_info->fs),
tmp_buffer);
}
FatDirEntry*
fat_traverse_next_dir_entry (FatTraverseInfo *trav_info)
{
if (trav_info->eof)
return NULL;
trav_info->current_entry++;
if (trav_info->current_entry
>= fat_traverse_entries_per_buffer (trav_info)) {
if (trav_info->dirty) {
if (!write_dir_buffer (trav_info))
return NULL;
}
trav_info->current_entry = 0;
if (is_last_buffer (trav_info)) {
trav_info->eof = 1;
return NULL;
}
if (!read_next_dir_buffer (trav_info))
return NULL;
}
return trav_info->dir_entries + trav_info->current_entry;
}
FatCluster
fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs)
{
FatSpecific* fs_info = FAT_SPECIFIC (fs);
switch (fs_info->fat_type) {
case FAT_TYPE_FAT12:
case FAT_TYPE_FAT16:
return PED_LE16_TO_CPU (dir_entry->first_cluster);
case FAT_TYPE_FAT32:
return PED_LE16_TO_CPU (dir_entry->first_cluster_high)
* 65536L
+ PED_LE16_TO_CPU (dir_entry->first_cluster);
}
return 0;
}
void
fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs,
FatCluster cluster)
{
FatSpecific* fs_info = FAT_SPECIFIC (fs);
switch (fs_info->fat_type) {
case FAT_TYPE_FAT12:
PED_ASSERT (0, (void) 0);
break;
case FAT_TYPE_FAT16:
dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster);
break;
case FAT_TYPE_FAT32:
dir_entry->first_cluster
= PED_CPU_TO_LE16 (cluster & 0xffff);
dir_entry->first_cluster_high
= PED_CPU_TO_LE16 (cluster / 0x10000);
break;
}
}
uint32_t
fat_dir_entry_get_length (FatDirEntry* dir_entry)
{
return PED_LE32_TO_CPU (dir_entry->length);
}
int
fat_dir_entry_is_null_term (const FatDirEntry* dir_entry)
{
FatDirEntry null_entry;
memset (&null_entry, 0, sizeof (null_entry));
return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0;
}
int
fat_dir_entry_is_active (FatDirEntry* dir_entry)
{
if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0;
if ((unsigned char) dir_entry->name[0] == 0) return 0;
if ((unsigned char) dir_entry->name[0] == 0xF6) return 0;
return 1;
}
int
fat_dir_entry_is_file (FatDirEntry* dir_entry) {
if (dir_entry->attributes == VFAT_ATTR) return 0;
if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
if (!fat_dir_entry_is_active (dir_entry)) return 0;
if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0;
return 1;
}
int
fat_dir_entry_is_system_file (FatDirEntry* dir_entry)
{
if (!fat_dir_entry_is_file (dir_entry)) return 0;
return (dir_entry->attributes & SYSTEM_ATTR)
|| (dir_entry->attributes & HIDDEN_ATTR);
}
int
fat_dir_entry_is_directory (FatDirEntry* dir_entry)
{
if (dir_entry->attributes == VFAT_ATTR) return 0;
if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
if (!fat_dir_entry_is_active (dir_entry)) return 0;
return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR;
}
int
fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs)
{
FatSpecific* fs_info = FAT_SPECIFIC (fs);
FatCluster first_cluster;
if (!fat_dir_entry_is_file (dir_entry)
&& !fat_dir_entry_is_directory (dir_entry))
return 0;
first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs);
if (first_cluster == 0
|| fat_table_is_eof (fs_info->fat, first_cluster))
return 0;
return 1;
}
/*
decrypts silly DOS names to FILENAME.EXT
*/
void
fat_dir_entry_get_name (FatDirEntry*dir_entry, char *result) {
int i;
char *src;
src = dir_entry->name;
for (i=0; i<8; i++) {
if (src[i] == ' ' || src[i] == 0) break;
*result++ = src[i];
}
if (src[8] != ' ' && src[8] != 0) {
*result++ = '.';
for (i=8; i<11; i++) {
if (src[i] == ' ' || src[i] == 0) break;
*result++ = src[i];
}
}
*result = 0;
}
#endif /* !DISCOVER_ONLY */