/************************************************************************/ /* */ /* 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.0a */ /* */ /* © José Miguel Santos, Carmelo Rubén García, Gabino Padrón, */ /* 1996-97 */ /* */ /************************************************************************/ /* 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 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 < 2 + DESC.tam_bloque_datos*DESC.num_BFB ) { 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; MONTADO /* Guarda el superbloque, por si acaso */ EscribeSuperbloque(&DESC); /* Inhabilita datos de los BFB */ BFB_termina_sesion(); DESC.montado=0; return DesmontaDisco(); } /* 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. */ 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 (DWORD)-1; } /* 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 (DWORD)-1; return bloque_inicial; } /* ReservaEspacio() */