////////////////////////////////////////////////////////////////////////////////// // 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 partition 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 three modes: BOOT/KERNEL/USER // defining three 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 ////////////////////////////////////////////////////////////////////////////////// #if GIET_DEBUG_FAT void display_fat_cache() { unsigned int line; unsigned int word; unsigned int temp[9]; temp[8] = 0; _tty_get_lock( 0 ); _puts("\n*********************** fat_cache_lba = "); _putx( fat.cache_lba ); _puts(" **************************\n"); for ( line=0 ; line<16 ; line++ ) { // display address _putx( (fat.cache_lba<<9) + (line<<5) ); _puts(" : "); // 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]); _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"); _tty_release_lock( 0 ); } // end display_fat_cache() #endif ////////////////////////////////////////////////////////////////////////////////// // 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; } ////////////////////////////////////////////////////////////////////////////// // 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 curent 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.partition_lba + 32 + (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( mode, // mode for IOC driver lba, // sector index fat.fat_cache, // fat cache 1 ) ) // one sector { _tty_get_lock( 0 ); _puts("[FAT_ERROR] in get_next cluster_id() cannot read block "); _putd( lba ); _puts("\n"); _tty_release_lock( 0 ); return 1; } fat.cache_lba = lba; return read_entry( ((cluster % 128) * 4), 4, fat.fat_cache, 1 ); } } /////////////////////////////////////////////////////////////////////////////// // 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; } ////////////////////////////////////////////////////// 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 GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] filename "); _puts( string ); _tty_release_lock( 0 ); #endif if(string[0] == '.' && string[1] == '\0') { sfn_string[0] = '.'; #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts(" converted to 8.3 SFN format : "); _puts( sfn_string ); _puts("\n"); _tty_release_lock( 0 ); #endif return 1; } if(string[0] == '.' && string[1] == '.' && string[2] == '\0') { sfn_string[0] = '.'; sfn_string[1] = '.'; #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts(" converted to 8.3 SFN format : "); _puts( string ); _puts("\n"); _tty_release_lock( 0 ); #endif 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++; } #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts(" converted to 8.3 SFN format : "); _puts( sfn_string ); _puts("\n"); _tty_release_lock( 0 ); #endif 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 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 { #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] enter _scan_directory() searching dir/file : "); _puts( file_name ); _puts("\n"); _tty_release_lock( 0 ); #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( mode, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _tty_get_lock( 0 ); _puts("[FAT ERROR] in scan directory() cannot read sector "); _putd( lba ); _puts("\n"); _tty_release_lock( 0 ); return -1; } fat.cache_lba = lba; #if GIET_DEBUG_FAT 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( mode, // mode for IOC driver lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _tty_get_lock( 0 ); _puts("[FAT ERROR] in scan directory() cannot read sector "); _putd( lba ); _puts("\n"); _tty_release_lock( 0 ); 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 ); 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 ); #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] FILE FOUND\n"); _tty_release_lock( 0 ); #endif return read_cluster( dir_entry ); } } return -1; } // end scan_directory() /////////////////////////////////////////////////////////////////////// // 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; } ////////////////////////////////////////////////////////////////////// // 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 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] _fat_create() not implemented\n"); _tty_release_lock( 0 ); 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 _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Enter _fat_init() / fat_cache_base = "); _putx( (unsigned int)fat.fat_cache ); _puts("\n"); _tty_release_lock( 0 ); #endif // load Master Boot Record (sector 0) into fat cache if ( _ioc_read( mode, // mode for IOC driver 0, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_init() cannot load Boot Sector\n"); _tty_release_lock( 0 ); return -1; } fat.cache_lba = 0; // checking Boot sector integrity if( MBR_SIGNATURE_VALUE != read_entry( MBR_SIGNATURE_POSITION, fat.fat_cache, 1)) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] Boot sector not recognized or corrupt \n"); _tty_release_lock( 0 ); return -1; } #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Boot Sector Loaded\n"); _tty_release_lock( 0 ); #endif // initialise fat descriptor from Boot sector fat.partition_lba = read_entry( FIRST_PARTITION_BEGIN_LBA, fat.fat_cache, 1 ); fat.partition_sectors = read_entry( FIRST_PARTITION_SIZE, fat.fat_cache, 1 ); // load Partition Boot Record (first partition sector) into fat cache if ( _ioc_read( mode, // mode for IOC driver fat.partition_lba, // sector index fat.fat_cache, // buffer address 1 ) ) // one sector { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_init() cannot load block "); _putd( fat.partition_lba ); _puts("\n"); _tty_release_lock( 0 ); return -1; } fat.cache_lba = fat.partition_lba; #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Partition First Sector Loaded\n"); _tty_release_lock( 0 ); #endif // checking various FAT32 assuptions from boot sector if( read_entry( BPB_BYTSPERSEC, fat.fat_cache, 1 ) != 512 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] The sector size must be 512 bytes\n"); _tty_release_lock( 0 ); return -1; } if( read_entry( BPB_RSVDSECCNT, fat.fat_cache, 1 ) != 32 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] The RSVD region in FAT32 must be 32 sectors\n"); _tty_release_lock( 0 ); return -1; } if( read_entry( BPB_NUMFATS, fat.fat_cache, 1 ) != 1 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] The number of FAT copies in FAT region must be 1\n"); _tty_release_lock( 0 ); return -1; } if( (read_entry( BPB_FAT32_FATSZ32, fat.fat_cache, 1 ) & 0xF) != 0 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] The FAT region in FAT32 must be multiple of 32 sectors\n"); _tty_release_lock( 0 ); return -1; } if( read_entry( BPB_FAT32_ROOTCLUS, fat.fat_cache, 1 ) != 2 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] The first cluster index must be 2\n"); _tty_release_lock( 0 ); return -1; } // initialise fat descriptor from partition first sector 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.data_lba = 32 + fat.fat_sectors + fat.partition_lba; fat.initialised = FAT_INITIALISED; // initialise file descriptor array for( n = 0 ; n < GIET_OPEN_FILES_MAX ; n++ ) fat.fd[n].used = 0; #if (GIET_DEBUG_FAT == 1) _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Exit _fat_init()\n"); _tty_release_lock( 0 ); #endif return 0; } // end _fat_init() ///////////////// void _fat_print() { _puts("\n################################ FAT32 ###############################"); _puts("\nFAT initialised "); _putx( fat.initialised ); _puts("\nSector Size (bytes) "); _putx( fat.sector_size ); _puts("\nSectors per cluster "); _putx( fat.sectors_per_cluster ); _puts("\nPartition size (sectors) "); _putx( fat.partition_sectors ); _puts("\nPartition first lba "); _putx( fat.partition_lba ); _puts("\nData region first lba "); _putx( fat.data_lba ); _puts("\nNumber of sectors for one FAT "); _putx( fat.fat_sectors ); _puts("\n######################################################################\n"); } /////////////////////////////////////////////////////////////////////////////// // This function checks that the kernel FAT structure has been initialised, // and makes the FAT initialisation if it is the first user open request. // This function 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. /////////////////////////////////////////////////////////////////////////////// // 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; // number of bytes unsigned int last_name; // directory containing file name is reached #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] enter _fat_open() for file "); _puts( pathname ); _puts("\n"); _tty_release_lock( 0 ); #endif // check FAT initialised if( fat.initialised != FAT_INITIALISED ) { _fat_init( IOC_BOOT_VA_MODE ); } // Scan the directories, starting from the root directory (cluster 2) // - The get_name_from_path() function extracts (successively) // each directory name from the pathname, and store it in name[] buffer // - The scan_directory() function scan one (or several) cluster(s) containing // a directory looking for name[], and return the cluster index // corresponding to the directory/file found. nb_read = 0; cluster = 2; last_name = 0; while ( get_name_from_path( pathname, name, &nb_read) ) { #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] _fat_open : search dir/file : "); _puts( name ); _puts("\n"); _tty_release_lock( 0 ); #endif // test if we reach the last name (file name) if( pathname[nb_read] == 0 ) { last_name = 1; dir_cluster = cluster; } // scan current directory cluster = scan_directory( mode, cluster, name, &file_size ); if( cluster == END_OF_CHAIN_CLUSTER && last_name && creat ) { cluster = fat_create( name, 1, dir_cluster ); } else if ( cluster == END_OF_CHAIN_CLUSTER ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_open() for file "); _puts( pathname ); _puts(" : cannot found name "); _puts( name ); _puts("\n"); _tty_release_lock( 0 ); return -1; } } #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] File "); _puts( pathname ); _puts(" found\n"); _tty_release_lock( 0 ); #endif // check the next value for cluster index found unsigned next = get_next_cluster_id( mode, cluster ); if ( (next != BAD_CLUSTER) && (next != FREE_CLUSTER) ) { // Search an empty slot scanning open file descriptors array fd_id = 0; while ( fat.fd[fd_id].used != 0 && fd_id < GIET_OPEN_FILES_MAX ) { fd_id++; } // set file descriptor if found empty slot if ( fd_id < GIET_OPEN_FILES_MAX ) { fat.fd[fd_id].used = 1; fat.fd[fd_id].first_cluster = cluster; fat.fd[fd_id].file_size = file_size; _strcpy( fat.fd[fd_id].name, pathname ); #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] file "); _puts( pathname ); _puts(" open with fd_id = "); _putd( fd_id ); _puts("\n"); _tty_release_lock( 0 ); #endif return fd_id; } else { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_open() for file "); _puts( pathname ); _puts(" : file descriptor array full\n "); _tty_release_lock( 0 ); return -1; } } else { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_open() for file "); _puts( pathname ); _puts(" : file found, but bad cluster\n"); _tty_release_lock( 0 ); return -1; } } // end _fat_open() /////////////////////////////////////////////////////////////////////////////// // For an open file, identified by the file descriptor index, transfer // an integer number of sectors from block device to a memory buffer. // If the number of requested sectors exceeds the file size, it is reduced. /////////////////////////////////////////////////////////////////////////////// // Returns number of sectors transfered if success, < 0 if error. /////////////////////////////////////////////////////////////////////////////// int _fat_read( 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 read unsigned int offset ) // nuber of sectors to skip in file { #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Enter _fat_read() for file "); _puts( fat.fd[fd_id].name ); _puts("\n - buffer base = "); _putx( (unsigned int)buffer ); _puts("\n - skipped sectors = "); _putd( offset ); _puts("\n - read sectors = "); _putd( count ); _tty_release_lock( 0 ); #endif 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 total_sectors; // actual number of sectors to be transfered 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 // 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); // arguments checking if ( fd_id >= GIET_OPEN_FILES_MAX ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_read() : illegal file descriptor index\n"); _tty_release_lock( 0 ); return -1; } if ( fat.fd[fd_id].used != 1 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_read() : file not open\n"); _tty_release_lock( 0 ); return -1; } if ( ((unsigned int)buffer & 0x1FF) != 0 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_read() : memory buffer not sector aligned\n"); _tty_release_lock( 0 ); return -1; } if ( offset >= file_sectors ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] offset larger than number of sectors in file\n"); _puts(" - offset = "); _putd( offset ); _puts(" - file_sectors = "); _putd( file_sectors ); _tty_release_lock( 0 ); 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 _tty_get_lock( 0 ); _puts("\n - first cluster = "); _putd( cluster ); _puts("\n - skiped clusters = "); _putd( clusters_to_skip ); _puts("\n"); _tty_release_lock( 0 ); #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* dst; // pointer on target buffer // initialize these variables for the first iteration todo_sectors = total_sectors; dst = (char*)buffer; lba = cluster_to_lba(cluster) + sectors_to_skip; if( total_sectors < (spc - sectors_to_skip) ) iter_sectors = total_sectors; else iter_sectors = spc - sectors_to_skip; // loop on the clusters while ( todo_sectors > 0 ) { #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] _fat_read() IOC request : buf = "); _putx( (unsigned int)dst ); _puts(" / lba = "); _putd( lba ); _puts(" / sectors = "); _putd( iter_sectors ); _puts("\n"); _tty_release_lock( 0 ); #endif if( _ioc_read( mode, // mode for IOC driver lba, // first sector index dst, // buffer address iter_sectors ) ) // number of sectors { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_read() cannot load block "); _putd( lba ); _puts("\n"); _tty_release_lock( 0 ); 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 // - 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 // 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); allocate = ( ((count + offset) / spc) > (file_sectors / spc) ); #if GIET_DEBUG_FAT _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] Enter _fat_write() for file "); _puts( fat.fd[fd_id].name ); _puts("\n - buffer base = "); _putx( (unsigned int)buffer ); _puts("\n - skipped sectors = "); _putd( offset ); _puts("\n - write sectors = "); _putd( count ); _puts("\n - file size (sectors) = "); _putd( file_sectors ); _puts("\n - need allocate = "); allocate ? _puts( "True" ) : _puts( "False"); _tty_release_lock( 0 ); #endif if ( allocate ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_write() : \n"); _puts("we need to allocate more cluster... But this function is not implemented\n"); _tty_release_lock( 0 ); return -1; } // arguments checking if ( fd_id >= GIET_OPEN_FILES_MAX ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_write() : illegal file descriptor index\n"); _tty_release_lock( 0 ); return -1; } if ( fat.fd[fd_id].used != 1 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_write() : file not open\n"); _tty_release_lock( 0 ); return -1; } if ( ((unsigned int)buffer & 0x1FF) != 0 ) { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_write() : memory buffer not sector aligned\n"); _tty_release_lock( 0 ); 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 _tty_get_lock( 0 ); _puts("\n - first cluster = "); _putd( cluster ); _puts("\n - skiped clusters = "); _putd( clusters_to_skip ); _puts("\n"); _tty_release_lock( 0 ); #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 _tty_get_lock( 0 ); _puts("\n[FAT DEBUG] _fat_write() IOC request : buf = "); _putx( (unsigned int)src ); _puts(" / lba = "); _putd( lba ); _puts(" / sectors = "); _putd( iter_sectors ); _puts("\n"); _tty_release_lock( 0 ); #endif if( _ioc_write( mode, // mode for IOC driver lba, // first sector index src, // buffer address iter_sectors ) ) // number of sectors { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_write() cannot write block "); _putd( lba ); _puts("\n"); _tty_release_lock( 0 ); 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; } // 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 { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_fstat() : illegal file descriptor index\n"); _tty_release_lock( 0 ); 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 { _tty_get_lock( 0 ); _puts("\n[FAT ERROR] in _fat_close() : illegal file descriptor index\n"); _tty_release_lock( 0 ); 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 KERNEL_MODE, because pathname, // we need to write into FAT cache 0 ); } ///////////////////////////////////////////////////////////////////////////////////// // 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 ) { _tty_get_lock( 0 ); _puts("[GIET ERROR] _fat_user_lseek function not implemented\n"); _tty_release_lock( 0 ); _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