<< >> Título

Sistema de ficheros básico


En este trabajo se creará un sistema de ficheros muy primitivo a partir de un disco simulado. Partirán de unas funciones elementales que gestionan un disco virtual, definidas en el Sistema de Bloques Físicos. A partir de ellas, confeccionarán un módulo funcional que permita operar con ficheros muy simples, llamados ficheros básicos.

El fichero básico

Un fichero básico es una secuencia de bloques de datos, los cuales podrán leerse o escribirse con funciones que ustedes desarrollarán. El fichero básico se identifica mediante un número: carece de nombre simbólico. El número que identifica un fichero básico se llamará índice a fichero básico (IFB).

Un fichero básico estará compuesto de un bloque de fichero básico (BFB) y un conjunto de bloques de datos. El BFB contendrá la información necesaria para conocer qué bloques de datos pertenecen al fichero (los datos exactos dependerán del esquema de administración de espacio que ustedes escojan).

Operaciones con ficheros básicos

En la práctica, implementarán una API para trabajar con estos ficheros básicos. Las funciones que tendrán que construir básicamente son para:

* crear y destruir ficheros

* añadir bloques a un fichero o truncarlo

* leer y escribir bloques de un fichero al azar

Observen que los ficheros básicos no tienen nombre en forma de cadena de caracteres, sino un simple identificador numérico. Observen también que los accesos al cotenido de un fichero básico son por bloques completos, nunca por bytes individuales. El desarrollo de servicios más elaborados y próximos a la interfaz de UNIX o MS-DOS se realiza en el nivel del Sistema de Ficheros Simbólico, que es otra práctica de la asignatura.

La API está definida y se expone en estas páginas. Ustedes sólo tendrán que implementarla, eso sí, de forma que cumpla con las especificaciones dadas.

Organización física del disco

Desde la óptica del programador, el punto de partida del Sistema de Ficheros Básico es un conjunto de servicios para gestionar un disco virtual, estructurado como una secuencia de bloques físicos contiguos de tamaño fijo. Estos servicios se definen e implementan en el nivel llamado Sistema de Bloques Físicos (SBF). Tal nivel se describe en este texto a partir de la página . Lean atentamente el apartado para tener claras las funciones del SBF. También tienen disponibles una implementación de muestra del SBF, en el archivo /prac/src/sbfisico.c, que deberían estudiar y utilizar en sus compilaciones.

Estructura del sistema de ficheros básico

Aunque tendrán cierta libertad, el sistema de ficheros que diseñen se ceñirá a la estructura que se muestra en este diagrama.

Salvo el superbloque, las distintas áreas del disco tienen un tamaño variable, que se define cuando se formatea el disco. Una vez formateado, no se pueden alterar los tamaños de esas áreas.

La rutina de formateo se invoca de la siguiente forma:

FormateaSFB ( nombre_fichero, BF_reservados,
tamaño_DFE, tamaño_bloque_datos, número_de_ficheros );

Campos del superbloque

El primer bloque del SFB es el superbloque, lo cual viene impuesto por la interfaz del SBF. El Sistema de Ficheros Básico habrá de modificar en el formateo algunos campos del superbloque:

WORD BF_bloque_datos

Bloques físicos por cada bloque de datos
WORD bytes_bloque_datos
Tamaño efectivo en bytes de un bloque de datos
DWORD num_BF_reservados
Bloques físicos reservados por el SFS
WORD tam_BFB
Tamaño en bytes de un bloque de fichero básico
WORD num_BFB
Número de BFBs reservados
WORD tam_DFE
Tamaño en bytes de un DFE

Según se avance en el documento, se irán manifestando los puntos donde se determinarán la razón de ser y los valores apropiados de cada uno de estos campos.

Habrá que insistir en que los restantes campos del superbloque no se deberían alterar directamente en este nivel.

Bloques físicos reservados

A continuación existirá un área de bloques reservados, que podría utilizar el Sistema de Ficheros Simbólico con fines particulares. Una vez formateado el disco, el Sistema de Ficheros Básico no debería trabajar con estos bloques reservados.

El tamaño de esta área viene dado por el argumento BF_reservados de la rutina de formateo. El área de bloques reservados puede ser nula (tamaño cero).

BFBs: Bloques de ficheros básicos

Los bloques de ficheros básicos (BFB) son una serie de estructuras donde se almacena la información de cada uno de los ficheros de este sistema. La cantidad total de BFB se determina en el momento del formateo (parámetro número_de_ficheros).

El contenido del BFB dependerá de la implementación que ustedes escojan. Campos típicos del BFB pueden ser: tamaño del fichero (en número de bloques), apuntadores a los bloques de datos del fichero, un campo que indique si el BFB está libre u ocupado, etc. Aparte, hay que reservar espacio para el DFE (ver siguiente apartado).

Cada BFB ocupará un tamaño fijo, que será determinado por ustedes. Habrán de calcular cuántos BFB caben en un bloque físico, así como cuántos bloques físicos hacen falta para albergar todos los BFB requeridos.

El DFE

El Sistema de Ficheros Básico no ofrece ninguna facilidad como nombres simbólicos de ficheros, permisos de uso, fechas de última modificación, etc. Previendo que otros usuarios quieran añadir campos a sus ficheros, se define un área por cada BFB llamada descriptor de fichero extendido (DFE).

El espacio reservado para el DFE en cada BFB es fijo y se declara en el momento del formateo del disco (argumento tamaño_DFE).

En esta práctica no se han de preocupar de qué contiene el DFE. Simplemente consideren que es un espacio adicional reservado en cada BFB.

Bloques reservados por el S.F. Básico

El sistema de ficheros básico puede necesitar una serie de bloques físicos para almacenar información de control. Por ejemplo, si eligen el mapa de bits como estructura para controlar el espacio libre, tendrán que reservar un espacio en disco para almacenarlo.

El número de bloques reservados queda a criterio de los implementadores, o sea, ustedes. Su situación siempre ha de ser posterior a los BFB, aunque no necesariamente contigua a ellos.

Bloques de datos

Lo restante del disco son bloques de datos utilizables para almacenar información. Todos los bloques de datos tendrán el mismo tamaño, que vendrá dado en número de bloques físicos necesarios para un bloque de datos. Un bloque de datos ocupa exactamente tamaño_bloque_datos bloques físicos.

Si emplean asignación de espacio enlazada, parte del bloque de datos se consume en los encadenamientos. Por ello hay un campo del superbloque que indica cuántos bytes de un bloque de datos son realmente utilizables: bytes_bloque_datos.

Los bloques de datos habrán de estar numerados de UNO en adelante.

Interfaz de programación

A continuación se describe la interfaz proporcionada por esta práctica.

Si usted va a realizar la práctica del Sistema de Ficheros Básico, deberá implementar las funciones aquí descritas.

Si usted va a realizar la práctica del Sistema de Ficheros Simbólico, ha de leer estas páginas como referencia de los servicios que se le proporcionan en el Sistema de Ficheros Básico.

Ficheros cabeceras

La interfaz queda accesible con el siguiente fichero cabecera:

#include <sfbasico.h>

A su vez, sfbasico.h tendrá que incluir lo siguiente:

#include <sbfisico.h>

#include <errores.h>

#include <superblq.h>

donde se declaran la interfaz con el Sistema de Bloques Físicos, los tipos básicos, las constantes de error y la estructura del superbloque.

Los ficheros cabeceras se encuentran instalados en el directorio /prac/include

Tipos de datos. Variables y constantes

La práctica puede implementar un almacenamiento de espacio contiguo. En tal caso, en el fichero sfbasico.h debería estar definido el símbolo CONTIGUO:

#define CONTIGUO

Otras constantes se utilizan para que niveles superiores sepan cuáles son ciertos límites del Sistema de Ficheros Básico:

#define MAX_BLOQUES_CONTIGUOS 1

#define MAX_FICHEROS 20000

La primera define cuántos bloques contiguos se pueden solicitar; sólo tiene sentido en almacenamiento contiguo. La segunda define la cantidad máxima de ficheros que puede tener un SFB.

Los valores expuestos son sólo a título de ejemplo.

Este es el tipo de datos para trabajar con ficheros básicos:

typedef unsigned short IFB;

Funciones de formateo y montaje

Tres funciones sirven para dar formato a discos, montarlos y desmontarlos. Para poder trabajar con un disco hay que montarlo con la función MontaSFB. Un disco montado ha de estar previamente formateado con FormateaSFB. El sistema se desmonta con DesmontaSFB.

Los servicios de este módulo sólo trabajarán con un único disco montado.

int FormateaSFB ( const char* nombre_fichero,

WORD BF_reservados, WORD tam_DFE,

WORD tam_bloque_datos, WORD num_ficheros );

Da formato a un disco virtual, especificado por nombre_fichero. El disco virtual ha de estar ya instalado con la función de bajo nivel InstalaDisco().

Si se produce un error, esta función devuelve -1 y la variable global Error_SF tomará alguno de estos valores:

ERR_DISCO

Error con el fichero UNIX
ERR_PARAMETRO
Parámetros no válidos
ERR_FORMATO
Error de formato del fichero

int MontaSFB ( const char* nombre_fichero, int nbloques_cache );

Esta función monta un sistema de ficheros ya formateado, que se encuentra en el fichero UNIX nombre_fichero. Para utilizar cualquier función que trabaje con ficheros básicos, hay que llamar previamente a esta función.

El parámetro nbloques_cache indica al SBF el número de bloques deseado para la caché. El SBF podría ignorar este parámetro.

No se puede mantener más de un sistema de ficheros activo. Si se llama a MontaSFB mientras se tiene un SFB activo, se considera un error.

Esta función devuelve -1 si hay un error. Algunos valores de la variable Error_SF pueden ser:

ERR_NOEXISTE

Fichero inexistente
ERR_FORMATO
Error de formato del fichero
ERR_MONTADO
Ya hay un SFB montado

int DesmontaSFB (void);

Esta función se invoca para desmontar el SFB actualmente montado.

Esta función devuelve -1 si hay un error. Algunos valores de la variable Error_SF pueden ser:

ERR_DISCO

Error con operaciones de bajo nivel sobre el fichero UNIX
ERR_INACTIVO
No hay un SFB montado

WORD TamBloqueDatos (void);

Esta función retorna el tamaño efectivo en bytes de un bloque de datos. Es una forma simple de acceder a la configuración del superbloque, y es útil para que las rutinas del Sistema de Ficheros Simbólico puedan trocear sus transferencias.

La función devuelve un (unsigned)-1 si no hay un SFB montado, con lo que Error_SF pasaría a valer ERR_INACTIVO.

Creación y destrucción de ficheros

Dos funciones, CreaFB y DestruyeFB, se encargan crear y destruir ficheros básicos.

IFB CreaFB ( WORD num_bloques );

La función CreaFB ubica un bloque de fichero básico y le asigna num_bloques bloques de datos. El contenido de los bloques de datos es indefinido.

Esta función devuelve un entero que corresponde con el fichero básico seleccionado. Este valor puede usarse en las operaciones de lectura, escritura o manipulación de DFE. En caso de que no pueda completar la operación, devuelve un -1. Si no se consiguen los num_bloques, no se crea el fichero.

Algunos valores de la variable Error_SF pueden ser:

ERR_ESPACIO

No hay BFB libres o bloques de datos suficientes
ERR_INACTIVO
No hay un SFB montado

int DestruyeFB ( IFB fichero );

La función DestruyeFB desasigna todos los bloques de datos pertenecientes al fichero número fichero y libera el BFB correspondiente.

Esta función devuelve -1 si se produce un error. Algunos valores de la variable Error_SF pueden ser:

ERR_RANGO

IFB fuera de rango
ERR_OCUPADO
El BFB ya estaba disponible
ERR_INACTIVO
No hay un SFB montado

Control del tamaño del fichero

Existen tres funciones para: averiguar el tamaño de un fichero; añadir bloques al fichero; recortar el tamaño de un fichero. La unidad de datos del fichero básico es el bloque de datos: no se puede trabajar con bytes ni fracciones del tamaño efectivo de un bloque de datos.

WORD LongitudFB ( IFB fichero );

Devuelve el número de bloques de datos asignados al fichero.

Si hay alguna anomalía, se devuelve un ((unsigned)-1). Algunos valores de la variable Error_SF pueden ser:

ERR_RANGO

IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

int ExpandeFB ( IFB fichero, WORD num_bloques );

Añade num_bloques al fichero. Los bloques se añaden siempre por el final (apéndice). El contenido de los nuevos bloques es indefinido. El contenido de los bloques de datos y asignados no ha de variar.

Si no hay espacio para satisfacer la petición, no se asigna ningún bloque y la función devuelve un -1.

ExpandeFB devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_ESPACIO

Espacio insuficiente: no se asignan los bloques pedidos
ERR_RANGO
IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

int TruncaFB ( IFB fichero, WORD num_bloques );

La función TruncaFB ajusta el tamaño de fichero a exactamente num_bloques. num_bloques ha de ser menor o igual al tamaño actual del fichero, y puede valer cero.

Los bloques de datos desasignados deberían quedar disponibles para otros ficheros.

La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_PARAMETRO

num_bloques es mayor que el tamaño del fichero
ERR_RANGO
IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

Lectura y escritura de bloques

Dos funciones primitivas se encargan de leer y escribir información en los ficheros básicos. Los accesos son por bloques de datos completos.

int LeeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer );

Esta función lee el bloque de datos num_bloque perteneciente a fichero y lo deposita en la zona de memoria apuntada por bufer.

El llamador de esta función es responsable de que bufer apunte a un lugar correcto y que tenga espacio suficiente para albergar un bloque de datos, que viene determinado por el campo bytes_bloque_datos del superbloque del S.F.B. actualmente montado.

La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_NOEXISTE

num_bloque está fuera del rango del fichero
ERR_RANGO
IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

int EscribeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer );

La función EscribeBloqueFB copia la zona de memoria apuntada por bufer en el bloque de datos num_bloque perteneciente a fichero. Se copian exactamente los bytes que ocupa un bloque de datos del S.F.B. actualmente montado, según el campo bytes_bloque_datos del superbloque.

El llamador de esta función es responsable de que bufer apunte a un lugar correcto en la memoria.

La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_NOEXISTE

num_bloque está fuera del rango del fichero
ERR_RANGO
IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

Acceso al descriptor de fichero extendido (DFE)

Se suministran dos funciones para que el Sistema de Ficheros Simbólico guarde o recoja información descriptora de los ficheros, que viene almacenada en el Descriptor de Fichero Extendido (DFE) del BFB (ver página ).

int LeeDFE ( IFB fichero, void* estructura );

Esta función copia el DFE de fichero en la zona de memoria apuntada por estructura. El llamador de la función es el responsable de que estructura apunte a una dirección de memoria correcta, capaz de albergar un DFE.

El tamaño del DFE ha de estar almacenado en el campo tam_DFE del superbloque del S.F. actualmente montado.

La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_RANGO

IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

int EscribeDFE ( IFB fichero, void* estructura );

Esta función copia la zona de memoria apuntada por estructura en el DFE de fichero. Se copian exactamente los bytes que ocupa un DFE del S.F.B. actualmente montado, según el campo tam_DFE del superbloque. El llamador de la función es el responsable de que estructura apunte a una dirección de memoria correcta.

La función devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_RANGO

IFB fuera de rango
ERR_LIBRE
IFB no ocupado
ERR_INACTIVO
No hay un SFB montado

Gestión elemental de los bloques de datos

Las funciones anteriores se bastan para proporcionar una interfaz al Sistema de Ficheros Simbólico. Sin embargo, se ha de suministrar un par de funciones para administrar los bloques de datos.

Sería muy recomendable que las funciones que gestionan bloques de datos: CreaFB, DestruyeFB, ExpandeFB, TruncaFB; llamaran a estas dos funciones.

DWORD ReservaBloqueDatos (void);

Esta función reserva un bloque de datos libre.

Devuelve un número que identifica el bloque de datos asignado.

Si se da algún error o no hay más espacio disponible, se devuelve (unsigned)-1. Algunos valores de la variable Error_SF pueden ser:

ERR_ESPACIO

No hay más bloques de datos libres
ERR_INACTIVO
No hay un SFB montado

int LiberaBloqueDatos ( DWORD bloque );

Esta función devuelve al espacio libre el bloque de datos indicado por bloque.

Devuelve un -1 en caso de error. Algunos valores de la variable Error_SF pueden ser:

ERR_RANGO

Bloque de datos fuera de rango
ERR_LIBRE
Bloque de datos ya disponible
ERR_INACTIVO
No hay un SFB montado

Funciones sólo implementadas en asignaciones contiguas

En caso de que se implemente una asignación de espacio contigua, se han de implementar dos funciones para tomar conjuntos de bloques de datos.

DWORD ReservaSegmentoDatos ( DWORD num_bloques );

Reserva num_bloques bloques de datos contiguos. Devuelve el número de bloque del primer bloque de datos del conjunto.

int LiberaSegmentoDatos ( DWORD bloque_inicial, DWORD num_bloques );

Devuelve al espacio libre un conjunto de bloques de datos, un total de num_bloques a partir de bloque_inicial.

Las condiciones de error serían similares a las dos anteriores funciones.

Función de informe

void InformeSFB (void);

Esta función imprimirá por la salida estándar un informe con las estadísticas de uso y el estado general del sistema de ficheros actualmente montado, si lo hay.

Esta función no retorna nada y no genera ningún error.

Ejemplos de uso

En este apartado se muestran algunos ejemplos simples del uso de los servicios del Sistema de Ficheros Básico.

Los ejemplos son fragmentos de código para insertar en un programa. Por mayor legibilidad, los ejemplos están escritos en C++.

Instalación y formato de un sistema

En este ejemplo, se instala y formatea una unidad de disco.

#include <sfbasico.h>

// ...

// Se instala un disco de 12000x512 = 6Mbytes

InstalaDisco ( "disco.dat", 512, 12000 );

// Estos dos valores provendrían de un nivel superior

int reservados = 24; // bloques físicos reservados

int dfe = 64; // espacio reservado para el DFE

// Se da formato al disco con

// tamaño de bloque = 2 bloques físicos(1Kbyte)

// y un máximo de 500 ficheros

FormateaSFB ( "disco.dat", reservados, dfe, 2, 500 );

Utilización de un disco

Este ejemplo muestra cómo se ha de montar y desmontar un disco para poder trabajar con él.

#include <sfbasico.h>

// ...

MontaSFB ("disco.dat");

// Crea un fichero tonto de 10 bloques

int fich = CreaFB (10);

// añade un bloque más

ExpandeFB(fich,1);

// Desmonta el disco

DesmontaSFB();

Acceso a bloques de datos

Este último ejemplo de uso muestra, entre otras cosas, cómo recuperar la información del superbloque para saber en este caso el tamaño en bytes del bloque de datos.

#include <sfbasico.h>

// ...

struct Superbloque SB; // Guardará los parámetros del disco

MontaSFB ("disco.dat");

LeeSuperbloque (&SB);

// Reserva espacio para escribir en un bloque

char* bufer = new char[SB->bytes_bloque_datos];

// Otra forma de hacerlo

char* bufer = new char[TamBloqueDatos()];

// Escribe algo en el primer bloque del fichero 33

strcat(bufer,"Hola, mundo\n");

EscribeBloqueFB (33,0,bufer);

// lee el tercer bloque del fichero 98

LeeBloqueFB (98,2,bufer);

for (int i=0; i<SB->bytes_bloque_datos; i++)

{

printf ( "bufer[%d] = %d\n", i, bufer[i] );

}

// Desmonta el disco, que ya no hace falta

DesmontaSFB();


<< >> Título