/************************************************************************/
/*									*/ 
/*	SFBASICO.C							*/ 
/*	==========							*/ 
/*									*/ 
/*	Implementación de un sistema de ficheros muy simple,		*/ 
/*	para realizar prácticas de diseño de sistemas operativos.	*/ 
/*									*/ 
/*	Características:						*/ 
/*									*/ 
/*		- Sólo un directorio raíz				*/ 
/*		- Ficheros de tamaño fijo				*/ 
/*		- Transferencias por bloques (no por bytes)		*/ 
/*		- Si se borra un fichero, los bloques ocupados		*/ 
/*		  no quedan disponibles					*/ 
/*									*/ 
/*	Versión 2.0b							*/ 
/*	                                                                */ 
/*	© José Miguel Santos, Carmelo Rubén García, Gabino Padrón,	*/ 
/*      1996-97-98							*/ 
/*									*/ 
/************************************************************************/ 

 
/* Ficheros cabeceras de la aplicación */ 
 
#include "sfbasico.h"	/* Interfaz pública de este módulo */ 
#include "sffisico.h"	/* Funciones de acceso a disco */ 
#include "errores.h"	/* Códigos de error */ 
 
#include "descr.h"	/* Descriptor de sistema de ficheros y superbloque */ 
#include "bfb.h"	/* Estructura Bloque de Fichero Básico (BFB) */ 
 
 
/* Ficheros cabeceras para las bibliotecas del C */ 
 
#include <assert.h> 
#include <string.h> 
#include <stdlib.h> 
 


/* En esta implementación, todos los ficheros tienen longitud fija */ 
/* Habrá que reformarlo para permitir longitudes variables */ 
 
#define NUM_BLOQUES_FICHERO	4 
 
 
 
/* Macro para conocer si hay un disco montado */ 
/* Se utiliza por todo el código */ 
 
#define MONTADO \
{ Error_SF=ERR_SINERROR; \
  if (DESC.montado==0) \
   { Error_SF=ERR_INACTIVO; return -1; } \
} 
 
 
 
 
 
 
/************************************************************************/ 
/*									*/ 
/*			FUNCIONES PÚBLICAS DEL MÓDULO			*/ 
/*				IMPLEMENTACIÓN				*/ 
/*									*/ 
/************************************************************************/ 
 
 
 
/* Reserva espacio para un fichero */ 
/* En esta implementación, reserva un número fijo de bloques */ 
 
static DWORD ReservaEspacio (void); 
 
 


/* Función FormateaSFB (nombre_fichero,args[])			*/ 
/*								*/ 
/*	Da formato a un disco, cuya ruta UNIX es el parámetro	*/ 
/*	"nombre_fichero". "args" es un vector de cadenas de	*/ 
/*	caracteres que contienen los parámetros para el formato.*/ 
/*	Después del último parámetro, debería venir una cadena	*/ 
/*	nula.							*/ 
/*								*/ 
/*	En esta implementación básica, se pasan dos parámetros:	*/ 
/*								*/ 
/*		args[0] - Cantidad de BFBs			*/ 
/*		args[1] - Tamaño de bloque de datos		*/ 
 

/* Funciones secundarias del formato */
/* para dar formato a los BFB y los bloques de datos */
 
static void FormatoBFBs(void); 
static void FormatoBloquesDatos(void); 
 


int FormateaSFB ( const char* nombre_fichero, const char* args[] ) 
{ 
	DWORD nbloques_BFB; 
 
	/* No puede haber un disco montado mientras se formatea */ 
	if (DESC.montado==1) 
	 { Error_SF=ERR_MONTADO; return -1; } 
 
	/* Lee los parámetros del formateo */ 
 
	if ( args[0]==0 || args[1]==0 || args[2]!=0 ) 
	{ Error_SF = ERR_PARAMETRO; return -1; } 
 
	DESC.num_BFB = atoi(args[0]); 
	DESC.tam_bloque_datos = atoi(args[1]); 
 
	if ( DESC.num_BFB<=0 || DESC.tam_bloque_datos<=0 )
	 { Error_SF = ERR_PARAMETRO; return -1; } 

 
	/* Monta el disco físico y recoge los parámetros del disco */ 
 
	Error_SF = ERR_SINERROR; 
	MontaDisco(nombre_fichero,0); 
	LeeDatosDisco (&DESC.tam_bloque_fisico, &DESC.num_bloques_fisicos); 
	if (HAY_ERROR) { return -1; } 
 
 
	/* Comprueba si hay espacio suficiente: */ 
	/*	- el BFB tiene que caber en un bloque físico */ 
	/*	- los BFBs no deben ocupar todo el disco */ 
	/*	- debería haber bloques de datos suficientes */
	/*	  para crear todos los ficheros */ 
 
	nbloques_BFB = (DESC.num_BFB*TAM_BFB)/(DESC.tam_bloque_fisico)+1; 
 
	if (   DESC.tam_bloque_fisico < TAM_BFB 
	    || nbloques_BFB >= DESC.num_bloques_fisicos 
	    || DESC.num_bloques_fisicos < DESC.tam_bloque_datos*DESC.num_BFB + 2
	   ) 
	{ 
	  DesmontaDisco(); 
	  Error_SF = ERR_ESPACIO; 
	  return -1; 
	} 
 
 
	/* Calcula tamaños y límites */ 
 
	DESC.org_BFB = 1; 
	DESC.org_datos = nbloques_BFB+1; 
	DESC.nbloques_datos = 
	 (DESC.num_bloques_fisicos-DESC.org_datos) / DESC.tam_bloque_datos; 
	assert(DESC.nbloques_datos>0); 
	DESC.primer_bloque_libre = 0; 
	DESC.ultimo_bloque_datos = DESC.nbloques_datos-1; 
 
	/* Si todo está correcto, */ 
	/* escribe la nueva información en el superbloque del disco */ 
 
	EscribeSuperbloque(&DESC); 
	DesmontaDisco(); 
 
 
	/* Se monta el nuevo sistema, para inicializar bloques, etc. */ 
 
	MontaSFB(nombre_fichero,0); 
 
	/* Da formato a los bloques de fichero básicos */ 
 
	FormatoBFBs(); 
 
	/* Da formato a los bloques de datos */ 
	/* (en esta implementación, no se hace nada con ellos) */ 
 
	FormatoBloquesDatos(); 
 
	/* Desmonta el disco y retorna */ 
 
	return DesmontaSFB(); 
 
} /* FormateaSFB() */ 
 
 
 
/* Implementación de las funciones secundarias */ 
 

static 

void FormatoBFBs (void) 
{ 
	int indice; 
	for ( indice=0; indice<DESC.num_BFB; indice++ ) 
	{ 
	  BFB bfb; 
 
	  /* Da valores iniciales a los campos del BFB */ 
	  bfb.estado = BFB_LIBRE; 
	  bfb.id = indice; 
	  bfb.nombre[0] = 0; 
	  bfb.bloque=0; 
 
	  /* Guarda el BFB en disco */ 
	  BFB_guarda (&bfb,indice); 
	} 
} 
 
 
 
static 

void FormatoBloquesDatos (void) 
{ 
	/* En esta implementación, no hace falta hacer nada */ 
} 
 
 
 
 
/* Función MontaSFB (nombre_fichero,nbloques_cache)		*/ 
/*								*/ 
/*	Monta un disco formateado, cuya ruta UNIX es el 	*/ 
/*	parámetro "nombre_fichero". "nbloques_cache" es		*/ 
/*	la cantidad de bloques en la caché, para pasárselo	*/ 
/*	como parámetro a MontaDisco().				*/ 
/*	  Para usar un SFB, primero hay que invocar a esta	*/ 
/*	función.						*/ 
 
 

int MontaSFB ( const char* nombre_fichero, int nbloques_cache ) 
{ 
	/* Monta el disco en bajo nivel */ 
 
	Error_SF = ERR_SINERROR; 
	MontaDisco(nombre_fichero,nbloques_cache); 
	if (HAY_ERROR) return -1; 
 
	/* Lee el superbloque y configura DESC */ 
 
	LeeSuperbloque(&DESC);
	if (HAY_ERROR) return -1; 
 
	DESC.montado=1; 
 
	/* Habilita los datos para los BFB */ 
 
	BFB_empieza_sesion (); 
 
	return 0; 
 
} /* MontaSFB() */ 
 
 
 
/* Función DesmontaSFB ()					*/ 
/*								*/ 
/*	Desmonta el disco actualmente montado.			*/ 

 

int DesmontaSFB (void) 
{ 
	Error_SF = ERR_SINERROR; 
 
	/* Guarda el superbloque, por si acaso */ 
	EscribeSuperbloque(&DESC); 
 
	/* Inhabilita datos de los BFB */ 
	BFB_termina_sesion(); 
 
	DESC.montado=0; 
	return 0;
 
} /* DesmontaSFB() */ 
 
 
 
/* Función CreaFB (nombre_fichero,num_bloques)			*/ 
/*								*/ 
/*	Crea un fichero, de nombre "nombre_fichero".		*/ 
/*	El parámetro "num_bloques" se ignora en esta		*/ 
/*	implementación, ya que se trabaja con ficheros		*/ 
/*	de tamaño fijo.						*/ 
/*								*/ 
/*	Hay que reformar el código para permitir ficheros	*/ 
/*	de cualquier tamaño.					*/ 
 
 

IFB CreaFB ( const char* nombre_fichero, WORD num_bloques ) 
{ 
	int indice; 
	int buscando_hueco; 
	IFB ifb_libre; 
        BFB bfb; 
 
	/* Verifica que el disco está montado */ 
	MONTADO 
 
        /* Si el nombre es la cadena vacía, es un error */ 
	if (nombre_fichero[0]==0) 
	 { Error_SF=ERR_PARAMETRO; return -1; } 
 
 
	/* Busca una entrada libre */ 
	/* y se asegura de que ningún otro BFB tiene el mismo nombre */ 
 
	buscando_hueco=1; 
 
	for ( indice=0; indice<DESC.num_BFB; indice++ ) 
	{ 
	  BFB_recoge (&bfb,indice); 
	  if (HAY_ERROR) return NINGUN_IFB; 
 
	  switch ( bfb.estado ) 
	  { 
	    /* Si está ocupado, se compara el nombre */ 
	    case BFB_OCUPADO: 
	      if ( !strncmp ( bfb.nombre, nombre_fichero, LONG_NOMBRE ) ) 
	      { 
		/* Si el nombre ya existía, es un error */ 
		Error_SF = ERR_EXISTE; 
		return NINGUN_IFB; 
	      } 
	    break; 
 
	    /* Si está libre, guarda la dirección del bloque */ 
	    /* para reservarlo posteriormente */ 
	    case BFB_LIBRE: 
	      if ( buscando_hueco ) 
	      { 
		buscando_hueco=0; 
                ifb_libre = indice; 
	      } 
	    break; 
 
	    default: 
	      Error_SF = ERR_CORRUPTO; 
	      return NINGUN_IFB; 
 
	  } /* switch */ 
 
	} /* for */ 
 
 
	/* Si no se ha encontrado un BFB libre */ 
	if (buscando_hueco) 
        { 
	  Error_SF = ERR_ESPACIO; 
	  return NINGUN_IFB; 
	} 
 
        /* Si se ha encontrado un BFB libre */ 
	/* Carga en memoria el BFB correspondiente */ 
 
        BFB_recoge (&bfb,ifb_libre); 
 
        /* Construye la estructura del nuevo BFB */ 
 
	bfb.estado = BFB_OCUPADO; 
	bfb.bloque = ReservaEspacio(); 
	if (HAY_ERROR) return NINGUN_IFB; 
	strncpy(bfb.nombre,nombre_fichero,LONG_NOMBRE); 
 
	/* Guarda el BFB en disco */ 
 
	BFB_guarda (&bfb,ifb_libre); 
 
        return ifb_libre; 
 
 
}  /* CreaFB() */ 
 
 
 
/* Función DestruyeFB (fichero)					*/ 
/*								*/ 
/*	Destruye el fichero referenciado por "fichero".		*/ 
/*								*/ 
/*	En esta implementación, no se desasignan los bloques	*/ 
/*	asignados al fichero. Hay que reformar el código para	*/ 
/*	permitirlo.						*/ 
 
 

int DestruyeFB (IFB fichero) 
{ 
	BFB bfb; 
 
	/* Verifica que el disco está montado */ 
	MONTADO 
 
        /* Lee de disco el BFB */ 
	BFB_recoge (&bfb,fichero); 
	if (HAY_ERROR) return -1; 
 
        /* Comprueba que es un ifb ocupado */

	if (bfb.estado==BFB_LIBRE) 
	{ 
	  Error_SF = ERR_LIBRE; 
	  return -1; 
	} 
 
	/* Modifica la estructura del BFB */

	/* Habría que liberar el espacio que ocupaba */
	/* BFB_cambia_longitud(bfb,0); */ 
 
	bfb.estado = BFB_LIBRE; 
 
        /* Actualiza la estructura en el disco */ 
 
        BFB_guarda (&bfb,fichero); 
	if (HAY_ERROR) return -1; 
 
	return 0; 
 
}  /* DestruyeFB() */ 
 
 
 
/* Función BuscaFB (nombre_fichero)				*/ 
/*								*/ 
/*	Busca el fichero especificado dentro de los BFBs.	*/ 
/*	Si lo encuentra, devuelve el IFB correspondiente.	*/ 
/*	Si no lo encuentra, devuelve NINGUN_IFB.		*/ 
 
 

IFB BuscaFB ( const char* nombre_fichero ) 
{ 
	int ifb; 
 
	/* Si el nombre es la cadena vacía, es un error */ 
	if (nombre_fichero[0]==0) 
	 { Error_SF=ERR_PARAMETRO; return NINGUN_IFB; } 
 
	/* Verifica que hay un disco montado */ 
        MONTADO 
 
	/* Busca en todos los BFBs */ 
 
	for ( ifb=0; ifb < DESC.num_BFB; ifb++ ) 
	{ 
          BFB bfb; 
          BFB_recoge (&bfb,ifb); 
	  if (HAY_ERROR) return NINGUN_IFB; 
 
	  /* Si lo encuentra, deja de buscar y retorna */ 
	  if ( bfb.estado==BFB_OCUPADO ) 
	   if ( !strncmp ( bfb.nombre, nombre_fichero, LONG_NOMBRE ) ) 
	    return ifb; 
	} 
 
	/* Si termina de barrer y no encuentra nada */ 
 
	return NINGUN_IFB; 
 
} /* BuscaFB() */ 
 
 
 
/* Función EntradaDir (entrada,nombre)				*/ 
/*								*/ 
/*	"entrada" es el número de orden dentro del direc-	*/ 
/*	torio del fichero que se quiere encontrar.		*/ 
/*	Si la entrada existe, devuelve el IFB correspondiente.	*/ 
/*	Si no existe, devuelve NINGUN_IFB.			*/ 
/*	En "nombre" aparece el nombre simbolico del fichero	*/ 
 
 

IFB EntradaDir ( int entrada, char* nombre ) 
{ 
	BFB bfb; 
	int contador=0; 
	IFB ifb; 
 
	MONTADO 
 
	/* Busca en el directorio la entrada correspondiente */ 
	for ( ifb=0; ifb<DESC.num_BFB; ifb++ ) 
	{ 
	  BFB_recoge(&bfb,ifb); 
	  if (bfb.estado==BFB_OCUPADO) 
	  { 
	    if (contador==entrada) 
	    { 
	      strncpy (nombre,bfb.nombre,LONG_NOMBRE); 
	      return ifb; 
	    } 
	    else contador++; 
	  } 
	} 
 
	/* Si el numero de entrada es muy grande, 
	   se devuelve un ifb nulo */ 
 
	nombre[0]=0; 
	Error_SF = ERR_RANGO; 
	return NINGUN_IFB; 
 
} /* EntradaDir() */ 
 
 
 
/* Función LongitudFB (fichero)					*/ 
/*								*/ 
/*	Devuelve la longitud del fichero referenciado por	*/ 
/*	"fichero".						*/ 
/*								*/ 
/*	En esta implementación, se devuelve una cantida fija.	*/ 
/*	Si se reforma el código para permitir ficheros de 	*/ 
/*	tamaño variable, habrá que retocar esta función.	*/ 
 
 

WORD LongitudFB ( IFB fichero ) 
{ 
	BFB bfb; 
	MONTADO 
	BFB_recoge (&bfb,fichero); 
	return HAY_ERROR ? (WORD)-1 : NUM_BLOQUES_FICHERO;
	 
} /* LongitudFB() */ 
 

 
/* Función CambiaLongitudFB (fichero,num_bloques)		*/ 
/*								*/ 
/*	Cambia la longitud del fichero referenciado por		*/ 
/*	"fichero" a "num_bloques". Por tanto, puede hacer	*/ 
/*	crecer o encoger el fichero. "num_bloques" puede	*/ 
/*	valer cero.						*/ 
/*								*/ 
/*	En esta implementación, no se hace nada, ya que se	*/ 
/*	trabaja con ficheros de longitud fija.			*/ 
/*	Si se reforma el código para permitir ficheros de 	*/ 
/*	tamaño variable, habrá que retocar esta función.	*/ 
 
 

int CambiaLongitudFB ( IFB fichero, WORD num_bloques ) 
{ 
	BFB bfb; 
 
	MONTADO 
 
	BFB_recoge(&bfb,fichero); 
	/* ... cambia la longitud del fichero ... */ 
	/* BFB_guarda(&bfb,fichero); */ 
 
	return HAY_ERROR ? -1 : 0; 
 
}  /* CambiaLongitudFB() */
 
 
 
 
/* Función TamBloqueDatos ()					*/ 
/*								*/ 
/*	Devuelve el tamaño en bytes del bloque de datos		*/ 
/*	empleado por el sistema de ficheros montado.		*/ 
 

WORD TamBloqueDatos (void) 
{ 
	MONTADO 
 
        return DESC.tam_bloque_datos*DESC.tam_bloque_fisico; 
} 
 
 
 
/* Función EspacioLibre()					*/ 
/*								*/ 
/*	Devuelve el espacio libre en bytes			*/ 
 

DWORD EspacioLibre (void) 
{ 
	MONTADO 
 
	return 
	    ( DESC.ultimo_bloque_datos - DESC.primer_bloque_libre + 1 ) 
          * TamBloqueDatos(); 
} 
 
 
 
/* Función FicherosLibres()					*/ 
/*								*/ 
/*	Devuelve el número de BFBs disponibles			*/ 
 

WORD FicherosLibres (void) 
{ 
	unsigned contador=0; 
        IFB ifb; 
 
	MONTADO 
 
	for ( ifb=0; ifb<DESC.num_BFB; ifb++ ) 
	{ 
          BFB bfb; 
	  BFB_recoge (&bfb,ifb); 
 
          if ( bfb.estado==BFB_LIBRE ) contador++; 
	} 
 
        return contador; 
} 
 
 
 
/* Funciones:							*/ 
/*	LeeBloqueFB (fichero,num_bloque,bufer)			*/ 
/*	EscribeBloqueFB (fichero,num_bloque,bufer)		*/ 
/*								*/ 
/*	Lee/escribe el bloque "num_bloque" del fichero		*/ 
/*	referenciado por "fichero" en/desde la dirección	*/ 
/*	de memoria apuntada por "bufer".			*/ 
/*								*/ 
/*	Como son funciones muy similares, se hace uso de una	*/ 
/*	función común de acceso a un bloque del FB, llamada	*/ 
/*	AccedeBloqueFB()					*/ 
 
 
enum Oper { LECTURA=0, ESCRITURA=1 }; 
 
static 
int AccedeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer, enum Oper ); 
 
 

int LeeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer ) 
{ 
	return AccedeBloqueFB(fichero,num_bloque,bufer,LECTURA); 
} 
 

int EscribeBloqueFB ( IFB fichero, WORD num_bloque, const void* bufer ) 
{ 
	return AccedeBloqueFB(fichero,num_bloque,(void*)bufer,ESCRITURA); 
} 
 
 
 
/* Función general para lecturas y escrituras */ 
 
static 

int AccedeBloqueFB ( IFB fichero, WORD num_bloque, void* bufer, enum Oper tipo ) 
{ 
	BFB bfb; 
 
	/* Verifica que el disco está montado */ 
	MONTADO 
 
	/* Si el bloque se sale de los límites del fichero, retorna */ 
	if ( num_bloque>=NUM_BLOQUES_FICHERO ) 
	{ 
	  Error_SF = ERR_RANGO; 
	  return -1; 
	} 
 
	/* Lee el BFB correspondiente */ 
	BFB_recoge (&bfb,fichero); 
 
	if (bfb.estado==BFB_LIBRE) 
	{ 
	  Error_SF = ERR_LIBRE; 
	  return -1; 
	} 
 
 
	/* Accede al bloque de datos */ 
	{ 
 
	DWORD bloque_datos = bfb.bloque + num_bloque; 
	DWORD bloque_fisico =   DESC.org_datos 
			      + bloque_datos*DESC.tam_bloque_datos; 
 
	int nbloque; 
 
	if ( bloque_datos > DESC.nbloques_datos ) 
	 { Error_SF = ERR_RANGO; return -1; } 
 
	/* Como un bloque de datos puede consistir en varios bloques físicos, 
	   se entra en un bucle de transferencias */ 
 
	for ( nbloque=0; nbloque<DESC.tam_bloque_datos; nbloque++ ) 
	{ 
	  /* Calcula de qué bloque físico se trata */ 
	  DWORD bloque_transfer = bloque_fisico + nbloque; 
 
	  /* Calcula la dirección de memoria para este bloque */ 
	  void* direccion_memoria =    ((char*)bufer) 
				     + (DESC.tam_bloque_fisico*nbloque); 
 
	  /* Realiza la transferencia */ 
	  if (tipo==LECTURA) 
	    LeeBloqueFisico (bloque_transfer,direccion_memoria); 
	  else 
	    EscribeBloqueFisico (bloque_transfer,(const void*)direccion_memoria); 
 
	  if (HAY_ERROR) return -1; 
	} 
 
	} 
 
	return 0; 
 
}  /* AccedeBloqueFB() */ 
 
 
 
 
/* Función ReservaEspacio()					*/ 
/*								*/ 
/*	Reserva NUM_BLOQUES_FICHERO bloques del espacio libre.	*/ 
/*	Simplemente avanza el apuntador a la zona de bloques	*/ 
/*	libres.							*/ 
/*								*/ 
/*	Esta función dejará de tener sentido cuando se gestione	*/ 
/*	eficientemente el espacio libre y se trabaje con fi-	*/ 
/*	cheros de tamaño variable.				*/  

#define CHUNGO ((DWORD)-1)
 
static 

DWORD ReservaEspacio (void) 
{ 
	DWORD bloque_inicial; 
 
	assert (DESC.primer_bloque_libre<=DESC.ultimo_bloque_datos); 
        assert (DESC.montado==1); 
 
        /* Verifica si queda espacio libre */ 
	if ( DESC.primer_bloque_libre >= 
	     DESC.ultimo_bloque_datos - NUM_BLOQUES_FICHERO ) 
	{ 
	  Error_SF = ERR_ESPACIO; 
	  return CHUNGO; 
	} 
 
	/* Mueve el apuntador a los bloques libres*/ 
	bloque_inicial = DESC.primer_bloque_libre; 
	DESC.primer_bloque_libre += NUM_BLOQUES_FICHERO; 
 
	/* Por seguridad, se actualiza el superbloque */ 
	EscribeSuperbloque (&DESC); 
	if (HAY_ERROR) return CHUNGO; 
 
	return bloque_inicial; 
 
} /* ReservaEspacio() */