= GIET_VM / FAT32 File System = [[PageOutline]] The [source:soft/giet_vm/giet_fat32/fat32.c fat32.c] and [source:soft/giet_vm/giet_fat32/fat32.h fat32.h] files define the GIET_VM File System. == 1) General Principles == This implementation supports only block devices with block_size = 512 bytes. The max size for a single file is 4 Gbytes -1. From the application software point of view, a cluster is the smallest storage allocation unit on the block device : any file (or directory) occupies at least one cluster, and a given cluster cannot be shared by 2 different files. This implementation supports only cluster size = 4 Kbytes (i.e. 8 contiguous blocks on block device). The FAT region on the block device is an array of 32 bits words defining the linked list of clusters allocated to a given file in the DATA region of the block device. The DATA region is actually an array of 4 Kbytes buffers (i.e. an array of clusters). Each slot in the FAT array contains a cluster index, that is the index of the next allocated cluster for a given file. The cluster index in the FAT array is also the cluster index in the DATA region array. The cluster index value cannot be larger than 0x0FFFFFFF (i.e. 256 M). The max addressable storage capacity in the DATA region on the block device is therefore (256 M * 4 Kbytes) = 1 Tbytes. We use the variable ''cluster'' to name the cluster index. This implementation defines four data structures: * The '''File-Cache''' and '''Fat-Cache''' are dynamically allocated memory caches, implemented in the distributed kernel heap. There is actually one independent cache per file (called File_Cache), and one cache for the FAT itself (called Fat_Cache). The cache size is not fixed: it is dynamically increased from the kernel heap as required by the read / write access to the files or to the FAT itself. The memory allocated to a given File-Cache is only released when the file is closed by all threads that have a reference on it. * The '''Inode-Tree''' is an internal representation of the FAT. It is a sub-tree of the File System tree. Each node define a file or a directory, and contains a pointer on the associated File-Cache. This Inode-Tree is dynamically increased (from the distributed kernel heap) when a new file or a directory is accessed. The memory allocated to the Inode-Tree is only released in case of system crash. * The '''File-Descriptor-Array''' is a statically defined array of file descriptors. A private file descriptor is allocated to each thread requiring to open the file, and contains mainly the current file pointer (called ''offset''). The max number of file descriptors is defined by the GIET_OPEN_FILES_MAX global variable (in the ''get_config.h'' file). * The global '''Fat-Descriptor''' contains general information such as the FAT region lba and size, the DATA region lba and size, pointers on the Fat-Cache and Inode-Tree, the File-Descriptor-Array, and the locks protecting the FAT shared structures. It contains also a single block buffer (512 bytes) used in the initialization phase, and for FS_INFO sector update. To support various block device peripheral, this FAT32 implementation defines a generic function to transparently access various physical block devices, using the driver specified in the ''hard_config.h'' file. Five drivers are presently supported ( IOC_BDV / IOC_HBA / IOC_SDC / IOC_SPI / IOC_RDK ). WARNING 1: A node name (file or directory) cannot be larger than 31 characters. WARNING 2: There is no rescue mechanism (at the moment) in case of heap overflow: The system crash with a nice error message on the kernel terminal if the heap defined in the mapping is too small... == 2) Cache Structure & Write Policy == The Fat_Cache and the File_Cache have the same organisation. Each cache contains an integer number of clusters, as the cluster is the smallest unit of data that can be loaded from the block device to a Cache-File. To reduce the access time, this set of clusters is organized as a 64-Tree: each node has one single parent and (up to) 64 children. The leaf nodes are the cluster descriptors. WARNING: To access a given cluster in a given file, we use the ''cluster_id'' variable, that is the index of cluster inside the file. This ''cluster_id'' variable is different from the ''cluster'' variable, that is used to index both the FAT and the DATA region on block device. The cluster_id variable must be split in pieces of 6 bits, that are used to access the proper children at a given level in the 64-Tree. The depth (number of levels) of the 64-Tree depends on the file size : || File Size || levels || || up to 256 Kbytes || 1 || || from 256 Kbytes to 16 Mbytes || 2 || || from 16 Mbytes to 1 Gbytes || 3 || || larger than 1 Gbytes || 4 || For the '''File-Caches''', the GIET_VM implements a '''Write-Back''' policy. In case of write, the data are always modified in the cache. In case of miss, new clusters are allocated to the target file, the cache is updated from the block device if required, and the data are modified in the cache, but not on the block device. The modified clusters are written on the block device only when the last reference is closed, using the dirty flag implemented in each cluster descriptor. For the '''Fat_Cache''', the GIET_VM implements a '''Write-Through''' policy. When the FAT content is modified (i.e. when new clusters are allocated to an existing file, or when a new file (or directory) is created, the modifications are written in the fat_cache (that must be updated in case of miss), and are immediately reported to the block device, for each modified cluster. == 4) Extern Functions == The following functions implement the FAT related system calls. === __int '''_fat_init'''( unsigned int kernel_mode )__ === This function initializes the FAT structures. It is called twice, by the boot-loader, and by the kernel_init. * in '''boot mode''' (kernel_mode == 0), it initializes only the statically defined Fat-Descriptor, using informations found in the boot sector and FS-INFO sector, that are loaded in the FAT descriptor 512 bytes buffer. In this mode, it is used by the boot code to load the ''kernel.elf'' file, and the various ''application.elf'' files, into memory by accessing directly to the block device. * in '''kernel mode''' (kernel_mode != 0), it uses the distributed kernel heap to initialize the dynamically allocated structures such as the Inode-Tree, the Fat-Cache, and the File-Cache for the root directory. Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_IO_ERROR, * GIET_FAT32_INVALID_BOOT_SECTOR === __int '''_fat_open'''( char* pathname , unsigned int flags )__ === This function implements the giet_fat_open() system call. The semantic is similar to the UNIX open() function, but only the O_CREATE, O_RDONLY and O_TRUNCATE flags are supported. The UNIX access rights are not supported. If the file does not exist in the specified directory, it is created, and the Inode-Tree, the Fat-Cache and the FAT region on device are updated. If the specified directory does not exist, an error is returned. It allocates a file descriptor to the calling task, for the file identified by "pathname". If several tasks try to open the same file, each task obtains a private file descriptor and the reference count is updated. A node name (file or directory) cannot be larger than 31 characters. * '''pathname''' : defines both the specified directory and the file name. * '''flags''' : O_CREATE, O_RDONLY, O_TRUNCATE (can be OR'ed). Returns a file descriptor index on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_FILE_NOT_FOUND, * GIET_FAT32_NAME_TOO_LONG, * GIET_FAT32_IO_ERROR, * GIET_FAT32_TOO_MANY_OPEN_FILES === __int '''_fat_close'''( unsigned int fd_id )__ === This function implements the "giet_fat_close()" system call. The semantic is similar to the UNIX "close()" function. It decrements the inode reference count, and releases the fd_id entry in the file descriptors array. If the reference count is zero, it writes all dirty clusters on block device, and releases the memory allocated to the file_cache: The cache 64-Tree infrastructure (depending on file size) is kept, but all buffers and all buffer descriptors are released. * '''fd_id''' : file descriptor index Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_IO_ERROR === __int '''_fat_file_info'''( unsigned int fd_id , fat_file_info_t* info )__ === This function implements the giet_fat_file_info() system call. It returns the size, offset value and is_dir info for a file identified by the "fd_id" argument. * '''fd_id''' : file descriptor index * '''info''' : pointer to the fat_file_info_s struct storing the values (return buffer) Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN === __int '''_fat_read'''( unsigned int fd_id , paddr_t buffer , unsigned int count, unsigned int phys )__ === This function implements the "giet_fat_read()" system call. It accesses the File_Cache associated to the file identified by the file descriptor, and transfers "count" bytes from the cache to the user buffer, starting from the current file offset. In case of miss in the File_Cache, it loads all involved clusters into the cache. * '''fd_id''' : file descriptor index * '''buffer''' : pointer to the memory buffer * '''count''' : number of bytes * '''phys''' : use _physical_memcpy instead of memcpy Returns the number of bytes actually transferred. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_IO_ERROR === __int '''_fat_write'''( unsigned int fd_id , void* buffer , unsigned int count )__ === This function implements the "giet_fat_write()" system call. It accesses the File-Cache associated to the file identified by the file descriptor, and transfers count bytes from the user buffer, to the cache, starting from the current file offset. It loads all involved clusters into cache if required. If (offset + count) is larger than the current file size, the file size will be increased. It allocates new clusters if required, and updates the Fat-Cache and the FAT region on block device. As it implements a Write-Back policy, the DATA region on block device is not updated, but the modified clusters are marked dirty. * '''fd_id''' : file descriptor index * '''buffer''' : pointer on the memory buffer * '''count''' : number of bytes Returns number of bytes actually written on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_READ_ONLY, * GIET_FAT32_NO_FREE_SPACE, * GIET_FAT32_IO_ERROR === __int '''_fat_lseek'''( unsigned int fd_id , unsigned int offset , unsigned int whence )__ === This function implements the "giet_fat_lseek()" system call. It repositions the offset in the file defined by the file descriptor, according to the offset and whence arguments. The accepted values for the whence argument are SEEK_SET / SEEK_CUR / SEEK_END : * SEEK_SET => new_offset = offset * SEEK_CUR => new_offset = current_offset + offset * SEEK_END => new_offset = file_size + offset Arguments: * '''fd_id''' : file descriptor index * '''offset''' : offset value (can be negative) * '''whence''' : SEEK_SET / SEEK_CUR / SEEK_END Returns new seek value (in bytes) on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_INVALID_ARG === __int '''_fat_mkdir'''( char* pathname )__ === This function implements the giet_fat_mkdir() system call, that has the same semantic as the UNIX ''mkdir()'' function. It creates a new directory in the File System as specified by the pathname argument. The FAT region is updated. The Inode-Tree is updated. * '''pathname''' : complete pathname Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_FILE_NOT_FOUND, * GIET_FAT32_NAME_TOO_LONG, * GIET_FAT32_FILE_EXISTS, * GIET_FAT32_NO_FREE_SPACE, * GIET_FAT32_IO_ERROR === __int '''_fat_remove'''( char* pathname , unsigned int should_be_dir )__ === This function implements the giet_fat_unlink() system call, that has the same semantic as the UNIX ''unlink()'' function. It removes the file identified by the pathname argument from the File System. An error is returned if the number of references (number of open file descriptors) is not zero. All clusters allocated to this file in the DATA region are released. The FAT region is updated on the block device and the Fat-Cache is updated. The memory allocated to the File_Cache is released. The Inode-Tree is updated. * '''pathname''' : directory complete pathname Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_FILE_NOT_FOUND, * GIET_FAT32_NAME_TOO_LONG, * GIET_FAT32_IS_DIRECTORY, * GIET_FAT32_NOT_A_DIRECTORY, * GIET_FAT32_IS_OPEN, * GIET_FAT32_IO_ERROR, * GIET_FAT32_DIRECTORY_NOT_EMPTY === __int '''_fat_rename'''( char* old_path , new_path )__ === This function implements the giet_fat_rename() system call. It moves an existing file or directory from one node (defined by "old_path" argument) to another node (defined by "new_path" argument) in the FS tree. The type (file/directory) and content are not modified. If the new_path file/dir exist, it is removed from the file system, but only if the remove condition is respected (directory empty / file not referenced). The removed entry is only removed after the new entry is actually created. * '''old_path''' : old path-name (from root). * '''new_path''' : new path-name (from root). Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_FILE_NOT_FOUND, * GIET_FAT32_MOVE_INTO_SUBDIR, * GIET_FAT32_IO_ERROR, * GIET_FAT32_DIRECTORY_NOT_EMPTY, * GIET_FAT32_IS_OPEN === __int '''_fat_opendir'''( char* pathname )__ === This function implements the giet_fat_opendir() system call. The semantic is similar to the UNIX opendir() function. If the specified directory does not exist, an error is returned. It allocates a file descriptor to the calling task, for the directory identified by "pathname". If several tasks try to open the same directory, each task obtains a private file descriptor. A node name cannot be larger than 31 characters. * '''pathname''' : directory complete pathname Returns a file descriptor for the directory index on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_NAME_TOO_LONG, * GIET_FAT32_FILE_NOT_FOUND, * GIET_FAT32_TOO_MANY_OPEN_FILES, * GIET_FAT32_NOT_A_DIRECTORY === __int '''_fat_closedir'''( unsigned int fd_id )__ === This function implements the "giet_fat_closedir()" system call. Same behavior as _fat_close(), no check for directory. * '''fd_id''' : file descriptor index Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_IO_ERROR === __int '''_fat_readdir'''( unsigned int fd_id, fat_dirent_t* entry )__ === This function implements the "giet_fat_readdir()" system call. It reads one directory entry from the file descriptor opened by "giet_fat_opendir()" and writes its info to the "entry" argument. This includes the cluster, size, is_dir and name info for each entry. * '''fd_id''' : file descriptor index * '''entry''' : pointer to write Returns GIET_FAT32_OK on success. Returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED, * GIET_FAT32_INVALID_FD, * GIET_FAT32_NOT_OPEN, * GIET_FAT32_NOT_A_DIRECTORY, * GIET_FAT32_IO_ERROR, * GIET_FAT32_NO_MORE_ENTRIES === __int '''_fat_read_no_cache'''( char* pathname , unsigned int buffer_vbase , unsigned int buffer_size )__ === This function loads a file identified by the pathname argument into the memory buffer defined by the buffer_vbase / buffer_size arguments. It is intended to be called by the boot-loader, as it uses neither the dynamically allocated FAT structures (Inode-Tree, Fat_Cache or File-Cache), nor the File-Descriptor-Array. It uses only the 4096 bytes buffer defined in the FAT descriptor. * '''pathname''' : file complete pathname * '''buffer_vbase''' : memory buffer virtual address * '''buffer_size''' : buffer size (bytes) Returns GIET_FAT32_OK on success. Returns negative value on error: * GIET_FAT32_NOT_INITIALIZED * GIET_FAT32_FILE_NOT_FOUND * GIET_FAT32_BUFFER_TOO_SMALL * GIET_FAT32_IO_ERROR === __int '''_get_file_cache_buffer'''( fat_node_t* indoor , unsigned int cluster_id , unsigned int writable , fat_cache_desc_t** desc )__ === This function returns in the argument a pointer on a buffer descriptor contained in a File_Cache. The searched buffer is idenfified by the and arguments. The argument is the buffer index in the file. The argument define the behaviour in case of miss in File-Cache: * if [all clusters (from 0 to cluster_id) are already allocated in FAT] it scan the FAT to find the cluster index on device, and load the missing cluster in the File-Cache, marked as dirty if writable. * if [not writable and all clusters (from 0 to cluster_id) not allocated] it returns an error. * if [writable and all clusters (from 0 to cluster_id) not allocated], it allocates in FAT all required clusters, it updates the size in the inode and dentry, and we allocate a buffer descriptor for the missing cluster, marked as dirty. This function is called by the _sys_fat_mmap() function, and by other FAT functions. It does not take the FAT lock, that must be taken by the caller. It returns GIET_FAT32_OK on success, and returns a negative value on error: * GIET_FAT32_NOT_INITIALIZED * GIET_FAT32_INVALID_ARG * GIET_FAT32_IO_ERROR == 5) Internal functions == The following functions can be used to implement new system calls if required. === __int '''_fat_ioc_access'''( unsigned int use_irq , unsigned int to_mem , unsigned int lba , unsigned int buf_vaddr , unsigned int count )__ === This function transfers one or several blocks between the block device and a memory buffer by calling the relevant driver. The boolean argument forces the descheduling mode if supported by the IOC driver. The boolean argument defines the transfer direction (from block device to memory if non zero). The argument is the logical block address on the block device. The argument is the memory buffer virtual address. The argument is the number of blocks to be transferred. It returns 0 on success, or -1 on failure. === __void '''_display_one_block'''( unsigned char* buf , char* string , unsigned int block_id )__ === This debug function displays the content of a 512 bytes buffer , with an identifier defined by the and arguments. === __void '''_display_fat_descriptor'''()__ === This debug function displays the FAT descriptor content. === __unsigned int '''_get_fat_cache_buffer'''( unsigned int cluster_id , fat_cache_desc_t** desc )__ === This function returns in the argument a pointer on a buffer descriptor contained in the Fat_Cache. The argument is the buffer index in the FAT_Cache. In case of miss, a 4 Kbytesbuffer and a buffer descriptor are allocated from the local heap, and the missing cluster is loaded in the Fat_Cache. It returns 0 on success. It returns 1 on error.=== __void '''_get_name_from_long'''( unsigned char* buffer , char* name )__ === This function extract a (partial) name from a LFN directory entry. === __void '''_get_name_from_short'''( unsigned char* buffer , char* name )__ === This function extract a name from a NORMAL directory entry. === __unsigned int '''_get_levels_from_size'''( unsigned int size )__ === This function returns the number of levels of a File-Cache from the size of the file. === __unsigned int '''_get_name_from_path'''( char* pathname , char* name , unsigned int* nb_read )__ === This function analyses the argument, from the character defined by the argument. It copies the found name in the buffer (without '/'), and updates the argument. It returns 0 on success. It returns 1 if one name length > NAME_MAX_SIZE characters. === __unsigned int '''_get_last_name'''( char* pathname , char* name )__ === This function scan the "pathname" argument, and copies in the buffer the last name in . It returns 0 on success.It returns 1 if one name length > NAME_MAX_SIZE characters. === __unsigned int '''_get_fat_entry'''( unsigned int cluster , unsigned int* value )__ === This function accesses the Fat-Cache and returns in the argument the content of the FAT slot identified by the argument. It loads the missing cluster from block device in the Fat-Cache in case of miss. It returns 0 on success. It returns 1 on error. === __unsigned int '''_set_fat_entry'''( unsigned int cluster , unsigned int value )__ === This function writes a new in the Fat-Cache, in the slot identified by the argument. It loads the missing cluster from block device in the Fat-Cache in case of miss. It returns 0 on success, It returns 1 on error. === __void '''_add_inode_in_tree'''( fat_inode_t* child , fat_inode_t* parent )__ === This function introduces the inode identified by the argument as a new child for the inode in the Inode-Tree. All checking are supposed to be done by the caller. Nor the File-Cache, neither the block device are modified. === __void '''_remove_inode_from_tree'''( fat_inode_t* inode )__ === This function removes one inode identified by the argument from the Inode-Tree. All checking are supposed to be done by the caller. Nor the File-Cache, neither the block device are modified. === __unsigned int '''_update_device_from_cache'''( unsigned int levels , fat_cache_node_t* root , char* string )__ === This recursive function scan one File-Cache (or the Fat-Cache) from root to leaves, to writes all dirty clusters to block device, and reset the dirty bits. The cache is identified by the an arguments. The argument is only used for debug. It returns 0 on success. It returns 1 on error. === __unsigned int '''_update_fs_info()'''__ === This function accesses directly the FS_INFO block on the block device, to update the "first_free_cluster" and "free_clusters_number" values, using only the Fat-Descriptor single block buffer. It return 0 on success. It return 1 on error. === __unsigned int '''_read_entry'''( unsigned int offset , unsigned int size , unsigned char* buffer , unsigned int little_indian )__ === This function read a data field (one to four bytes) from an unsigned char[] , taking endianness into account. The analysed field is defined by the and arguments. === __unsigned int '''_cluster_to_lba'''( unsigned int cluster )__ === This function returns the lba (logic block address in DATA region) from the index. Exit if the cluster value is smaller than 2. === __unsigned int '''_get_nb_entries'''( fat_inode_t* inode , unsigned int* nb_entries )__ === This function returns in the argument the number of entries contained in a directory identified by the pointer. It returns 0 on success. It returns 1 on error. === __unsigned int '''_get_child_from_parent'''( fat_inode_t* parent , char* name , fat_inode_t** inode )__ === This function search in the directory identified by the inode pointer a child (file or directory) identified by its . It returns in the argument the searched child inode pointer. If the searched name is not found in the Inode-Tree, the function accesses the File-cache associated to the parent directory. If the child exists on block device, the Inode-Tree is updated, and a success code is returned. If the file/dir does not exist on block device, a error code is returned. It returns 0 if inode found. It returns 1 if inode not found. It returns 2 in case of error in cache access. === __unsigned int '''_get_inode_from_path'''( char* pathname , fat_inode_t** inode )__ === For a file (or a directory) identified by the argument, this function returns in the argument the inode pointer associated to the searched file (or directory), with code (0). If the searched file (or directory) is not found, but the parent directory is found, it returns in the argument the pointer on the parent inode, with code (1). Code (2) and code (3) are error codes. Both the Inode-Tree and the involved Cache-Files are updated from the block device in case of miss on one inode during the search. Neither the Fat-Cache, nor the block device are updated. It returns 0 if searched inode found It returns 1 if searched inode not found but parent directory found. It returns 2 if searched inode not found and parent directory not found. It returns 3 if one name too long === __unsigned int '''_is_ancestor'''( fat_inode_t* a , fat_inode_t* b )__ === This function checks if inode is an ancestor of inode . It returns 0 on failure. It returns 1 otherwise. === __unsigned int '''_check_name_length'''( char* name , unsigned int* length , unsigned int* nb_lfn )__ === This function computes the length and the number of LFN entries required to store a node name, and return the values in the and arguments. * Short name (less than 13 characters) require 1 LFN entry. * Medium names (from 14 to 26 characters require 2 LFN entries. * Large names (up to 31 characters) require 3 LFN entries. It returns 0 on success. It returns 1 if length larger than 31 characters. === __unsigned int '''_update_dir_entry'''( fat_inode_t* inode )__ === For a node identified by the argument, this function updates the "size" and "cluster" values in the parent directory entry File-Cache. It set the dirty bit in the modified buffer of the parent directory File-Cache. === __unsigned int '''_add_dir_entry'''( fat_inode_t* child , fat_inode_t* parent )__ === The following function add new entry in the Cache-File of the directory. It accesses the File_Cache associated to the parent directory, and scan the clusters allocated to this directory to find the NO_MORE entry, that will be the first modified entry in the directory. Regarding the name storage, it uses LFN entries for all names, but computes a legal (upper case / 8.3) SFN name that will be stored in the NORMAL entry. It writes 1, 2, or 3 LFN entries (depending on the child name actual length), writes one NORMAL entry, and writes the new NO_MORE entry. It updates the dentry field in the child inode. It set the dirty bit for all modified File-Cache buffers. The block device is not modified by this function. === __unsigned int '''_remove_dir_entry'''( fat_inode_t* inode )__ === This function invalidates all dir_entries associated to the argument from its parent directory. It set the dirty bit for all modified buffers in parent directory Cache-File. The inode itself is not modified by this function. The block device is not modified by this function. === __void '''_add_special_directories'''( fat_inode_t* child , fat_inode_t* parent )__ === This function adds the special entries "." and ".." in the File-Cache of the directory identified by the argument. The parent directory is defined by the argument. The child directory File-Cache is supposed to be empty. We use two NORMAL entries for these "." and ".." entries. The block device is not modified by this function. === __unsigned int '''_clusters_release'''( unsigned int cluster )__ === The following function releases all clusters allocated to a file or directory, from the cluster index defined by the argument, until the end of the FAT linked list. It calls _get_fat_entry() and _set_fat_entry() functions to scan the FAT, and to update the clusters chaining. The FAT region on block device is updated. It returns 0 on success. It returns 1 on error. === __unsigned int '''_cluster_allocate'''( fat_inode_t* inode , unsigned int* cluster )__ === This function allocates one cluster in FAT to a file (or directory) identified by the pointer. The allocated cluster index is returned in the argument. It allocates also the associated buffers and buffer descriptors in Cache-File. It calls _get_fat_entry() and _set_fat_entry() functions to update the clusters chaining in the Cache-Fat. The FAT region on block device is updated. It returns 0 on success. It returns 1 on error. === __void '''_release_cache_memory'''( fat_cache_node_t* root , unsigned int levels )__ === This recursive function scans one File-Cache (or Fat-Cache) from root to leaves. All memory allocated for 4KB buffers, and buffer descriptors (in leaves) is released, along with the 64-Tree structure (root node is kept). The released cache is identified by the and arguments. It should not contain any dirty clusters. === __fat_cache_node_t* '''_allocate_one_cache_node'''( fat_cache_node_t* first_child )__ === This function allocates and initializes a new cache node. Its first child can be specified (used when adding a cache level). The 63 other children are set to NULL. It returns a pointer to the new Fat-Cache node. === __fat_inode_t* '''_allocate_one_inode'''( char* name , unsigned int is_dir , unsigned int cluster , unsigned int size , unsigned int count , unsigned int dentry , unsigned int cache_allocate )__ === This function allocates and initializes a new inode, using the values defined by the , , , , , and arguments. If the argument is true, an empty cache is allocated. It returns a pointer on the new inode. === __void '''_allocate_one_buffer'''( fat_inode_t* inode , unsigned int cluster_id , unsigned int cluster )__ === This function allocates a 4 Kbytes buffer and the associated cluster descriptor from the heap[x,y] defined by the calling thread, for the file (or directory) identified by the argument. It updates the Cache_File slot identified by the argument. The File-Cache slot must be empty. It updates the buffer descriptor, using the argument, that is the cluster index in FAT. The buffer descriptor dirty bit is set. It traverses the 64-tree Cache-file from top to bottom to find the last level. === __unsigned int '''_allocate_one_cluster'''( unsigned int* cluster )__ == This function allocates one free cluster from the FAT and returns the cluster index in the argument. It updates the FAT slot, and the two FAT global variables "first_free_cluster" and "free_clusters_number". It returns 0 on success. It returns 1 on error. === __unsigned int '''_remove_node_from_fs'''( fat_inode_t* inode )__ === This function remove from the file system a file or a directory identified by the argument. The remove conditions must be checked by the caller. The relevant lock(s) must have been taken by te caller. It returns 0 on success. It returns 1 on error. === __unsigned int '''_file_info_no_cache'''( char* pathname , unsigned int* file_cluster , unsigned int* file_size )__ === This function returns in the and arguments the cluster index and the size for a file identified by the argument, scanning directly the block device DATA region. It is intended to be called only by the _fat_load_no_cache() function, as it does not use the dynamically allocated File-Caches, but uses only the 4 Kbytes _fat_buffer_data. It returns 0 on success. It returns 1 on error. === __unsigned int '''_next_cluster_no_cache'''( unsigned int cluster , unsigned int* next )__ === This function scan directly the FAT region on the block device, and returns in the argument the value stored in the fat slot identified by the argument. It is intended to be called only by the _fat_load_no_cache() function, as it does not use the dynamically allocated Fat-Cache, but uses only the 4 Kbytes _fat_buffer_fat. It returns 0 on success. It returns 1 on error. === __int '''get_length'''( int offset , int length )__ === This function returns the the size (bytes) of a FAT field, identified by an mnemonic defined in the fat32.h file. === __int '''get_offset'''( int offset , int length )__ === This function returns the the offset (bytes) of a FAT field, identified by an mnemonic defined in the fat32.h file.