////////////////////////////////////////////////////////////////////////////////// // File : fat32.c // Date : 01/09/2013 // Authors : Marco Jankovic, Cesar Fuguet & Alain Greiner // Copyright (c) UPMC-LIP6 ////////////////////////////////////////////////////////////////////////////////// // The fat32.h and fat32.c files define a library of access functions // to a FAT32 disk on a block device. It is intended to be used // by the GIET_VM nano-kernel for both the boot code and the kernel code. // This code uses functions defined in the utils.c and drivers.c files. ////////////////////////////////////////////////////////////////////////////////// // Implementation notes: // 1. the "lba" (Logical Block Address) is the physical sector index on // the block device. The physical sector size is supposed to be 512 bytes. // 2. the "cluster" variable is actually a cluster index. A cluster contains // typically 8 sectors (4K bytes) and the cluster index is a 32 bits word. // 2. This FAT32 library uses a FAT cache whose storage capacity is one // sector (512 bytes, or 128 cluster indexes in FAT) // 3. This FAT32 library can be used in 3 modes: BOOT/BOOT/KERNEL/USER // defining different behaviours for the IOC driver. ////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////// // Global variable : internal FAT representation ////////////////////////////////////////////////////////////////////////////////// extern fat32_fs_t fat __attribute__((aligned(512))); ////////////////////////////////////////////////////////////////////////////////// // This function displays the content of the FAT cache ////////////////////////////////////////////////////////////////////////////////// void _display_fat_cache() { unsigned int line; unsigned int word; unsigned int temp[9]; temp[8] = 0; _puts("\n*********************** fat_cache_lba = "); _putx( fat.cache_lba ); _puts(" *****************************\n"); for ( line = 0 ; line < 16 ; line++ ) { // display line index _putx( line ); _puts(" : "); // display 8*4 bytes hexa for ( word=0 ; word<8 ; word++ ) { unsigned int byte = (line<<5) + (word<<2); unsigned int hexa = (fat.fat_cache[byte ]<<24) | (fat.fat_cache[byte+1]<<16) | (fat.fat_cache[byte+2]<< 8) | (fat.fat_cache[byte+3]); _putx( hexa ); _puts(" | "); // prepare display ascii temp[word] = fat.fat_cache[byte] | (fat.fat_cache[byte+1]<<8) | (fat.fat_cache[byte+2]<<16) | (fat.fat_cache[byte+3]<<24) ; } // display data ascii _puts( (char*)temp ); _puts("\n"); } _puts("***************************************************************************\n"); } // end _display_fat_cache() ////////////////////////////////////////////////////////////////////////////////// // This function returns the length of a FAT field. This field is identified // by an (offset,length) mnemonic defined in fat32.h file. ////////////////////////////////////////////////////////////////////////////////// static inline int get_length( int offset, int length ) { return length; } ////////////////////////////////////////////////////////////////////////////// // Write one 32 bits word "value" in a char[] buffer. // The modified field in buffer is defined by the offset and size arguments. ////////////////////////////////////////////////////////////////////////////// static void _write_entry( unsigned int offset, unsigned int size, char* buffer, unsigned int value ) { unsigned int turn = 0; unsigned int res = value; unsigned int mask = 0x000000ff; while( turn != size - 1 ) { buffer[ offset + turn ] = res & mask; res = res >> 8; turn++; } buffer[offset + turn] = res & mask; } ////////////////////////////////////////////////////////////////////////////// // Read one 32 bits word in a char[] buffer, taking endianness into account. // The analysed field in buffer is defined by the offset and size arguments. ////////////////////////////////////////////////////////////////////////////// static unsigned int _read_entry( unsigned int offset, unsigned int size, char* buffer, unsigned int little_indian ) { unsigned int turn; unsigned int res = 0; unsigned int mask = 0x000000ff; if( little_indian ) { turn = size; while( turn != 1 ) { res = res | (buffer[offset + (turn-1)] & mask); res = res << 8; turn--; } res = (buffer[offset + (turn-1)] & mask) | res; } else { turn = 0; while( turn != size - 1 ) { res = res | (buffer[ offset + turn ] & mask ); res = res << 8; turn++; } res = res | (buffer[offset + turn] & mask); } return res; } ////////////////////////////////////////////////////////////////////////////////// // This function retuns the cluster index from the lba of a DATA sector. // The lba must be larger than the lba of the first DATA sector. // The DATA region indexing starts a cluster 2. ////////////////////////////////////////////////////////////////////////////////// static inline unsigned int lba_to_cluster( unsigned int lba ) { if (lba < fat.data_lba ) return 0; return ( (lba - fat.data_lba) / fat.sectors_per_cluster) + 2; } ////////////////////////////////////////////////////////////////////////////////// // This function retuns the lba of first sector in DATA region // from the cluster index. The cluster index must be larger than 2. ////////////////////////////////////////////////////////////////////////////////// static inline unsigned int cluster_to_lba( unsigned int cluster ) { if ( cluster < 2 ) return 0; return (fat.sectors_per_cluster * (cluster - 2)) + fat.data_lba; } ///////////////////////////////////////////////////////////////////////////////// // This function search the FAT (using the FAT cache), and returns // the next cluster index from the current cluster index in the FAT. // remark: a sector of FAT contains 128 cluster indexes. ///////////////////////////////////////////////////////////////////////////////// static unsigned int _get_next_cluster( unsigned int mode, unsigned int cluster ) { // compute lba of the sector containing the cluster index unsigned int lba = fat.fat_lba + (cluster / 128); #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1< 1) _display_fat_cache(); #endif } unsigned int next = _read_entry( ((cluster % 128) * 4), 4, fat.fat_cache, 1 ); #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _get_next_cluster() : P[%d,%d,%d] next cluster = %x\n", next ); #endif return next; } ////////////////////////////////////////////////////// static inline unsigned char to_upper(unsigned char c) { if (c >= 'a' && c <= 'z') return (c & ~(0x20)); else return c; } //////////////////////////////////////////////////////////////// // This function is a filter: // Return the c character if c is a legal short name character // Return the '_' character if c is illegal //////////////////////////////////////////////////////////////// static unsigned char illegal_short(unsigned char c) { const unsigned char illegal_char [] =";+=[]’,\"*\\<>/?:|\0"; short i = 0; while (illegal_char[i]!='\0') { if (c == illegal_char[i]) return '_'; i++; } return c; } ///////////////////////////////////////////////////////////////////////////////// // This function test if the string argument is a legal SFN (Short File Name) // and copies this name (removing the .) in the sfn_string argument. // Criteria for a Short File Name are: // - separator is '.' (extension is not mandatory) // - 1 <= name length <= 8 // - 0 <= extension length <= 3 // - no illegal character (see illega_short() function) // Return 1 if it string is a legal SFN // Return 0 if not legal SFN ///////////////////////////////////////////////////////////////////////////////// static int is_short( char* string, char* sfn_string) { int s_size = 0; int dot_ext = 0; // dot offset in the filename int name_len = 0; // length of file name int ext_len = 0; // length of extension int i = 0; int sc_i = 0; char ch; if(string[0] == '.' && string[1] == '\0') { sfn_string[0] = '.'; return 1; } if(string[0] == '.' && string[1] == '.' && string[2] == '\0') { sfn_string[0] = '.'; sfn_string[1] = '.'; return 1; } sfn_string[11] = '\0'; while (string[s_size] != '\0') { if (string[s_size] == '.') { dot_ext = s_size; ext_len = -1; } ext_len++; s_size++; } if (dot_ext != 0) { name_len = s_size - ext_len - 1; } else { name_len = s_size; ext_len = 0; } if ( ext_len > 3 || ( name_len > 8)) { return 0; } if (dot_ext != 0) { while (i != ext_len) { ch = to_upper(string[dot_ext + 1 + i]); ch = illegal_short(ch); sfn_string[8+i] = ch; i++; } } i = 0; sc_i = 0; while (i!= name_len) { ch = to_upper(string[i]); ch = illegal_short(ch); if (ch != '.') sfn_string[sc_i++] = ch; i++; } return 1; } /////////////////////////////////////////////////////////////////////// // This function analyses the pathname argument, from the character // defined by the *nb_read argument. // It copies the found name (between '/') in the name[] buffer, // and updates the nb_read argument. // Return 1 if name found, Return 0 if NUL character found, /////////////////////////////////////////////////////////////////////// static int get_name_from_path( char* pathname, char* name, unsigned int* nb_read ) { if ( pathname[*nb_read] == 0 ) return 0; int i = (pathname[*nb_read] == '/')? (*nb_read) + 1 : *nb_read; int j = 0; while(pathname[i] != '/' && pathname[i] != '\0') { name[j] = pathname[i]; j++; i++; } name[j] = 0; if ( pathname[i] == '/' ) *nb_read += j+1; else *nb_read += j; return 1; } //////////////////////////////////////////////////////////////////////////////// static int get_name_from_short( char* dir_entry, // input: SFN dir_entry char* entry_name ) // output: name { unsigned int i = 0; unsigned int length = get_length(DIR_NAME); while ( i < length ) { entry_name[i] = dir_entry[i]; i++; } entry_name[i] = '\0'; return i; } /////////////////////////////////////////////////////////////////////////////// static int get_name_from_long( char *dir_entry, // input : LFN dir_entry char *entry_name ) // output : name { unsigned int entry_name_offset = 0; unsigned int dir_entry_offset = get_length(LDIR_ORD); unsigned int l_name_1 = get_length(LDIR_NAME_1); unsigned int l_name_2 = get_length(LDIR_NAME_2); unsigned int l_name_3 = get_length(LDIR_NAME_3); unsigned int l_attr = get_length(LDIR_ATTR); unsigned int l_type = get_length(LDIR_TYPE); unsigned int l_chksum = get_length(LDIR_CHKSUM); unsigned int l_rsvd = get_length(LDIR_RSVD); unsigned int j = 0; unsigned int eof = 0; while ( (dir_entry_offset != DIR_ENTRY_SIZE) && (!eof) ) { while (j != l_name_1 && !eof ) { if ( (dir_entry[dir_entry_offset] == 0x00) || (dir_entry[dir_entry_offset] == 0xFF) ) { eof = 1; continue; } entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; dir_entry_offset += 2; j += 2; entry_name_offset++; } dir_entry_offset += (l_attr + l_type + l_chksum); j = 0; while (j != l_name_2 && !eof ) { if ( (dir_entry[dir_entry_offset] == 0x00) || (dir_entry[dir_entry_offset] == 0xFF) ) { eof = 1; continue; } entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; dir_entry_offset += 2; j += 2; entry_name_offset++; } dir_entry_offset += l_rsvd; j = 0; while (j != l_name_3 && !eof ) { if ( (dir_entry[dir_entry_offset] == 0x00) || (dir_entry[dir_entry_offset] == 0xFF) ) { eof = 1; continue; } entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; dir_entry_offset += 2; j += 2; entry_name_offset++; } } entry_name[entry_name_offset] = '\0'; return entry_name_offset; } // end get_name_from_long() ////////////////////////////////////////////////////////////////////////////////////// // This function update a DIR_ENTRY, write a new value into a specific field // (ex : DIR_FILE_SIZE, when we want update the file size after a fat_write) // Return 0 in case of success, > 0 if failure. ////////////////////////////////////////////////////////////////////////////////////// // TODO : make this function less complex ////////////////////////////////////////////////////////////////////////////////////// static inline unsigned int update_entry( unsigned int fd_id, unsigned int field, unsigned int size, unsigned int value ) { char dir_entry[32]; // buffer to store a full directory_entry char name_entry[14]; // buffer to store a 13 characters (partial) name char file_name[256]; // buffer to store the name (not pathname) of the file char sfn_string[12] = {[0 ... 10] = ' ', '\0'}; // buffer for a Short File Name unsigned int lba = fat.fd[fd_id].lba_dir_entry; // Lba of file dir_entry unsigned int is_sfn; unsigned int attr = 0; // directory entry attribute unsigned int ord = 0; // directory entry sequence unsigned int found = 0; // name found unsigned int offset = 0; // offset in fat_cache unsigned int i = 0; unsigned int nb_read = 0; for ( i = 0 ; i < 32 ; i++ ) dir_entry[i] = 0; for ( i = 0 ; i < 14 ; i++ ) name_entry[i] = 0; // Get the name of the file. while ( get_name_from_path( fat.fd[fd_id].name, file_name, &nb_read ) ) { } // Format file_name to SFN format is_sfn = is_short( file_name, sfn_string ); if ( _ioc_read( 0, // channel IOC_KERNEL_MODE, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _printf("\n[FAT ERROR] in update_entry() cannot read sector %x\n", lba ); return 1; } fat.cache_lba = lba; // - the offset increment is an integer number of directory entry (32 bytes) // - the exit condition is success (name found) or failure (end of directory) while ( !found ) { attr = _read_entry( DIR_ATTR, fat.fat_cache + offset, 0 ); ord = _read_entry( LDIR_ORD, fat.fat_cache + offset, 0 ); if ( is_sfn == 1 ) // searched name is short { if ( (ord != FREE_ENTRY ) && (ord != NO_MORE_ENTRY) && (attr == ATTR_LONG_NAME_MASK) ) // LFN entry : skipped { offset = offset + ((ord & 0xF) * DIR_ENTRY_SIZE); continue; } else if ( (attr != ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY ) ) // SFN entry : checked { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); } else if (ord == NO_MORE_ENTRY ) // end of directory : return { _printf("\n[FAT ERROR] in update_entry() : reaches end of directory\n"); return 1; } else // free entry : skipped { offset = offset + DIR_ENTRY_SIZE; continue; } } else // searched name is long { if ( (attr == ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY) ) // LFN entry : checked { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); } else if ( (attr != ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY)) // SFN entry : skipped { offset = offset + DIR_ENTRY_SIZE; continue; } else if (ord == NO_MORE_ENTRY ) // end of directory : return { _printf("\n[FAT ERROR] in update_entry() reaches end of directory\n"); return 1; } else // free entry : skipped { offset = offset + DIR_ENTRY_SIZE; continue; } } // testing the name extracted from dir entry if ( is_sfn == 1 ) // searched name is short { get_name_from_short( dir_entry, name_entry ); if ( _strncmp( (char*)sfn_string, (char*)name_entry, 13 ) == 0 ) { _write_entry(offset + field, size, fat.fat_cache, value); found = 1; } else // no matching : skip { offset = offset + DIR_ENTRY_SIZE; } } else // searched name is long { get_name_from_long( dir_entry, name_entry ); unsigned shift = ((ord & 0xf) - 1) * 13; if ( _strncmp( (char*)(file_name + shift), (char*)name_entry, 13 ) == 0 ) { if ( (ord & 0xf) == 1 ) { offset = offset + DIR_ENTRY_SIZE; _write_entry(offset + field, size, fat.fat_cache, value); found = 1; } offset = offset + DIR_ENTRY_SIZE; continue; } else // no matching : skip { offset = offset + ((ord & 0xf) * DIR_ENTRY_SIZE) + DIR_ENTRY_SIZE; } } } return _ioc_write( 0, // channel IOC_KERNEL_MODE, // mode lba, // sector index fat.fat_cache, // source buffer 1 ); // one sector } ////////////////////////////////////////////////////////////////////////////////// // This function update FS_INFO: // last cluster allocated and number of free cluster. // Return 0 in case of success, > 0 if failure. ////////////////////////////////////////////////////////////////////////////////// static inline unsigned int _update_fs_info( ) { unsigned int lba = fat.fs_info_lba; #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _update_fs_info() : enters\n"); #endif if ( lba == fat.cache_lba ) // hit cache { _write_entry( FS_FREE_CLUSTER , fat.fat_cache, fat.number_free_cluster ); _write_entry( FS_FREE_CLUSTER_HINT, fat.fat_cache, fat.last_cluster_allocated ); } else // miss cache { if ( _ioc_read( 0, // channel IOC_KERNEL_MODE, // mode for IOC driver lba, // sector index fat.fat_cache, // source buffer 1 ) ) // one sector { _printf("\n[FAT_ERROR] in _update_fat() cannot read block %x\n", lba ); return 1; } fat.cache_lba = lba; _write_entry( FS_FREE_CLUSTER , fat.fat_cache, fat.number_free_cluster ); _write_entry( FS_FREE_CLUSTER_HINT, fat.fat_cache, fat.last_cluster_allocated ); } return _ioc_write( 0, // channel IOC_KERNEL_MODE, // mode lba, // sector index fat.fat_cache, // source buffer 1 ); // one sector } ////////////////////////////////////////////////////////////////////////////////// // This function update FAT, write a new value into cluster index. // Used by the function _fat_allocate to update the chaining of clusters. // Return 0 in case of success, > 0 if failure. ////////////////////////////////////////////////////////////////////////////////// static inline unsigned int _update_fat( unsigned int cluster, unsigned int value ) { unsigned int lba = fat.fat_lba + (cluster / 128); #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _update_fat() : cluster = %x / value = %x\n", cluster, value ); #endif if ( lba == fat.cache_lba ) // hit cache { _write_entry( ((cluster % 128) << 2), 4, fat.fat_cache, value ); } else // miss cache { if ( _ioc_read( 0, // channel IOC_KERNEL_MODE, // mode for IOC driver lba, // sector index fat.fat_cache, // source buffer 1 ) ) // one sector { _printf("\n[FAT_ERROR] in _update_fat() cannot read block %x\n", lba ); return 1; } fat.cache_lba = lba; _write_entry( ((cluster % 128) << 2), 4, fat.fat_cache, value ); } return _ioc_write( 0, // channel IOC_KERNEL_MODE, // mode lba, // sector indexs fat.fat_cache, // source buffer 1 ); // one sector } // end update_fat() ////////////////////////////////////////////////////////////////////////////////// // This function allocate count clusters to a file by calling the // _update_fat function that takes care to update the chaining of clusters. // return 0 if success, -1 if failure ////////////////////////////////////////////////////////////////////////////////// static inline int _fat_allocate( unsigned int fd_id, unsigned int count ) { unsigned int next_cluster = fat.fd[fd_id].first_cluster; // get first cluster unsigned int cluster_to_allocate = count; // clusters to allocate unsigned int last_cluster_file; // Last cluster (EOC) unsigned int free_cluster = fat.last_cluster_allocated + 1; // First free cluster // Check if free_cluster is really free (must be true) if ( _get_next_cluster( IOC_KERNEL_MODE, free_cluster ) != FREE_CLUSTER) { _printf("\n[FAT ERROR] in _fat_allocate() : first free cluster not free\n"); return -1; } // Check if FAT contains enough cluster free for this allocation if ( count > fat.number_free_cluster ) { _printf("\n[FAT ERROR] in _fat_allocate() : Not enough free cluster(s)\n"); return -1; } #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _fat_allocate() for fd = %d\n" fd_id ); #endif // Get the last cluster allocated for the file (seek END_OF_CHAIN_CLUSTER). do { last_cluster_file = next_cluster; next_cluster = _get_next_cluster( IOC_KERNEL_MODE, next_cluster ); } while ( next_cluster < END_OF_CHAIN_CLUSTER ); // Loop on the number of clusters to be allocated while ( cluster_to_allocate > 0 ) { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] cluster to update = %x / free cluster = %x / nb_clusters %d\n", last_cluster_file , free_cluster , cluster_to_allocate ); #endif // update, in the FAT, the value of last cluster allocated by the index // of free cluster. if ( _update_fat( last_cluster_file, free_cluster ) ) { _printf("\n[FAT ERROR] in _fat_allocate() : update fat failed\n"); return -1; } cluster_to_allocate = cluster_to_allocate - 1; // Last cluster allocated is then free_cluster last_cluster_file = free_cluster; // Last cluster to allocate done, then we must close the chain of clusters if ( cluster_to_allocate == 0 ) { // update, in the FAT, the value of the last cluster allocated by // END_OF_CHAIN_CLUSTER if ( _update_fat( last_cluster_file, END_OF_CHAIN_CLUSTER ) ) { _printf("\n[FAT ERROR] in _fat_allocate() : update fat failed\n"); return -1; } } free_cluster = free_cluster + 1; // Check if free_cluster is really free (must be true) if ( _get_next_cluster( IOC_KERNEL_MODE, free_cluster ) != FREE_CLUSTER) { _printf("\n[FAT ERROR] in _fat_allocate() : free_cluster not free\n"); return -1; } } // Update field number_free_cluster and last_cluster_allocated // of structure fat for next fat_allocate fat.last_cluster_allocated = last_cluster_file; fat.number_free_cluster = fat.number_free_cluster - count; if ( _update_fs_info() ) { _printf("\n[FAT ERROR] in _fat_allocate() : update fs_info failed\n"); return -1; } return 0; } // end _fat_allocate() //////////////////////////////////////////////////////////////////////////////////////// // This function read the blocks defined by the cluster index argument, in a data // region containing a directory to search the name of a file/firectory. // It returns the cluster index of the file/directory when the name has been found, // as well as the file size, and the lba. // We consider 3 types of directory entries: // - SFN : directory entry associated to a Short File Name (8.3) // - LFN : directory entry associated to a Long File Name // - XTN : directory entry containing only a name extension for a Long File Name // The cluster index is always stored in a SFN or LFN entry. // Return cluster index if name found / Return -1 if not found. //////////////////////////////////////////////////////////////////////////////////////// static int _scan_directory( unsigned int mode, // mode for IOC driver unsigned int cluster, // cluster containing dir_entry char* file_name, // searched file/directory name unsigned int* file_size, // file size unsigned int* lba_dir_entry ) // lba of dir_entry { char dir_entry[32]; // buffer to store a full directory_entry char name_entry[14]; // buffer to store a 13 characters (partial) name char sfn_string[12] = {[0 ... 10] = ' ', '\0'}; // buffer for Short File Name unsigned int is_sfn = is_short(file_name, sfn_string); // if file_name is short unsigned int offset = 0; // byte offset in block unsigned int block_id = fat.sectors_per_cluster; // sector index initialisation unsigned int lba = cluster_to_lba(cluster); // lba of cluster containing dir unsigned int attr = 0; // directory entry attribute unsigned int ord = 0; // directory entry sequence unsigned int found = 0; // searched name found unsigned int long_name_found = 0; // a matching XTN has been found unsigned int searched_cluster; // searched cluster index #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1< 1 ) _display_fat_cache(); #endif // in this loop we scan all names in the directory identified by cluster index // - the offset increment is an integer number of directory entry (32 bytes) // - the exit condition is success (name found) or failure (end of directory) while( found == 0 ) { // load a new sector if required if (offset >= 512) { if ( block_id ) // not a new cluster { lba += 1; block_id --; } else // get next cluster { cluster = _get_next_cluster( mode, cluster ); if ( cluster >= END_OF_CHAIN_CLUSTER ) return END_OF_CHAIN_CLUSTER; lba = cluster_to_lba(cluster); block_id = fat.sectors_per_cluster; } if( _ioc_read( 0, // channel mode, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _printf("\n[FAT ERROR] in _scan_directory() : cannot read sector %x\n", lba); return -1; } fat.cache_lba = lba; block_id--; offset = offset % 512; } // store the directory entry pointed by offset in dir_entry buffer, // if it a possible candidate for the searched name attr = _read_entry( DIR_ATTR, fat.fat_cache + offset, 0); ord = _read_entry( LDIR_ORD, fat.fat_cache + offset, 0); if ( is_sfn == 1 ) // searched name is short { if ( (ord != FREE_ENTRY ) && (ord != NO_MORE_ENTRY) && (attr == ATTR_LONG_NAME_MASK) ) // EXT entry : skipped { offset = offset + ((ord & 0xF) * DIR_ENTRY_SIZE); } else if ( (attr != ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY ) ) // SFN entry : to be checked { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); get_name_from_short( dir_entry, name_entry ); if ( _strncmp( (char*)sfn_string, (char*)name_entry, 13 ) == 0 ) // short name found { found = 1; } else { offset = offset + DIR_ENTRY_SIZE; } } else if (ord == NO_MORE_ENTRY ) // end of directory : return { return END_OF_CHAIN_CLUSTER; } } else // searched name is long { if( (attr == ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY) ) // EXT entry : to be checked { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); get_name_from_long( dir_entry, name_entry ); unsigned shift = ((ord & 0xf) - 1) * 13; if ( _strncmp( (char*)(file_name + shift), (char*)name_entry, 13 ) == 0 ) // matching EXT { if( (ord & 0xf) == 1 ) // long name found { long_name_found = 1; } } offset = offset + DIR_ENTRY_SIZE; } else if( (attr != ATTR_LONG_NAME_MASK) && (ord != FREE_ENTRY) && (ord != NO_MORE_ENTRY) ) { if ( long_name_found ) // LFN entry { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); found = 1; } else // SFN entry: must be skipped { offset = offset + DIR_ENTRY_SIZE; } } else if (ord == NO_MORE_ENTRY ) // end of directory : return { return END_OF_CHAIN_CLUSTER; } } } // end while // returns cluster index *file_size = _read_entry( DIR_FILE_SIZE, dir_entry, 1 ); *lba_dir_entry = lba; searched_cluster = (_read_entry( DIR_FST_CLUS_HI, dir_entry, 1 ) << 16) | (_read_entry( DIR_FST_CLUS_LO, dir_entry, 1 ) ) ; #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _scan_directory() : P[%d,%d,%d] found %s" " : cluster = %x\n", x, y, p, file_name, searched_cluster ); #endif return searched_cluster; } // end _scan_directory() ////////////////////////////////////////////////////////////////////// // This function create a new entry in a directory identified // by "dir_cluster". The name is defined by "name". // The type (dir/file) is defined by "is_file". // Returns cluster index if success, Returns -1 if error. ////////////////////////////////////////////////////////////////////// static int _fat_create( char* name, unsigned int is_file, unsigned int dir_cluster ) { _printf("\n[FAT ERROR] _fat_create() not implemented\n"); return 0; } //end _fat_create() ////////////// Extern functions ////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // This function initializes the FAT structure, including the open // files descriptors array and the lock protecting the FAT, // from informations found in the boot record. // This should be done only once. ////////////////////////////////////////////////////////////////////////// // Return 0 if success, exit if failure ////////////////////////////////////////////////////////////////////////// int _fat_init( unsigned int mode ) // mode for IOC driver { unsigned int n; #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1< 1 _printf("\n[FAT DEBUG] _fat_init() : Boot Sector Loaded\n"); _display_fat_cache(); #endif // checking various FAT32 assuptions from boot sector if( _read_entry( BPB_BYTSPERSEC, fat.fat_cache, 1 ) != 512 ) { _printf("\n[FAT ERROR] The sector size must be 512 bytes\n"); _exit(); } if( _read_entry( BPB_NUMFATS, fat.fat_cache, 1 ) != 1 ) { _printf("\n[FAT ERROR] The number of FAT copies in FAT region must be 1\n"); _exit(); } if( (_read_entry( BPB_FAT32_FATSZ32, fat.fat_cache, 1 ) & 0xF) != 0 ) { _printf("\n[FAT ERROR] The FAT region in FAT32 must be multiple of 32 sectors\n"); _exit(); } if( _read_entry( BPB_FAT32_ROOTCLUS, fat.fat_cache, 1 ) != 2 ) { _printf("\n[FAT ERROR] The first cluster index must be 2\n"); _exit(); } // FS Info always in sector 1 fat.fs_info_lba = _read_entry( BPB_FAT32_FSINFO, fat.fat_cache, 1 ); // initialise fat descriptor from VBR fat.sectors_per_cluster = _read_entry( BPB_SECPERCLUS, fat.fat_cache, 1 ); fat.sector_size = _read_entry( BPB_BYTSPERSEC, fat.fat_cache, 1 ); fat.cluster_size = fat.sectors_per_cluster * 512; fat.fat_sectors = _read_entry( BPB_FAT32_FATSZ32, fat.fat_cache, 1 ); fat.fat_lba = _read_entry( BPB_RSVDSECCNT, fat.fat_cache, 1 ); fat.data_lba = fat.fat_lba + fat.fat_sectors; fat.initialised = FAT_INITIALISED; // initalise the lock protecting the FAT _spin_lock_init( &fat.fat_lock ); // initialise file descriptor array for( n = 0 ; n < GIET_OPEN_FILES_MAX ; n++ ) fat.fd[n].used = 0; #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _fat_init() : FS_INFO Sector = %x\n", fat.fs_info_lba ); #endif // load FS_INFO into fat cache if ( _ioc_read( 0, // channel mode, // mode for IOC driver fat.fs_info_lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _printf("\n[FAT ERROR] in _fat_init() cannot load FS_INFO Sector\n"); _exit(); } fat.cache_lba = fat.fs_info_lba; fat.number_free_cluster = _read_entry( FS_FREE_CLUSTER , fat.fat_cache, 1); fat.last_cluster_allocated = _read_entry( FS_FREE_CLUSTER_HINT, fat.fat_cache, 1); #if GIET_DEBUG_FAT _fat_print(); _printf("\n[FAT DEBUG] P[%d,%d,%d] exit _fat_init()\n" ); #endif return 0; } // end _fat_init() ///////////////// void _fat_print() { _printf("\n########################## FAT32 ################################" "\nFAT initialised %x" "\nSector Size (bytes) %x" "\nSectors per cluster %x" "\nFAT region first lba %x" "\nData region first lba %x" "\nNumber of sectors for one FAT %x" "\nNumber of free clusters %x" "\nLast allocated cluster %x" "\n#################################################################\n", fat.initialised, fat.sector_size, fat.sectors_per_cluster, fat.fat_lba, fat.data_lba, fat.fat_sectors, fat.number_free_cluster, fat.last_cluster_allocated ); } /////////////////////////////////////////////////////////////////////////////// // This function checks that the kernel FAT structure has been initialised. // It searches a file identified by the "pathname" argument. // It starts from root (cluster 2) to scan successively each subdirectory. // When the file is not found, but the path is found, and "creat" is set, // a new file is created and introduced in the directory. // Finally, it sets a new open file in the file descriptors array. // The same file can be open several times by differents tasks. /////////////////////////////////////////////////////////////////////////////// // Returns file descriptor index if success, returns -1 if error. /////////////////////////////////////////////////////////////////////////////// int _fat_open( unsigned mode, char* pathname, unsigned int creat ) { char name[256]; // buffer for one name in pathname unsigned int nb_read; // number of characters written in name[] unsigned int cluster; // current cluster index when scanning FAT unsigned int dir_cluster; // previous cluster index when scanning FAT unsigned int fd_id; // index when scanning file descriptors array unsigned int file_size = 0; // number of bytes unsigned int last_name = 0; // directory containing file name is reached unsigned int lba = 0; // lba of dir_entry for this file #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1<= GIET_OPEN_FILES_MAX ) { _printf("\n[FAT ERROR] in _fat_read() : illegal file descriptor index\n"); return -1; } if ( fat.fd[fd_id].used != 1 ) { _printf("\n[FAT ERROR] in _fat_read() : file not open\n"); return -1; } if ( ((unsigned int)buffer & 0x1FF) != 0 ) { _printf("\n[FAT ERROR] in _fat_read() : memory buffer not sector aligned\n"); return -1; } // compute file size as a number of sectors file_size = fat.fd[fd_id].file_size; if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; else file_sectors = (file_size >> 9); if ( offset >= file_sectors ) { _printf("\n[FAT ERROR] offset larger than number of sectors\n"); return -1; } // compute total number of sectors to read if ( file_sectors < (offset + count) ) total_sectors = file_sectors - offset; else total_sectors = count; // compute clusters and sectors to be skipped clusters_to_skip = offset / spc; sectors_to_skip = offset % spc; // get first cluster index cluster = fat.fd[fd_id].first_cluster; #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1< 0 ) { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _fat_read() : P[%d,%d,%d] makes an IOC read\n" " - cluster = %x\n" " - buffer = %x\n" " - lba = %x\n" " - sectors = %x\n", x, y, p, cluster, (unsigned int)dst, lba, iter_sectors ); #endif if( _ioc_read( 0, // channel mode, // mode for IOC driver lba, // first sector index dst, // buffer address iter_sectors ) ) // number of sectors { _printf("\n[FAT ERROR] in _fat_read() cannot load block %x", lba ); return -1; } // update variables for next iteration cluster = _get_next_cluster( mode, cluster ); todo_sectors = todo_sectors - iter_sectors; dst = dst + (iter_sectors << 9); lba = cluster_to_lba(cluster); if ( todo_sectors > spc ) iter_sectors = spc; else iter_sectors = todo_sectors; } // returns number of sectors actually transfered return total_sectors; } // end _fat_read() /////////////////////////////////////////////////////////////////////////////// // For an open file, identified by the file descriptor index, transfer // an integer number of sectors from a memory buffer to block device. // Allocate new clusters if the offset+count larger than current file size, // but the offset should be smaller than the current file size... // - fat : pointer on FAT // - mode : mode for the IOC driver // - fd_id : open file descriptor index // - buffer : base address of the memory buffer (must be sector aligned) // - offset : number of sectors to skip in file // - count : number of sectors to be written. /////////////////////////////////////////////////////////////////////////////// // Returns number of sectors written if success, < 0 if error. /////////////////////////////////////////////////////////////////////////////// int _fat_write( unsigned int mode, // mode for IOC driver unsigned int fd_id, // file descriptor void* buffer, // target buffer base address unsigned int count, // number of sector to write unsigned int offset ) // nuber of sectors to skip in file { unsigned int spc = fat.sectors_per_cluster; unsigned int file_size; // number of bytes in file unsigned int file_sectors; // number of sectors in file unsigned int cluster; // cluster index unsigned int clusters_to_skip; // number of clusters to skip because offset unsigned int sectors_to_skip; // number of sectors to skip in first iteration unsigned int allocate; // need allocate or not unsigned int current_cluster; // number of cluster allocated to the file unsigned int required_cluster; // number of cluster needed for the write // compute file size as a number of sectors file_size = fat.fd[fd_id].file_size; if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; else file_sectors = (file_size >> 9); // Compute the number of clusters occupied by the file current_cluster = file_sectors / spc; // Compute the number of clusters that will occupy the file (after fat_write) required_cluster = (count + offset) / spc; // Check if we need to allocate new cluster(s) for the file allocate = ( required_cluster > current_cluster ); #if GIET_DEBUG_FAT unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1<= GIET_OPEN_FILES_MAX ) { _printf("\n[FAT ERROR] in _fat_write() : illegal file descriptor index\n"); return -1; } if ( fat.fd[fd_id].used != 1 ) { _printf("\n[FAT ERROR] in _fat_write() : file not open\n"); return -1; } if ( ((unsigned int)buffer & 0x1FF) != 0 ) { _printf("\n[FAT ERROR] in _fat_write() : memory buffer not sector aligned\n"); return -1; } if ( allocate ) { if ( _fat_allocate( fd_id, (required_cluster - current_cluster) ) < 0 ) { _printf("\n[FAT ERROR] in _fat_write() : fat_allocate failed\n"); return -1; } } // compute clusters and sectors to be skipped clusters_to_skip = offset / spc; sectors_to_skip = offset % spc; // get first cluster index cluster = fat.fd[fd_id].first_cluster; #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _fat_write() : P[%d,%d,%d] get cluster %x\n", x, y, p, cluster ); #endif // compute index of first cluster to be loaded // as we may need to scan the FAT, we use the kernel mode while ( clusters_to_skip ) { cluster = _get_next_cluster( IOC_KERNEL_MODE, cluster ); clusters_to_skip--; } // variables used in the loop on clusters int todo_sectors; // number of sectors still to be loaded unsigned int lba; // first sector index on device unsigned int iter_sectors; // number of sectors to load in iteration char* src; // pointer on target buffer // initialize these variables for the first iteration todo_sectors = count; src = (char*)buffer; lba = cluster_to_lba(cluster) + sectors_to_skip; if( count < (spc - sectors_to_skip) ) iter_sectors = count; else iter_sectors = spc - sectors_to_skip; // loop on the clusters while ( todo_sectors > 0 ) { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] _fat_write() : P[%d,%d,%d] makes an IOC write" " - cluster = %x\n" " - buffer = %x\n" " - lba = %x\n" " - sectors = %x\n", x, y, p, cluster, (unsigned int) src, lba, iter_sectors ); #endif if( _ioc_write( 0, // channel mode, // mode for IOC driver lba, // first sector index src, // source buffer address iter_sectors ) ) // number of sectors { _printf("\n[FAT ERROR] in _fat_write() cannot write block %x\n", lba ); return -1; } // update variables for next iteration cluster = _get_next_cluster( mode, cluster ); todo_sectors = todo_sectors - iter_sectors; src = src + (iter_sectors << 9); lba = cluster_to_lba(cluster); if ( todo_sectors > spc ) iter_sectors = spc; else iter_sectors = todo_sectors; } // Update structure file descriptor, field file_size with // the new file size if the file is bigger than the previous file if ( ( offset + count ) > file_sectors ) { fat.fd[fd_id].file_size = (count + offset) << 9; } // Update entry of directory with the new value // of file size (Field : DIR_FILE_SIZE) if ( update_entry(fd_id, DIR_FILE_SIZE, fat.fd[fd_id].file_size) ) { _printf("\n[FAT ERROR] in _fat_write() update entry failed\n"); return -1; } // returns number of sectors actually transfered return count; } // end _fat_write() ///////////////////////////////////////////////////////////////////////////////// // Return stats of a file identified by "fd". // (Only the file_size in sectors for this moment) ///////////////////////////////////////////////////////////////////////////////// // Returns file size (on sectors) on success, -1 on failure. ///////////////////////////////////////////////////////////////////////////////// int _fat_fstat( unsigned int fd_id ) { unsigned int file_size = 0; unsigned int file_sectors = 0; if( (fd_id < GIET_OPEN_FILES_MAX) ) { file_size = fat.fd[fd_id].file_size; if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; else file_sectors = (file_size >> 9); return file_sectors; } else { _printf("\n[FAT ERROR] in _fat_fstat() : illegal file descriptor index\n"); return -1; } } // end _fat_fstat() ///////////////////////////////////////////////////////////////////////////////// // Close the file identified by the file_descriptor index. ///////////////////////////////////////////////////////////////////////////////// // Returns 0 on success, -1 on failure. ///////////////////////////////////////////////////////////////////////////////// int _fat_close( unsigned int fd_id ) { if( (fd_id < GIET_OPEN_FILES_MAX) ) { fat.fd[fd_id].used = 0; return 0; } else { _printf("\n[FAT ERROR] in _fat_close() : illegal file descriptor index\n"); return -1; } } // end fat_close() ///////////////////////////////////////////////////////////////////////////////////// // The following function implement the user_level system call. // The flags argument is not used, as file access modes are not implemented yet. ///////////////////////////////////////////////////////////////////////////////////// // Return the file descriptor index if success / return -1 if failure ///////////////////////////////////////////////////////////////////////////////////// int _fat_user_open( char* pathname, // absolute pathname from root unsigned int flags ) // unused: TODO { return _fat_open( IOC_KERNEL_MODE, // we use KERNEL_MODE, because pathname, // we need to write into FAT cache 0 ); // no creation if not found } ///////////////////////////////////////////////////////////////////////////////////// // The following function implement the user_level system call. // This function should be modified to respect the UNIX specification ///////////////////////////////////////////////////////////////////////////////////// // Return number of sectors actually transfered if success / return -1 if failure ///////////////////////////////////////////////////////////////////////////////////// int _fat_user_read( unsigned int fd, // file descriptor index void* buffer, // destination buffer unsigned int count, // number of sectors to read unsigned int offset ) // number of sectors to skip { return _fat_read( IOC_USER_MODE, fd, buffer, count, offset ); } ///////////////////////////////////////////////////////////////////////////////////// // The following function implement the user_level system call. // This function should be modified to respect the UNIX specification. ///////////////////////////////////////////////////////////////////////////////////// // Return number of sectors actually transfered if success / return -1 if failure ///////////////////////////////////////////////////////////////////////////////////// int _fat_user_write( unsigned int fd, // file descriptor void* buffer, // source buffer unsigned int count, // number of sectors to write unsigned int offset ) // number of sectors to skip on file { return _fat_write( IOC_USER_MODE, fd, buffer, count, offset ); } ///////////////////////////////////////////////////////////////////////////////////// int _fat_user_lseek( unsigned int fd_id, unsigned int offset, unsigned int whence ) { _printf("\n[GIET ERROR] _fat_user_lseek() not implemented\n"); _exit(); return 0; } // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4