uint32_t MFS_Write ( MFS_HANDLE_PTR handle, MFS_DRIVE_STRUCT_PTR drive_ptr, uint32_t num_bytes, /*[IN] number of bytes to be written */ char *buffer_address, /*[IN/OUT] bytes are written from this buffer */ _mfs_error_ptr error_ptr /*[IN/OUT] error code is written to this address */ ) { uint32_t bytes_written; uint32_t copy_size; uint32_t cluster_offset; uint32_t sector_number, sector_index; uint32_t sector_offset; uint32_t whole_sectors; uint32_t cont_sectors; uint32_t proc_sectors; _mfs_error error, temp_error; uint32_t file_size; uint32_t next_cluster; bool need_hwread; uint32_t location; uint32_t num_zeros; uint32_t zeros_written; uint32_t zero_size; #if MFSCFG_READ_ONLY_CHECK if (MFS_is_read_only (drive_ptr)) { MFS_set_error_and_return(error_ptr,MFS_DISK_IS_WRITE_PROTECTED,0); } #endif if ( buffer_address == NULL ) { MFS_set_error_and_return(error_ptr,MFS_INVALID_PARAMETER,0); } if ( num_bytes == 0 ) { MFS_set_error_and_return(error_ptr,MFS_NO_ERROR,0); } error = MFS_lock_dos_disk( drive_ptr ); if ( error != MFS_NO_ERROR ) { MFS_set_error_and_return(error_ptr,error,0); } if ( handle->ACCESS == MFS_ACCESS_READ_ONLY ) { MFS_unlock(drive_ptr,FALSE); MFS_set_error_and_return(error_ptr,MFS_ACCESS_DENIED,0); } /* ** Setup the current cluster. If this is the first time writing to the file, a cluster needs to be added. */ if ( handle->CURRENT_CLUSTER == 0 ) { handle->PREVIOUS_CLUSTER = 0; handle->CURRENT_CLUSTER = clustoh(handle->DIR_ENTRY.HFIRST_CLUSTER, handle->DIR_ENTRY.LFIRST_CLUSTER); if ( handle->CURRENT_CLUSTER==0 ) { next_cluster = MFS_Find_unused_cluster_from(drive_ptr,drive_ptr->NEXT_FREE_CLUSTER); if ( next_cluster != CLUSTER_INVALID ) { clustod(handle->DIR_ENTRY.HFIRST_CLUSTER, handle->DIR_ENTRY.LFIRST_CLUSTER, next_cluster); handle->TOUCHED = 1; error = MFS_Put_fat(drive_ptr, next_cluster, CLUSTER_EOF); if ( error == MFS_NO_ERROR ) { handle->CURRENT_CLUSTER = next_cluster; } else { MFS_unlock(drive_ptr,FALSE); MFS_set_error_and_return(error_ptr,error,0); } } else { MFS_unlock(drive_ptr,FALSE); MFS_set_error_and_return(error_ptr,MFS_DISK_FULL,0); } } } else if ( handle->CURRENT_CLUSTER == CLUSTER_EOF ) { error = MFS_Add_cluster_to_chain(drive_ptr, handle->PREVIOUS_CLUSTER, &handle->CURRENT_CLUSTER); if ( MFS_NO_ERROR != error ) { MFS_unlock(drive_ptr,FALSE); MFS_set_error_and_return(error_ptr,error,0); } } else if ( handle->CURRENT_CLUSTER > drive_ptr->LAST_CLUSTER ) { MFS_unlock(drive_ptr,FALSE); MFS_set_error_and_return(error_ptr,MFS_DISK_FULL,0); } /* Make sure location (local variable) never points behind the end of file */ file_size = mqx_dtohl(handle->DIR_ENTRY.FILE_SIZE); location = (handle->LOCATION > file_size) ? file_size : handle->LOCATION; /* Calculate sector number and offsets within cluster and sector */ cluster_offset = OFFSET_WITHIN_CLUSTER(location); sector_index = CLUSTER_OFFSET_TO_SECTOR(cluster_offset); sector_number = CLUSTER_TO_SECTOR(handle->CURRENT_CLUSTER) + sector_index; sector_offset = OFFSET_WITHIN_SECTOR(location); /* Calculate possible gap to fill in by zeros if writing behind the end of file */ num_zeros = handle->LOCATION - location; zeros_written = 0; bytes_written = 0; /* Write zeros to fill in gap if LOCATION points behind the end of file */ while (zeros_written < num_zeros) { /* If offset is non-zero, then reading the data is required */ error = MFS_Read_data_sector(drive_ptr, handle, sector_number, sector_offset != 0); if ( error != MFS_NO_ERROR ) break; /* Zero the buffer */ zero_size = min(num_zeros-zeros_written, drive_ptr->BPB.SECTOR_SIZE-sector_offset); _mem_zero(&drive_ptr->DATA_SECTOR_PTR[sector_offset], zero_size); drive_ptr->DATA_SECTOR_DIRTY = TRUE; if ( drive_ptr->WRITE_CACHE_POLICY == MFS_WRITE_THROUGH_CACHE ) { error = MFS_Flush_data_sector_buffer(drive_ptr); if ( error != MFS_NO_ERROR ) break; } zeros_written += zero_size; sector_offset += zero_size; /* ** Check to see if we need to advance to the next sector, which has ** the side effect of increasing the cluster number if required. */ if ( sector_offset >= drive_ptr->BPB.SECTOR_SIZE ) { temp_error = MFS_next_data_sector(drive_ptr, handle, §or_index, §or_number); if (temp_error == MFS_EOF) { /* Always allocate new cluster, there is something to be written for sure */ error = MFS_Add_cluster_to_chain(drive_ptr, handle->PREVIOUS_CLUSTER, &handle->CURRENT_CLUSTER); if ( MFS_NO_ERROR == error ) { sector_number = CLUSTER_TO_SECTOR(handle->CURRENT_CLUSTER); sector_index = 0; } } else { error = temp_error; } sector_offset = 0; } } /* Write partial sector if sector_offset is non-zero */ if ((sector_offset != 0) && (error == MFS_NO_ERROR)) { /* Offset is non-zero, reading the data is required */ error = MFS_Read_data_sector(drive_ptr, handle, sector_number, TRUE); if (error == MFS_NO_ERROR) { /* The requested lenght of data may span the sector to it's end */ copy_size = min(num_bytes, drive_ptr->BPB.SECTOR_SIZE-sector_offset); _mem_copy(buffer_address, &drive_ptr->DATA_SECTOR_PTR[sector_offset], copy_size); drive_ptr->DATA_SECTOR_DIRTY = TRUE; if (drive_ptr->WRITE_CACHE_POLICY == MFS_WRITE_THROUGH_CACHE) { error = MFS_Flush_data_sector_buffer(drive_ptr); } if (error == MFS_NO_ERROR) { bytes_written = copy_size; /* ** Check to see if we need to advance to the next sector, which has ** the side effect of increasing the cluster number if required. */ if ((sector_offset + bytes_written) >= drive_ptr->BPB.SECTOR_SIZE) { temp_error = MFS_next_data_sector(drive_ptr, handle, §or_index, §or_number); /* Only an error if we are not done writing and can't extend the chain */ if (bytes_written < num_bytes) { if (temp_error == MFS_EOF) { /* Allocate new cluster */ error = MFS_Add_cluster_to_chain(drive_ptr, handle->PREVIOUS_CLUSTER, &handle->CURRENT_CLUSTER); /* Update sector_number and index unconditionally - if there was an error the value is never used anyways. */ sector_number = CLUSTER_TO_SECTOR(handle->CURRENT_CLUSTER); sector_index = 0; } else { error = temp_error; } } } } } } /* Check whether the application buffer is properly aligned */ if ((((uint32_t)buffer_address+bytes_written) & drive_ptr->ALIGNMENT_MASK) == 0) { /* Yes, use zero copy approach */ whole_sectors = (num_bytes - bytes_written) >> drive_ptr->SECTOR_POWER; while ((whole_sectors > 0) && (error == MFS_NO_ERROR)) { cont_sectors = drive_ptr->BPB.SECTORS_PER_CLUSTER - sector_index; if (cont_sectors > whole_sectors) cont_sectors = whole_sectors; error = MFS_Write_device_sectors(drive_ptr, sector_number, cont_sectors, MFSCFG_MAX_WRITE_RETRIES, buffer_address+bytes_written, &proc_sectors); if (proc_sectors > 0) { bytes_written += proc_sectors * drive_ptr->BPB.SECTOR_SIZE; whole_sectors -= proc_sectors; /* Advance to next unprocessed sector */ sector_index += proc_sectors - 1; temp_error = MFS_next_data_sector(drive_ptr, handle, §or_index, §or_number); /* Go on only if we are not done writing yet */ if ((error == MFS_NO_ERROR) && (bytes_written < num_bytes)) { if (temp_error == MFS_EOF) { /* Allocate new cluster */ error = MFS_Add_cluster_to_chain(drive_ptr, handle->PREVIOUS_CLUSTER, &handle->CURRENT_CLUSTER); /* Update sector_number and index unconditionally - if there was an error the value is never used anyways. */ sector_number = CLUSTER_TO_SECTOR(handle->CURRENT_CLUSTER); sector_index = 0; } else { error = temp_error; } } } } }
MFS_DIR_ENTRY_PTR MFS_Create_directory_entry ( MFS_DRIVE_STRUCT_PTR drive_ptr, /*[IN] the drive on which to operate */ char_ptr filename_ptr, /*[IN] pointer to the file's name */ char attribute, /*[IN] attribute to be assigned to the file */ uint_32_ptr dir_cluster_ptr, /*[IN/OUT] indicates cluster in which the entry was put.*/ uint_32_ptr dir_index_ptr, /*[IN/OUT] index of the location of new file within the directory */ uint_32_ptr error_ptr /*[IN/OUT] error_return_address */ ) { MFS_DIR_ENTRY_PTR dir_entry_ptr, dir_entry_ptr1; MFS_DIR_ENTRY_PTR temp_entry_ptr; TIME_STRUCT time; DATE_STRUCT clk_time; uint_32 dir_index, saved_index, temp_index; uint_32 entry_cluster, saved_cluster, temp_cluster; uint_32 needed_entries, i; int_32 length; boolean found_all = FALSE; char temp_name[SFILENAME_SIZE+1], short_name[SFILENAME_SIZE+1]; uchar checksum = 0; uint_32 prev_cluster= CLUSTER_INVALID, saved_prev_cluster, temp_prev_cluster; dir_entry_ptr = NULL; entry_cluster = *dir_cluster_ptr; dir_index = 0; /* ** Check for duplicate file */ if ( filename_ptr == NULL ) { *error_ptr = MFS_INVALID_PARAMETER; } else if ( *filename_ptr == '\0' ) { *error_ptr = MFS_FILE_NOT_FOUND; } #if MFSCFG_READ_ONLY_CHECK else if (MFS_is_read_only (NULL, drive_ptr)) { *error_ptr = MFS_DISK_IS_WRITE_PROTECTED; } #endif else if ( (dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, filename_ptr, &entry_cluster, &dir_index, &prev_cluster, MFS_ATTR_ANY, error_ptr)) != NULL ) { *error_ptr = MFS_FILE_EXISTS; } else { /* ** Search for an empty slot */ dir_index = 0; dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); if ( MFS_Dirname_valid(filename_ptr) ) { found_all = TRUE; } if ( dir_entry_ptr == NULL && !*error_ptr ) { found_all = TRUE; } /* ** Save it now because we might not go into the while loop ** If we don't go into while, we lose original values when these ** variables are restored after the while loop */ saved_cluster = entry_cluster; saved_index = dir_index; while ( !found_all ) { /* Calculate the amount of extra entries needed for LFN's */ saved_cluster = entry_cluster; saved_index = dir_index; for ( needed_entries = (strlen(filename_ptr) + 12) / 13; needed_entries >= 1 && !found_all; needed_entries-- ) { *error_ptr = MFS_Increment_dir_index(drive_ptr, &entry_cluster, &dir_index, NULL); if ( entry_cluster == CLUSTER_EOF && !*error_ptr ) { /* This means the LFN will span a cluster. */ found_all = TRUE; break; } temp_index = dir_index; temp_cluster = entry_cluster; temp_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, MFS_ATTR_ANY,error_ptr); if ( *error_ptr ) { return NULL; } if ( !temp_entry_ptr ) { found_all = TRUE; dir_entry_ptr = NULL; break; } else if ( dir_index == temp_index && temp_cluster == entry_cluster ) { continue; } else { break; } } if ( needed_entries == 0 ) { found_all = TRUE; } if ( found_all ) { dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &saved_cluster, &saved_index, &saved_prev_cluster, MFS_ATTR_ANY,error_ptr); } } entry_cluster = saved_cluster; dir_index = saved_index; if ( dir_entry_ptr == NULL && !*error_ptr ) { /* ** Empty spot not found... get a new cluster and use the first entry. */ dir_index = 0; if ( entry_cluster == 0 ) { *error_ptr = MFS_ROOT_DIR_FULL; return(NULL); } else { *error_ptr = MFS_Add_cluster_to_chain(drive_ptr, entry_cluster, &entry_cluster); if ( *error_ptr == MFS_NO_ERROR ) { *error_ptr = MFS_Clear_cluster(drive_ptr, entry_cluster); if ( *error_ptr ) { return(NULL); } dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR; *error_ptr = MFS_Write_back_fat(drive_ptr); if ( *error_ptr ) { return(NULL); } } } } } if ( *error_ptr == MFS_NO_ERROR ) { /* At this point we have one of the following cases ** ** 1. We have a normal 8.3 filename and an empty slot for it ** 2. We have a LFN, and a slot which has enough consecutive free slots ** to store the LFN, followed by the actual entry */ /* If the file is a normal 8.3 file */ if ( MFS_Dirname_valid(filename_ptr) ) { _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY)); htodc(dir_entry_ptr->ATTRIBUTE, attribute); MFS_Expand_dotfile(filename_ptr, dir_entry_ptr->NAME); _time_get(&time); _time_to_date(&time, &clk_time); NORMALIZE_DATE(&clk_time); htods(dir_entry_ptr->TIME, PACK_TIME(clk_time)); htods(dir_entry_ptr->DATE, PACK_DATE(clk_time)); drive_ptr->DIR_SECTOR_DIRTY = TRUE; } else { /* Case where the file is a LFN */ length = strlen(filename_ptr); /* Get the 8.3 name and calculate the checksum */ temp_index = 0; temp_cluster = *dir_cluster_ptr; *error_ptr = MFS_lfn_to_sfn(filename_ptr, temp_name); if ( !*error_ptr ) { do { dir_entry_ptr1 = MFS_Find_directory_entry(drive_ptr, temp_name, &temp_cluster, &temp_index, &temp_prev_cluster, MFS_ATTR_ANY, error_ptr); if ( !*error_ptr && dir_entry_ptr1 != NULL ) { MFS_increment_lfn(temp_name); temp_index = 0; } } while ( !*error_ptr && dir_entry_ptr1 != NULL ); dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); /* ** The memory should be cleared after finding the directory entry. */ _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY)); /* Setup the final slot */ ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ID |= MFS_LFN_END; drive_ptr->DIR_SECTOR_DIRTY = TRUE; MFS_Expand_dotfile(temp_name,short_name); checksum = MFS_lfn_checksum(short_name); } while ( length && !*error_ptr ) { i = length - ((length -1) % 13 + 1); *error_ptr = MFS_lfn_name_to_entry(filename_ptr + i, (MFS_LNAME_ENTRY_PTR) dir_entry_ptr); /* Set the entry number, the checksum value and the attribute */ ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ID |= (length + 12) / 13; ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ALIAS_CHECKSUM= checksum; ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ATTR = MFS_ATTR_LFN; drive_ptr->DIR_SECTOR_DIRTY = TRUE; length -= (length - i); if ( length < 0 ) { length = 0; } if ( length && !*error_ptr ) { dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); if ( dir_entry_ptr == NULL && !*error_ptr ) { /* No empty spots... We need to get a new cluster */ dir_index = 0; if ( entry_cluster == 0 ) { *error_ptr = MFS_ROOT_DIR_FULL; return(NULL); } else { *error_ptr = MFS_Add_cluster_to_chain( drive_ptr, entry_cluster, &entry_cluster); if ( *error_ptr == MFS_NO_ERROR ) { *error_ptr = MFS_Clear_cluster(drive_ptr, entry_cluster); if ( *error_ptr ) { return(NULL); } dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR; *error_ptr = MFS_Write_back_fat(drive_ptr); if ( *error_ptr ) { return(NULL); } } } } _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY)); drive_ptr->DIR_SECTOR_DIRTY = TRUE; } } /* LFN is written, so write the actual entry */ if ( !*error_ptr ) { dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); if ( dir_entry_ptr == NULL && !*error_ptr ) { dir_index = 0; if ( entry_cluster == 0 ) { *error_ptr = MFS_ROOT_DIR_FULL; return(NULL); } else { *error_ptr = MFS_Add_cluster_to_chain( drive_ptr, entry_cluster, &entry_cluster); if ( *error_ptr == MFS_NO_ERROR ) { *error_ptr = MFS_Clear_cluster(drive_ptr, entry_cluster); if ( *error_ptr ) { return(NULL); } dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR; *error_ptr = MFS_Write_back_fat(drive_ptr); if ( *error_ptr ) { return(NULL); } } } } _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY)); htodc(dir_entry_ptr->ATTRIBUTE, attribute); MFS_Expand_dotfile(temp_name, dir_entry_ptr->NAME); _time_get(&time); _time_to_date(&time, &clk_time); NORMALIZE_DATE(&clk_time); htods(dir_entry_ptr->TIME, PACK_TIME(clk_time)); htods(dir_entry_ptr->DATE, PACK_DATE(clk_time)); drive_ptr->DIR_SECTOR_DIRTY = TRUE; } } if ( *error_ptr ) { return(NULL); } } *dir_cluster_ptr = entry_cluster; *dir_index_ptr = dir_index; return(dir_entry_ptr); }