////////////////////////////////////////////////////////////////////////////////// // 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 four modes: BOOT_PA/BOOT_VA/KERNEL/USER // defining different behaviours for the IOC driver. ////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////// // Global variable used by all FAT access functions ////////////////////////////////////////////////////////////////////////////////// __attribute__((section (".kdata"))) fat32_fs_t fat __attribute__((aligned(64))); ////////////////////////////////////////////////////////////////////////////////// // 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; _printf("\n*********************** fat_cache_lba = %x *****************************\n", fat.cache_lba ); for ( line=0 ; line<16 ; line++ ) { // display address _printf( "%x : ", (fat.cache_lba<<9) + (line<<5) ); // display data 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]); _printf("%x | ", hexa ); // 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 _printf( (char*)temp ); _printf("\n"); } _printf("**************************************************************************\n"); } // end display_fat_cache() ////////////////////////////////////////////////////////////////////////////////// // This function returns the length of a FAT field. This field is identified // man 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. // This field 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. // This field 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_id( unsigned int mode, unsigned int cluster ) { // compute lba of the sector containing the cluster index unsigned int lba = fat.fat_lba + (cluster / 128); if ( lba == fat.cache_lba ) // hit in cache { return read_entry( ((cluster % 128) * 4), 4, fat.fat_cache, 1 ); } else // miss in cache { // we cannot access fat in user mode if( mode == IOC_USER_MODE ) mode = IOC_KERNEL_MODE; // access fat if( _ioc_read( 0, // channel mode, // mode for IOC driver lba, // sector index fat.fat_cache, // fat cache 1 ) ) // one sector { _printf("[FAT_ERROR] in get_next cluster_id() " "cannot read block %x\n", lba ); return 1; } fat.cache_lba = lba; return read_entry( ((cluster % 128) * 4), 4, fat.fat_cache, 1 ); } } ////////////////////////////////////////////////////// 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[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; 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 ) ) { } // Check if file_name is short is_sfn = is_short( file_name, sfn ); if ( _ioc_read( 0, // channel IOC_KERNEL_MODE, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _printf("[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("[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("[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, (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] Enter update_fs_info()\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("[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] Enter 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("[FAT_ERROR] in update_fat() cannot read block %x\n"); 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 } ////////////////////////////////////////////////////////////////////////////////// // This function allocate a count number of cluster 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 the first cluster of file unsigned int cluster_to_allocate = count; // Number of cluster to allocate unsigned int last_cluster_file; // Last cluster of the file (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_id( 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] Enter in _fat_allocate() for file %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_id( IOC_KERNEL_MODE, next_cluster ); }while ( next_cluster < END_OF_CHAIN_CLUSTER ); // Loop on the number of cluster needed to be allocated while ( cluster_to_allocate > 0 ) { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] cluster to update = %x / free cluster = %x / clusters required = %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_id( 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; } /////////////////////////////////////////////////////////////////////////////// // This function returns the cluster index from a (32 bytes) directory entry /////////////////////////////////////////////////////////////////////////////// static inline unsigned int read_cluster( char* buf ) { unsigned int cluster = read_entry( DIR_FST_CLUS_HI, buf, 1 ) << 16; cluster = cluster | read_entry( DIR_FST_CLUS_LO, buf, 1 ); return cluster; } //////////////////////////////////////////////////////////////////////////////////////// // 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, // and returns the cluster index of the file/directory when the name has been found. // 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 { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] enter _scan_directory() for dir/file %s\n", file_name ); #endif 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[12] = {[0 ... 10] = ' ', '\0'}; // buffer for a Short File Name unsigned int is_sfn = is_short(file_name, sfn); // if file_name is short, sfn = 1 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); unsigned int attr = 0; // directory entry attribute unsigned int ord = 0; // directory entry sequence unsigned int found = 0; // name found unsigned int i; for( i = 0 ; i < 32 ; i++ ) dir_entry[i] = 0; for( i = 0 ; i < 14 ; i++ ) name_entry[i] = 0; // load first cluster sector from DATA region into FAT cache // other sectors will be loaded inside loop as required if( _ioc_read( 0, // channel mode, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _printf("[FAT ERROR] in scan directory() cannot read sector %x\n", lba ); return -1; } fat.cache_lba = lba; #if ( GIET_DEBUG_FAT > 1 ) display_fat_cache(); #endif // in this loop we scan all names in directory identified by cluster: // - 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( 1 ) { // 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_id( 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("[FAT ERROR] in scan directory() cannot read sector %x\n", lba ); return -1; } fat.cache_lba = lba; block_id--; offset = offset % 512; } // analyse a directory entry (pointed by fat.fat_cache + offset) if ( !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 ); offset = offset + DIR_ENTRY_SIZE; } else if (ord == NO_MORE_ENTRY ) // end of directory : return { return END_OF_CHAIN_CLUSTER; } 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 ); offset = 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 director : return { return END_OF_CHAIN_CLUSTER; } 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, (char*)name_entry, 13 ) == 0 ) { *file_size = read_entry( DIR_FILE_SIZE , dir_entry, 1 ); *lba_dir_entry = lba; return read_cluster( dir_entry ); } } 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 ) found = 1; continue; } else // no matching : skip { offset = offset + ((ord & 0xf) * DIR_ENTRY_SIZE); } } } else // file found { memcpy( dir_entry, fat.fat_cache + offset, DIR_ENTRY_SIZE ); offset = offset + DIR_ENTRY_SIZE; *file_size = read_entry( DIR_FILE_SIZE, dir_entry, 1 ); *lba_dir_entry = lba; return read_cluster( dir_entry ); } } return -1; } // 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 // files descriptors array, from informations found in the boot record. ////////////////////////////////////////////////////////////////////////// // Return 0 if success, Return -1 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 cid = procid / NB_PROCS_MAX; unsigned int lpid = procid % NB_PROCS_MAX; unsigned int x = cid >> Y_WIDTH; unsigned int y = cid & ((1<> Y_WIDTH; unsigned int y = cid & ((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 cid = procid / NB_PROCS_MAX; unsigned int lpid = procid % NB_PROCS_MAX; unsigned int x = cid >> Y_WIDTH; unsigned int y = cid & ((1< 0 ) { #if GIET_DEBUG_FAT _printf("\n[FAT DEBUG] Processor[%d,%d,%d] makes an IOC read " " for cluster %x : buf = %x / lba = %x / sectors = %d\n", x, y, lpid, 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\n", lba ); return -1; } // update variables for next iteration cluster = get_next_cluster_id( 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 cid = procid / NB_PROCS_MAX; unsigned int lpid = procid % NB_PROCS_MAX; unsigned int x = cid >> Y_WIDTH; unsigned int y = cid & ((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(" - first cluster = %x\n" " - skiped clusters = %x\n", cluster, clusters_to_skip ); #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_id( 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] Processor[%d,%d,%d] makes an IOC write : " "buf = %x / lba = %x / sectors = %x\n", x, y, lpid, (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_id( 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; } ///////////////////////////////////////////////////////////////////////////////// // 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 nor 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("[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