Sistemas de archivos FAT16 / FAT32 para las tarjetas SD / MMC

INTRODUCCIÓN

 

En el artículo anterior, veíamos que gracias al controlador interno y a la arquitectura y organización de las tarjetas SD/MMC se pueden escribir y leer bloques de 512 bytes de datos con relativa facilidad.  También se vio que estas tarjetas incorporan comandos específicos para seleccionar grupos de bloques de 512 bytes y para seleccionar conjuntos de grupos de estos bloques, de modo que con un comando posterior, se borraban todos los bloques seleccionados.  Tras la lectura de este artículo se comprenderá la utilidad de este mecanismo de borrado.

Para aproximarnos a la creación y gestión de un sistema de archivos FAT en nuestro equipo electrónico, primero debemos hacer una introducción a los discos y a la manera en la que estaban organizados para que los antiguos sistemas operativos DOS los gestionasen.

Los discos, entendiéndolos como medios magnéticos, son el principal sistema de almacenamiento duradero de los ordenadores personales. Están formados por una superficie circular dividida en pistas concéntricas que contienen cada una un número de sectores de tamaño fijo. Bajo DOS, los sectores tienen un tamaño fijo de 512 bytes. Que si bien puede modificarse, es a costa de perder compatibilidad, por lo que es muy raro encontrarse con algún sistema que lo haga.

El primer sector físico de un disco contiene información especial, como el sector de arranque de los disquetes, el código de la tabla de particiones en discos duros o simplemente las instrucciones necesarias para escribir en pantalla el conocido mensaje “No es disco sistema. Retire y presione cualquier tecla”. En el caso de los discos duros, el código es relativamente sencillo: consulta la tabla de particiones ubicada en este mismo sector, determina cuál es la partición activa, dónde comienza y dónde termina. Para, a continuación, cargar el sector lógico cero de esa partición y ejecutarlo. En los disquetes no existe dicho paso intermedio y el sector cero lógico coincide con el sector cero físico. Por su sencillez, éste es el método recomendado para implementar en tarjetas MultimediaCard.

Antes de comenzar a realizar una comparativa entre los sistemas de archivos FAT16 y FAT32, conviene introducir algunos conceptos y nociones. Cabe decir además que independientemente del tipo de sistema de archivos, Windows accede a los discos con sistema de acceso de 32 ó 64 bits, pero nuestros microcontroladores podrán acceder a los sistemas de archivos de las tarjetas MultimediaCard con 16, 32 e incluso, los más valientes, con 8 bits.

 

ORGANIZANDO LA MEMORIA: CLUSTERS + PUNTEROS = SISTEMAS DE ARCHIVOS

 

MS-DOS define un concepto llamado cluster, que es una agrupación de sectores y, a su vez, la unidad mínima de memoria que es capaz de recuperar un sistema operativo. La ventaja de utilizar clusters radica en que resulta más sencillo manejar pocos clusters que muchos sectores; mientras que el inconveniente es que, al ser la menor unidad de memoria capaz de entender el sistema operativo, si en un disco un cluster son 32 Kb y escribimos un fichero con un byte, se emplean 32 Kb para ello. Por este motivo, cuanto más pequeño sea el tamaño del cluster, menor espacio se pierde. Siendo lo ideal desde esta perspectiva, si bien no siempre posible, que el tamaño de un cluster sea un sector.

Para entender cómo funciona un sistema de archivos MS-DOS, véanse los clusters como páginas de memoria. Para optimizar su acceso, al igual que en un libro, se dispondrá de un índice, llamado FAT (File Allocation Table); con punteros que señalen de algún modo a los clusters. Según estos punteros sean de 12, 16 ó 32 bits, se llamará al sistema de archivos FAT12, FAT16 ó FAT32 respectivamente.

Para poder tener un índice, todos los clusters de la partición deben estar numerados. Así, se crea una tabla con punteros al inicio de la partición, con tantos punteros como clusters haya en la misma. Para conocer cuántos punteros ha de haber en la partición, y, por tanto, cuántos clusters y cuál será el tamaño del cluster; hay que realizar un cálculo: si los punteros son de 16 bits, el número máximo de clusters al que podrán apuntar será 65535. Por tanto se divide el tamaño de nuestra partición por 65535 y se toma un número múltiplo de 512 como resultado. A modo de ejemplo, si la unidad cuenta con 827.000.000 de bytes, cada cluster tendrá 16.384 bytes y, realizando la operación inversa sabremos que tenemos 50476 clusters en la partición.

Además se define en el disco otro espacio llamado directorio raíz del disco, como un espacio, limitado en FAT12 y en FAT16, y de tamaño variable en FAT32; donde se escribirán los nombres de los ficheros, sus atributos y el primer cluster donde comienza su contenido. La forma de leer o escribir un fichero es un tanto peculiar y se introduce más adelante. De momento, con lo mencionado se pueden observar algunas ventajas e inconvenientes de utilizar un sistema de archivos FAT16 ó FAT32. No se analizan las posibilidades de FAT12, pues permite direccionar tan poca memoria, típicamente los 1.44 Mb de un disquete; que es prácticamente una estructura de datos de museo.

 

Sistema de archivos

Ventajas

Inconvenientes

FAT16

  • Mediante una tabla de sólo 65535 entradas de 2 bytes se direcciona toda la memoria. Posibilidad de incluir toda la tabla en una caché de sólo 128 Kb
  • El uso de una caché de coste ínfimo implica mayor rapidez con menor potencia de cálculo
  • El tamaño de la partición estará limitado, ya que Windows no acepta clusters de más de 32 Kb
  • Se desperdicia mucho espacio, a partir de tamaños de disco relati-vamente importantes, así, para un disco de 2 Gb el tamaño de un cluster es de 32 Kb

FAT32

  • Se desperdicia mucho menos espacio de disco
  • Prácticamente ya no hay límite para el tamaño de la partición
 Al aumentar el tamaño de la tabla, y ser sus punteros de 4 bits se pierde mucho tiempo en acceder a ella y deja de ser sencilla la implementación de una caché en microprocesadores pequeños de 16 bits.

 

LA FAT BYTE A BYTE…

 

Con los conceptos introducidos, se trata aquí de profundizar en la implementación de los sistemas de archivos FAT16 y FAT32. Para ello, primero se estudiará FAT32 y, posteriormente, se verán las pequeñas diferencias a bajo nivel que FAT16 presenta.

Para que un sistema operativo reconozca una unidad de almacenamiento con el sistema de archivos FAT32, en el primer sector lógico tienen que encontrarse una serie de datos sobre la unidad. Este sector se denomina sector de arranque, Boot Sector, o sector cero. Tras este sector hay un espacio reservado, típicamente de 32 sectores.

A continuación, se encuentra la tabla FAT propiamente dicha, que como se ha introducido, será una sucesión de entradas de cuatro bytes que apuntan a los distintos clusters de la unidad. Tras la FAT, se encuentra el directorio raíz, Root, cuyo tamaño no está fijado en el sector de arranque, como ocurre en FAT16, sino que es una cadena de clusters, comportándose como un fichero más.

 

Funcionamiento a grandes rasgos

Una entrada en el directorio contiene, entre otros datos, el nombre del fichero y el primer cluster donde se encuentra el contenido del fichero. Tómese por ejemplo que el primer cluster del fichero fuese el cluster tres. Tras leer el cluster tres, para seguir leyendo el fichero, se va a la posición de FAT correspondiente al cluster tres. Teniendo en cuenta que una entrada son cuatro bytes, en el primer sector de FAT, los bytes del doce al quince, contienen el número del siguiente cluster de la cadena, por ejemplo, 128. Para encontrar el siguiente cluster hay que volver a leer la posición en FAT correspondiente al cluster 128; que será, en el segundo sector de la FAT, los bytes del cero al tres.

 

El sector cero

Los discos con sistema de archivos FAT32 tienen información útil en el sector cero para trabajar con ellos. Esta información se encuentra almacenada en campos definidos por el estándar de Microsoft [1] y su contenido se explica a continuación.

 BS_jmpBoot (Bytes del 0 al 2): Salto a la rutina de arranque. Normalmente un disco no tendrá otra rutina de arranque que la de escribir “Retire disco y presione una tecla” o similares, salvo que sea uno de los denominados discos de arranque.

BS_OEMName (Del byte 3 al 10): Es sólo un string. Los sistemas operativos Microsoft Windows escriben “MSDOS5.0”, pese a que en su documento FAT: General Overview of On-Disk Format indican que el string “MSWIN4.1” es el recomendado para una mayor compatibilidad. Aunque quizá hayan cambiado la cadena en honor al mejor sistema operativo que ha habido en todos los tiempos…

BPB_BytsPerSec (Bytes 11 y 12): Indica el número de bytes por sector. Lo deseado para máxima compatibilidad es 512.

BPB_SecPerClus (Byte 13): Indica el número de sectores contenidos en un cluster. Sus valores legales son potencias de dos. El valor del número de bytes por cluster (BPB_BytsPerSec * BPB_SecPerClus) no debe ser mayor que 32 Kb.

Dependiendo del tamaño del disco se utilizará un valor u otro, por ejemplo, en FAT32:

64 Mb: un cluster será un sector.

128 Mb: un cluster serán dos sectores.

256 Mb: un cluster serán cuatro sectores.

32 Gb: un cluster serán treinta y dos sectores

BPB_RsvdSecCnt (Bytes 14 y 15): Indica el número de sectores reservados del volumen, comenzando por el primer sector del volumen. Este campo no puede ser cero. Para volúmenes FAT12 y FAT16 este campo no puede tener otro valor que uno. En sistemas FAT32, típicamente es 32.

BPB_NumFATs (Byte 16): Número de tablas FAT en el volumen. Este campo típicamente contiene dos para cualquier tipo de volumen FAT. Su utilidad es guardar una copia de seguridad de las cadenas de clusters para posibilitar las recuperaciones de datos.

BPB_RootEntCnt (Bytes 17 y 18): Contiene el número de entradas de 32 bytes al directorio raíz. Su valor multiplicado por 32 debe ser múltiplo de BPB_BytsPerSec. Para una máxima compatibilidad, los sistemas FAT16 suelen usar el valor 512. En sistemas FAT32, su valor puede ser cero porque el directorio raíz se estructura como un fichero y se direcciona en la tabla FAT.

BPB_TotSec16 (Bytes 19 y 20): Número de sectores del volumen. Si es cero, es porque el valor es mayor o igual que 0×10000, entonces BPB_TotSec32 debe ser distinto de cero.

BPB_Media (Byte 21): Los valores legales en este campo son 0xF0, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE y 0xFF. Si el medio es fijo se usa 0xF8 típicamente y 0xF0 si el medio no es fijo.

Se ha probado a formatear tarjetas con el valor 0xF0 y Windows no las reconocía correctamente, ya que les asignaba un tamaño de disco mucho menor que el indicado. Por tanto, pese a no ser discos fijos, al dar formato a las tarjetas se recomienda escribir en este campo el valor 0xF8, que es el que también utiliza Windows.

BPB_FATSz16 (Bytes 22 y 23): Indica el número de sectores ocupados por una tabla FAT.

Si el sistema de archivos es FAT32, este campo no se utiliza y se escribe a cero.

BPB_SecPerTrk (Bytes 24 y 25): Sólo tiene importancia para volúmenes con geometría para interrupciones 0×13.

BPB_NumHeads (Bytes 26 y 27): Igualmente que BPB_SecPerTrk sólo tiene importancia para volúmenes muy específicos como disqueteras, cintas, etc.

BPB_HiddSec (Bytes 28, 29, 30 y 31): Número de sectores ocultos que preceden a la partición que contiene este volumen FAT. Sin relevancia en este sistema.

BPB_TotSec32 (Bytes 32, 33, 34 y 35): Indica, siempre que BPB_TotSec16 sea cero, el número total de sectores que se encuentran en el volumen. Hay que tener en cuenta que una tarjeta de 128 Mb no tiene 262.144, como estrictamente le correspondería, sino que se ha comprobado que tienen 246016 escrito en este campo. Los fabricantes indican capacidades ligeramente superiores a las reales, ya que venden cada Mb como si fuesen 1000 Kb y no 1024; etiquetando así con 128 Mb a las tarjetas de 128 millones de bytes.

Los campos que se encuentran a continuación son específicos de sistemas de archivos FAT32, existiendo otros campos para sistemas FAT12 y FAT16.

BPB_FATSz32 (Bytes 36, 37, 38 y 39): Número de sectores ocupados por una FAT.

BPB_ExtFlags (Bytes 40 y 41): Flags del sistema de archivos, no se utilizan y se dejan a cero.

BPB_FSVer (Bytes 42 y 43): La versión del sistema de archivos, utilizado para definir los campos del sector FSInfo, nosotros utilizamos la versión 0.0, al igual que Windows 2000, en la que dicho sector no tiene casi importancia.

BPB_RootClus (Bytes 44, 45, 46 y 47): Indica el número del primer cluster del directorio raíz. El resto de sectores que vaya ocupando este directorio se irán direccionando en la FAT como si de un archivo se tratara.

BPB_FSInfo (Bytes 48 y 49): Indica el número del sector de la estructura FSInfo, que normalmente suele ser uno.

BPB_BkBootSec (Bytes 50 y 51): Contiene el número del sector donde se encuentra la copia de seguridad del sector de arranque.

BPB_Reserved (Bytes del 52 al 63 inclusive): Estos bytes están reservados para futuras revisiones y expansiones del sistema de archivos FAT32.

BS_DrvNum (Byte 64): Número de la unidad. Usado para sistemas MS-DOS. Sin relevancia en nuestro sistema.

BS_Reserved1 (Byte 65): Usado por Windows NT, para formatear un volumen se escribe este campo a cero.

BS_BootSig (Byte 66): Firma digital que indica la existencia de los siguientes campos. Si no existe o no es la correcta, el volumen no será reconocido por los sistemas operativos de Microsoft. Su valor debe ser 0×29.

BS_VolID (Bytes 67, 68, 69 y 70): Número del volumen, generado generalmente por el reloj del sistema y que puede ser utilizado con fines de seguridad.

BS_VolLab (Bytes del 71 al 81): Es una cadena de caracteres que indica la etiqueta del volumen, inicialmente los sistemas operativos Microsoft escriben aquí “NO NAME”.

BS_FilSysType (Bytes 82 a 89 inclusive): Es otra cadena de caracteres con el nombre del sistema de archivos. En este caso “FAT32”.

Existe además otra firma digital en los bytes 510 y 511 cuyo valor correcto es 0x55AA.


El sector FSInfo

El sector FSInfo, cuya posición está indicada en el correspondiente campo del sector de arranque, contiene una información que podría ser útil de ser correcta ya que contiene dos campos: uno indicando cuál es el siguiente cluster libre y otro indicando el número total de clusters libres en la unidad. Sin embargo, estos datos no tienen porqué ser ciertos, ya que por ejemplo, Microsoft Windows 2000TM no los actualiza, y por ello, este equipo tampoco, pues su uso podría ocasionar errores.  Sin embargo, sí que se recomienda su uso en caché, pues dará como resultado un producto más rápido.

 

De todos modos, el sector incluye una serie de firmas digitales cuyo valor sí se comprueba y, si no son correctas, el volumen no es inicializado.

 

A continuación se describen los campos de este sector:

FSI_LeadSig (Bytes 0, 1, 2 y 3): su valor debe ser 0×41615252

FSI_Reserved1 (Bytes del 4 al 479 ambos inclusive): reservado para futuras expansiones del sistema de archivos FAT32.

FSI_StrucSig (Bytes 484, 485, 486, 487): su valor debe ser 0×61417272.

FSI_FreeCount (Bytes 488, 489, 490 y 491): Teóricamente indica el número de clusters libres en la unidad, pero en la versión de 0.0 de FSInfo, estos datos no tienen porqué ser correctos. Se recomienda su uso en la caché.

FSI_NxtFree (Bytes 492, 493, 494 y 495): Indica el número del siguiente cluster libre. Al igual que ocurre con el campo anterior, su valor no tiene porqué ser correcto. Se recomienda su uso en la caché.

FSI_Reserved2 (Bytes del 496 al 511 ambos inclusive): Reservados para futuras expansiones del sistema de archivos FAT32.


EMPECEMOS A DIVERTIRNOS: UN POCO DE MATEMÁTICAS

 

 Una vez escritos correctamente los sectores Boot y FSInfo, un volumen FAT32 será reconocido correctamente por un sistema operativo Microsoft. La siguiente estructura de datos importante es la propia tabla FAT.

 

La FAT se define como una lista enlazada que apunta a las direcciones de los clusters que tienen el contenido de un archivo. El primer cluster de datos es el cluster dos, normalmente asignado al directorio raíz.

 

El primer sector de la región de datos, que es el primer sector del cluster dos, se calcula del siguiente modo:

 

if (BPB_FATSz16 != 0) {

FATSz = BPB_FATSz16;

}

else {

FATSz = BPB_FATSz32;

}

FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz);

 

Dado un número de cluster N, el número del primer sector de este cluster se computa del siguiente modo:

 

FirstSectorOfCluster = ((N – 2) * BPB_SecPerClus) + FirstDataSector;

 

De modo que el primer sector de un cluster podría calcularse con la siguiente función:

/*! Función que calcula el primer sector de un cluster.

* @param[in] cluster Cluster del que se quiere conocer su primer sector.

* @return Primer sector del cluster

*/

unsigned long PrimerSectorDelCluster( unsigned long cluster ) {

return (cluster – 2) * FAT.SecPerClus + FAT.RootDirSector;

}

Cómo se escribe un fichero en un sistema de archivos FAT 32

Un archivo en FAT32 tiene, junto con sus atributos y nombre, el número del primer cluster de dicho archivo. Tras leer dicho cluster, en adelante ultimoClusterProcesado; se deberá averiguar cuál es el siguiente cluster libre en disco, en adelante siguienteCluster. Para calcular siguienteCluster hay que tener en cuenta que ultimoClusterProcesado, aunque no esté marcado como ocupado lo está, debiendo por ello ignorarlo en este cómputo. Una vez concluido ultimoClusterProcesado y calculado siguienteCluster, se escribe en la posición de FAT correspondiente a ultimoClusterProcesado el número que apunte a siguienteCluster y así sucesivamente.

Cuando una cadena de clusters acaba, se marca fin de cadena de clusters haciendo siguienteCluster = 0xFFFFFFF8, indicando que no hay siguiente cluster, si la cadena de clusters se refiriese a un directorio la marca es 0x0FFFFFFF.

 

Estas y otras marcas de interés en la FAT se listan a continuación:

#define FIN_DE_FICHERO 0xFFFFFFF8 //EOF
#define FIN_DE_DIRECTORIO 0x0FFFFFFF
#define CLUSTER_LIBRE 0×00000000
#define CLUSTER_LIBRE 0×10000000
#define CLUSTER_LIBRE 0xF0000000
#define CLUSTER_DEFECTUOSO 0x0FFFFFF7

 

Determinación del tipo de FAT

El tipo de FAT se determina solamente por el número de clusters en el volumen. Para ello se calculan los sectores del directorio raíz:

 

sectoresDelDirectorioRaiz = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec;

 

Determinando después el número de sectores en la región de datos del volumen:

 

if ( BPB_TotSec16 != 0 ) {

numeroDeSectores = BPB_TotSec16;

}

else {

numeroDeSectores = BPB_TotSec32;

}

sectoresDeDatos = numeroDeSectores – (BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors);

 

Lo que permite calcular el número de clusters.

 

numeroDeClusters = sectoresDeDatos / BPB_SecPerClus;

 

 

Cabe señalar que existe una diferencia entre numeroDeClusters y el número máximo de cluster válido porque el primer cluster de datos válidos es el 2.

La forma de determinar qué tipo de FAT corresponde a un volumen, se indica en el documento de Microsoft FAT General Overview of On-Disk Format[1] en función del número de clusters de datos, CountOfClusters, de la siguiente manera:

 

if (CountOfClusters < 4085) {

/* El volumen es FAT12 */

} else if (CountOfClusters < 65525) {

/* El volumen es FAT16 */

} else {

/* El volumen es FAT32 */

}

 

Otro cálculo importante relacionado con la estructura FAT es, dado un número de cluster válido ultimoClusterProcesado, cómo encontrar la posición que le corresponde en la tabla FAT para escribir por dónde continua el fichero. A continuación se muestra una forma de codificar la solución a este problema:

 

SectorFAT = FAT.InicioFAT;

if (ultimoClusterProcesado > 127) {

do{

ultimoClusterProcesado -= 128;

SectorFAT++;

}while (ultimoClusterProcesado > 127);

}

ultimoClusterProcesado <<= 2;

 

ultimoClusterProcesado se multiplica finalmente por cuatro porque en FAT32, una entrada de FAT son cuatro bytes. Por ello, por ejemplo al ultimoClusterProcesado dos, le corresponda la posición ocho del primer sector de FAT.

Los 4 bits más significativos de una marca FAT en FAT32 están reservados y pueden ignorarse.

Otra nota a tener en cuenta en la estructura FAT, es el último sector de la misma, ya que el último cluster direccionable no será la última entrada del último sector de FAT, sino la correspondiente a numeroDeClusters + 1; que corresponde al número máximo de cluster válido. Las posiciones más allá de esta entrada en la FAT serán tomadas como cero.

 

Formateando un disco

 

Queda una cuestión de importancia. Si el tipo de FAT depende del número de clusters de datos y el número de sectores disponible en el área de datos de un volumen FAT depende del tamaño de la tabla FAT; teniendo un volumen sin formatear, habrá que conocer el modo de averiguar cómo calcular los valores adecuados para escribir en los campos correspondientes BPB_SecPerClus y BPB_FATSz16 ó BPB_FATSz32. Los sistemas operativos de Microsoft realizan estas tareas con valores fijos y tablas.

Dichos sistemas operativos sólo formatean con FAT12 los volúmenes que corresponden a disquetes, y puesto que hay pocos valores de capacidad realizan esta tarea mediante tablas.

Los sistemas Windows utilizan una regla para dar formato FAT16 o FAT32. Simplemente, si un volumen es menor de 512 Mb, es FAT16, caso contrario, es FAT32. Aunque de todos modos, puede forzarse el que se dé formato a una unidad con un sistema de archivos u otro. Las tablas correspondientes a FAT16 y FAT32 de los sistemas operativos Microsoft para obtener el valor de BPB_SecPerClus se indican a continuación. En ellas, el tamaño en sectores es el tamaño máximo de disco, dado en sectores, para que el volumen tenga el correspondiente valor de SecPerClus.

FAT16

Tamaño máximo de la unidad Tamaño máximo en sectores BPB_SecPerClus Comentarios
4.1 Mb 8400 0 Valor erróneo
16 Mb 32680 2
128 Mb 262144 4
256 Mb 524288 8
512 Mb 1048576 16
1 Gb 2097152 32 Formato FAT16 forzado
2 Gb 4194304 64 Formato FAT16 forzado
> 2 Gb 0xFFFFFFFF 0 Valor erróneo

 

FAT32

Tamaño máximo de la unidad Tamaño máximo en sectores BPB_SecPerClus Comentarios
32.5 Mb 66600 0 Valor erróneo
260 Mb 532480 1
8 Gb 16777216 8
16 Gb 33554432 16
32 Gb 67108864 32
> 32 Gb 0xFFFFFFFF 64 Discos mayores de 32 Gb tienen clusters de 32 Kb

 

Para calcular el tamaño de la FAT, conocido el tamaño del disco, y el correspondiente tamaño de un cluster, Microsoft suministra un código que no explica y que calcula el tamaño de la FAT conocidos ambos datos, y es el que sigue:

 

TmpVal1 = DskSize – (BPB_ResvdSecCnt + RootDirSectors);

TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;

if (FATType == FAT32) {

TmpVal2 = TmpVal2 / 2;

}

FATSz = (TmpVal1 + (TmpVal2 – 1)) / TmpVal2;

if (FATType == FAT32) {

BPB_FATSz16 = 0;

BPB_FATSz32 = FATSz;

} else

{

BPB_FATSz16 = FATSz;

}

 

Creando directorios y ficheros

 

 

En este artículo, por motivos de sencillez, no se utilizan entradas de directorio largas, esto es, nombres de archivos mayores de 8 caracteres. Limitándose a las entradas cortas, sin embargo, cabe decir, que para que una entrada corta sea reconocida como tal, habrá de ser una entrada de tipo MS-DOS. Y, por tanto, todos sus caracteres deberán estar en mayúsculas, de lo contrario obtendremos errores, y el archivo no será reconocido correctamente.

La tabla FAT no es más que un listado de entradas, cada una de las cuales es una estructura de 32 bytes. Así, si se exploran los sectores de la FAT no se tendrán más que una lista lineal de estas entradas, dentro de cada una de las entradas, uno de los campos, es el primero de los clusters que contiene el fichero (o directorio) cuyo nombre también está en la entrada. Un directorio tiene la misma estructura que un fichero. El único directorio especial es el directorio raíz.

En FAT32, el directorio raíz, es una cadena de clusters de tamaño variable, como cualquier otro directorio. El primer cluster del directorio raíz viene indicado por BPB_RootClus. A diferencia del resto de directorios, el directorio raíz no tiene etiquetas de fecha y hora, no tiene nombre y sus dos primeras entradas no son “.” y “..”, denominadas dot y dotdot respectivamente. Además, es el único directorio en que sería válido que un archivo tuviese el atributo ATTR_VOL_ID.

A continuación se muestra la estructura de datos de una entrada corta (32 bytes) en una tabla FAT32

Nombre del campo

Offset

(en bytes)

Tamaño

(en bytes)

Descripción

DIR_Name

0 11 Nombre corto, en mayúsculas, del archivo o del directorio.

DIR_Attr

11 1 Atributos del archivo.

DIR_NTRes

12 1 Utilizado por Windows NT. Se recomienda poner este byte a cero al crear un fichero nuevo e ignorarlo después.

DIR_CrtTime_Tenth

13 1 Etiqueta de los milisegundos cuando se creó el fichero. Campo opcional.

DIR_CrtTime

14 2 Hora a la que se creó el fichero. Campo opcional.

DIR_CrtDate

16 2 Fecha en la que se creó el fichero. Campo opcional.

DIR_LstAccDate

18 2 Fecha en que se accedió al fichero por última vez. Campo opcional.
DIR_FstClusHI 20 2 Los 2 bytes más significativos de la palabra que indica cuál es el primer cluster del contenido de ese fichero o directorio.
DIR_WrtTime 22 2 Hora de la última escritura. La creación se considera una escritura. Este campo es obligatorio.
DIR_WrtDate 24 2 Fecha de la última escritura. La creación se considera como una escritura. Este campo es obligatorio.
DIR_FstClusLO 26 2 Los dos bytes menos significa-tivos de la palabra que indica cuál es el primer cluster del contenido de ese fichero o directorio.
DIR_FileSize 28 4 Tamaño del Fichero en bytes. Ha de ser coherente con la cadena de clusters o dará error.

En lo relativo al byte de atributos, es un valor bit-masked, y el significado de cada uno de los bits que componen la máscara se indica a continuación:

Atributo Valor de DIR_Attr Descripción
ATTR_READ_ONLY 0×01 Indica que no se puede escribir en el archivo.Un intento de escritura debe generar error.
ATTR_HIDDEN 0×02 Un listado normal del directorio no debe mostrar este archivo.
ATTR_SYSTEM 0×04 Indica que el archivo pertenece a un sistema operativo.
ATTR_VOLUME_ID 0×08 Sólo puede haber un archivo con este atributo y debe de estar en el directorio raíz. El nombre del archivo que tenga este atributo, si lo hay, será la etiqueta del volumen. En este archivo, DIR_FstClusLO y DIR_FstClusHI deberán ser cero.
ATTR_DIRECTORY 0×10 Indica que el archivo es un directorio.
ATTR_ARCHIVE 0×20 Las utilidades backup usan este bit para indicar qué ficheros han sido creados, modificados o cambiados de nombre desde que se hizo la última copia de seguridad.
ATTR_LONG_NAME 0x0F Indica que es un archivo con nombre mayor de 11 bytes.

 

Consideraciones sobre DIR_Name[0]:

-      Si DIR_Name[0] es 0xE5 la entrada del directorio está vacía.

-      Si DIR_Name[0] es 0×00 la entrada del directorio está vacía y el resto de entradas del directorio que haya a continuación están también vacías. Por ello, cuando se borre un archivo, se borrará su nombre con 0xE5 y no con 0×00.

-      Si DIR_Name[0] es 0×05, se procesará como si fuese 0xE5, ya que 0xE5 corresponde a un carácter especial perteneciente a los caracteres kanji, usados en algunos idiomas como japonés y chino, en cuyas FAT ya se indica esta característica.

El nombre de un archivo quedará dividido, para el sistema operativo, en 2 partes, llamadas nombre y extensión, de modo que los 8 primeros bytes de DIR_Name corresponderán a su nombre y los 3 últimos corresponderán a su extensión. Los bytes sobrantes del nombre deberán completarse con el carácter de espaciado 0×20, salvo que queramos indicar el espacio especial 0xFF.

Cuando se crea un directorio, se marca ATTR_DIRECTORY y su tamaño se fija a cero. Se indica en los correspondientes campos en qué cluster comienza el directorio y en FAT, se recorre la correspondiente cadena de los clusters que pertenecen a ese directorio terminando con la marca 0x0FFFFFFF, se necesitan crear 2 entradas especiales en ese directorio, llamadas dot (.) y dotdot (..), en primer y segundo lugar de dicho directorio respectivamente.

En el campo correspondiente al tamaño de estas entradas se indicará cero, y en el resto de campos se indicarán los mismos valores que los del directorio creado. El primer cluster de dot será el mismo que el del directorio creado, y el primer cluster de dotdot será el directorio en el que está contenido este nuevo directorio, si es cero, apuntará al directorio raíz.

La siguiente figura, muestra cómo se grabaría un cluster de un fichero:

Grabación de datos en tarjetas SD, FAT 32

Figura 1. Diagrama de bloques con el proceso de grabación de datos en un fichero

Este procedimiento se llamaría cada vez que hubiese datos en RAM que deseasen ser grabados en un fichero que ya habría sido dado de alta en la estructura FAT, el prototipo del fichero podría ser, por ejemplo el siguiente:

/*! Función que graba datos en un fichero. Si el fichero no existe se creará.

* @param[in] *nombreDelFichero apunta a un string que contiene el nombre del fichero. No es necesario indicar la longitud, <br>

*  ya que en el estándar se definen los caracteres que delimitan el final de esta cadena.

* @param[in] *datos puntero al inicio del array de datos que desea grabarse en el fichero.

* @param[in] numeroDeBytes Número de bytes que desean grabarse en el fichero.

* @param[in] EOF sirve para indicar si habrá que cerrar el fichero o mantenerlo abierto

* @return un valor previamente definido con el estado de la ejecución, que podría ser ok, error, fichero lleno, fichero nuevo, etc.

*/

unsigned char GrabarDatosEnFichero ( unsigned char * nombreDelFichero, unsigned char *datos, unsigned long numeroDeBytes, unsigned char EOF ) ;

Fecha y Hora

Sólo son obligatorios los campos DIR_WrtTime y DIR_WrtDate.

 

La fecha son 16 bits que indican la fecha relativa al 1 de enero de 1980, estructurados de la siguiente forma:

Bits 0-4: Día del mes, su rango válido es desde el 0 hasta el 31.

Bits 5-8: Mes del año, su rango válido es desde el 1 hasta el 12.

Bits 9-15: Año desde 1980, su rango válido es desde el 0 hasta el 127.

 

La hora son 16 bits que la indican con una granularidad de 2 segundos de la siguiente forma:

Bits 0-4: Segundos, contados de 2 en 2, su rango válido es de 0 a 29.

Bits 5-10: Minutos, su rango válido es de 0 a 59.

Bits 11-15: Horas, su rango válido es de 0 a 23.

 

Diferencias entre FAT16 y FAT32

La diferencia principal a la hora de elaborar código, es el hecho que una entrada en FAT, que en FAT32 son cuatro bytes, en FAT16 son dos. Por lo tanto, donde antes se multiplicaba por cuatro, habrá de hacerse por dos. Esto también implica que mientras en un sector de FAT32 caben 128 punteros a clusters, en FAT16 pueden alojarse 256.

También en el sector cero se encuentran diferencias, pues su contenido es en parte distinto ya que, por ejemplo, en FAT16 no existe el sector FSInfo. Además, a partir de la posición 36 (campo BPB_FATSz32 en FAT32), el contenido de los campos es distinto en FAT16 y en FAT12. Sus campos se enumeran a continuación:

BS_DrvNum (Byte 36): Importante sólo para unidades con interrupciones 0×13

BS_Reserved1 (Byte 37): Reservado, utilizado por Microsoft Windows NTTM.

BS_BootSig (Byte 38): Indica que los siguientes tres campos existen, su valor debe ser 0×29.

BS_VolID (Byte 39): Número de serie del volumen, en ordenador, generado por el reloj y utilizado para aplicaciones de seguridad, sin relevancia alguna para esta aplicación.

BS_VolLab (Byte 43): String de 11 bytes que Microsoft WindowsTM escribe con “NO NAME”

BS_FilSysType: String de ocho caracteres que indica el nombre del sistema de archivos: “FAT12” ó “FAT32”, también puede ser simplemente “FAT”.

 

Se puede observar que estos campos existen en FAT32, pero con una posición diferente.

Otra diferencia importante es que en FAT16, el directorio raíz no es una estructura de fichero, sino que es un número reservado de sectores que se indica en los campos correspondientes del sector cero.

 

UN EJEMPLO DE FUNCIONES QUE PODRÍA INCORPORAR LA LIBRERÍA

 /*! Función que devuelve el primer cluster que se encuentra libre en la FAT

* @return el primer cluster libre encontrado

*/

unsigned long SiguienteClusterLibre ( void) ;

/*! Escribe en la posición del último cluster procesado el número que indica el siguiente cluster en el cual continúa el contenido

* del fichero.

* @param[in] ultimoClusterProcesado posición en la FAT en la cual habrá que escribir el número del siguiente cluster del fichero.

* @param[in] siguienteCluster cluster en el cual continúa el contenido del fichero

* @return código de error con el estado de ejecución de la función.

*/

unsigned char EscribirFAT ( ultimoClusterProcesado, siguienteCluster ) ;

/*! Busca un fichero a partir de su nombre y devuelve el primer cluster que contiene datos de dicho fichero.

* @param[in] nombreDelFichero puntero al inicio del string que contiene el nombre del fichero, en formato short name.

* @return el cluster en el cual inicia el fichero su contenido o un código de error de fichero no encontrado, inválido, etc

*/

unsigned long BuscarFichero ( unsigned char *nombreDelFichero );

/*! Lista el contenido del directorio recibido como parámetros, o del directorio raíz si recibe NULL o ‘.’

* @param[in] tramaDeParametros puntero a un string, finalizado por enter, con los parámetros similares al DIR de MS-DOS 5.0

* @return un puntero a una cadena con el listado de ficheros del directorio, su espacio en disco, etc.

*/

unsigned char *dir ( unsigned char *tramaDeParametros );

/*! Crea un directorio

* @param[in] nombreDelDirectorio Nombre del directorio que desea crearse

* @return un código con el estado de ejecución del comando, por ejemplo, OK, disco lleno, etc

*/

unsigned char MD ( unsigned char *nombreDelDirectorio );

/*! Borra un directorio

* @param[in] nombreDelDirectorio Nombre del directorio que desea borrarse

* @return un código con el estado de ejecución del comando, por ejemplo, OK, el directorio no existe, etc

*/

unsigned char RD ( unsigned char *nombreDelDirectorio );

/*! Borra un fichero

* @param[in] nombreDelFichero Nombre del fichero que desea borrarse

* @return un código con el estado de ejecución del comando, por ejemplo, OK, disco lleno, etc

*/

unsigned char Delete ( unsigned char *nombreDelFichero );

/*! Formatea la tarjeta: vacía la FAT, poniendo en todos los inicios de fichero o directorio 0xE5 y

* escribiendo las marcas de cluster vacío en todas las posiciones de la FAT, posteriormente, si no están

* los datos de los sectores de arranque los escribe, y si están, en ellos pone el nombre de volumen recibido

* como parámetro

* @param[in] nombreDelVolumen puntero a un string con el nombre del volumen que se escribirá al

* formatear si se recibe NULL el nombre será ARTE8BIT

* @return un código con el estado de ejecución del comando.

*/

unsigned char Format ( unsigned char *nombreDelVolumen );

/*! Crea un fichero para escritura, si el fichero existe se puede elegir entre sobreescribir el contenido o añadir

* contenido al final del fichero

* @param[in] nombreDelFichero Nombre del fichero que desea crearse

* @param[in] datos puntero a la trama de datos que desea escribirse

* @param[in] numeroDeDatos número de datos que contiene la trama de datos a escribir

* unsigned char sobreescribir byte con las opciones SOBREESCRIBIR o CONTINUAR

* @return un código con el estado de ejecución del comando, por ejemplo, OK, disco lleno, etc

*/

unsigned char CopyCon ( unsigned char *nombreDelFichero, unsigned char *datos, unsigned int numeroDeDatos, unsigned char sobreescribir );

/*! Lee el contenido de un fichero y lo devuelve mediante el puntero datos, pasado por referencia.

* @param[in] nombreDelFichero Puntero al inicio del string que contiene el nombre del fichero

* cuyo contenido desea leerse.

* @param[out] datos Puntero pasado por referencia donde se almacenarán los datos leidos del fichero

* @param[in] numeroDeDatos Número de datos que se leerán del fichero.

* @return código de error con el estado de ejecución de la función, como, ok, fichero no encontrado, etc

*/

unsigned char type ( unsigned char *nombreDelFichero, unsigned char *datos, unsigned int numeroDeDatos);

 

¿TE GUSTÓ EL ARTÍCULO?

 

¿Te parece interesante que exista un blog de referencia en español sobre electrónica y sistemas embebidos? Ayuda a que Arte en 8 bits crezca, para ello sólo tienes que pulsar en los botones para compartirlo en redes sociales, Twitter o Google+. De este modo contribuirás a que Arte en 8 bits se posicione mejor, y por tanto podremos crecer e ir ofreciéndote cada vez un mejor contenido y de mayor calidad.

De antemano, muchas gracias.

 

BIBLIOGRAFIA

[1]FAT: General Overview of On-Disk Format. Microsoft.

2 Comentarios a "Sistemas de archivos FAT16 / FAT32 para las tarjetas SD / MMC"

  1. Federico's Gravatar Federico
    20 julio, 2013 - 11:29 | Enlace permanente

    En
    sectoresDelDirectorioRaiz = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec;
    debería aclararse que es división entera
    En
    sectoresDeDatos = numeroDeSectores – (BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;
    está faltando un paréntesis. Pero además, ¿por qué restar el número de sectores del directorio raíz si ya es parte de los datos? Además, si no se le resta, al multiplicar por 512 se obtiene lo que declara Windows en las propiedades de la unidad.

Deja una respuesta

4 × four =