void *MFS_Create_file ( MFS_DRIVE_STRUCT_PTR drive_ptr, unsigned char attr, /*[IN] attribute for created file*/ char *pathname, /*[IN] directory and filename of the file to be created */ _mfs_error_ptr error_ptr /*[IN/OUT] error code is written to this address */ ) { MFS_HANDLE_PTR handle; MFS_HANDLE_PTR next_handle; MFS_DIR_ENTRY_PTR dir_entry_ptr; TIME_STRUCT time; DATE_STRUCT clk_time; uint32_t dir_cluster; uint32_t dir_index; char access; _mfs_error error_code, saved_code = 0; if ( (pathname == NULL) || (*pathname == '\0') ) { *error_ptr = MFS_INVALID_PARAMETER; return( NULL ); } #if MFSCFG_READ_ONLY_CHECK if (MFS_is_read_only (drive_ptr)) { *error_ptr = MFS_DISK_IS_WRITE_PROTECTED; return NULL; } #endif error_code = MFS_lock_dos_disk( drive_ptr ); if ( error_code != MFS_NO_ERROR ) { *error_ptr = error_code; return( NULL ); } handle = NULL; attr &= (MFS_ATTR_READ_ONLY | MFS_ATTR_HIDDEN_FILE | MFS_ATTR_SYSTEM_FILE | MFS_ATTR_ARCHIVE | MFS_ATTR_VOLUME_NAME); attr |= MFS_ATTR_ARCHIVE; access = (attr & MFS_ATTR_READ_ONLY) ? MFS_ACCESS_READ_ONLY : MFS_ACCESS_READ_WRITE; dir_entry_ptr = MFS_Create_entry_slave(drive_ptr, attr, pathname, &dir_cluster, &dir_index, &error_code, TRUE); if ( (dir_entry_ptr != NULL) && !(attr & MFS_ATTR_VOLUME_NAME) ) { handle = MFS_Get_handle(drive_ptr,dir_entry_ptr); if ( handle != NULL ) { handle->DIR_CLUSTER = dir_cluster; handle->DIR_INDEX = dir_index; handle->ACCESS = access; handle->CURRENT_CLUSTER = 0; handle->PREVIOUS_CLUSTER = 0; /* ** If file exists, overwrite and set size to 0 */ if ( error_code == MFS_FILE_EXISTS ) { _time_get(&time); _time_to_date(&time, &clk_time); NORMALIZE_DATE(&clk_time); saved_code = MFS_Release_chain(drive_ptr, clustoh(handle->DIR_ENTRY.HFIRST_CLUSTER, handle->DIR_ENTRY.LFIRST_CLUSTER)); if ( saved_code == MFS_NO_ERROR || saved_code == MFS_LOST_CHAIN ) { clustod(handle->DIR_ENTRY.HFIRST_CLUSTER, handle->DIR_ENTRY.LFIRST_CLUSTER, 0); mqx_htodl(handle->DIR_ENTRY.FILE_SIZE, 0L); mqx_htodc(handle->DIR_ENTRY.ATTRIBUTE, attr); mqx_htods(handle->DIR_ENTRY.TIME, PACK_TIME(clk_time)); mqx_htods(handle->DIR_ENTRY.DATE, PACK_DATE(clk_time)); error_code = MFS_Update_entry(drive_ptr, handle); /* ** If the same file is already open, mark it as 'freshly ** truncated' so reads and writes don't clobber any data. */ if ( error_code == MFS_NO_ERROR ) { next_handle = (MFS_HANDLE_PTR) _queue_head(&drive_ptr->HANDLE_LIST); while ( next_handle ) { if ( next_handle->DIR_CLUSTER == dir_cluster && next_handle->DIR_INDEX == dir_index ) { next_handle->CURRENT_CLUSTER = 0; next_handle->PREVIOUS_CLUSTER = 0; } next_handle = (MFS_HANDLE_PTR) _queue_next(&drive_ptr->HANDLE_LIST, (QUEUE_ELEMENT_STRUCT_PTR) next_handle); } } } } /* ** No need to update the disk image if we didn't change anything. */ if ( (mqx_dtohc(handle->DIR_ENTRY.ATTRIBUTE) != attr) && (error_code == MFS_NO_ERROR) ) { mqx_htodc(handle->DIR_ENTRY.ATTRIBUTE, attr); error_code = MFS_Update_entry(drive_ptr, handle); } } else { error_code = MFS_INSUFFICIENT_MEMORY; } } MFS_unlock(drive_ptr,FALSE); if ( error_code == MFS_NO_ERROR && saved_code == MFS_LOST_CHAIN ) { *error_ptr = saved_code; } else { *error_ptr = error_code; } return((void *)handle); }
_mfs_error MFS_Create_subdir ( MFS_DRIVE_STRUCT_PTR drive_ptr, char *pathname /*[IN] pathname of the directory to be created */ ) { MFS_DIR_ENTRY_PTR dir_entry_ptr; uint32_t dir_cluster; uint32_t parent_cluster; uint32_t free_cluster; uint32_t dir_index; _mfs_error error_code; char *temp_dirname; char *temp_filename; if ( (pathname == NULL) || (*pathname == '\0') ) { return MFS_INVALID_PARAMETER; } #if MFSCFG_READ_ONLY_CHECK if (MFS_is_read_only (drive_ptr)) { return MFS_DISK_IS_WRITE_PROTECTED; } #endif error_code = MFS_alloc_2paths(&temp_dirname,&temp_filename); if ( error_code != MFS_NO_ERROR ) { return( error_code ); } error_code = MFS_lock_dos_disk( drive_ptr ); if ( error_code != MFS_NO_ERROR ) { MFS_free_path(temp_dirname); MFS_free_path(temp_filename); return error_code; } MFS_Parse_pathname (temp_dirname, temp_filename, pathname); dir_cluster = drive_ptr->CUR_DIR_CLUSTER; parent_cluster = MFS_Find_directory (drive_ptr, temp_dirname, dir_cluster); dir_cluster = parent_cluster; if ( MFS_is_valid_lfn(temp_filename) ) { if ( dir_cluster != CLUSTER_INVALID ) { /* ** We'll obtain a cluster for the new directory first. If we ** cannot create the directory afterwards, it is easier to re-free ** the cluster than to remove the new entry. */ free_cluster = MFS_Find_unused_cluster_from(drive_ptr, drive_ptr->NEXT_FREE_CLUSTER); if ( free_cluster != CLUSTER_INVALID ) { error_code = MFS_Clear_cluster(drive_ptr, free_cluster); if ( error_code ) { MFS_unlock(drive_ptr,TRUE); MFS_free_path(temp_dirname); MFS_free_path(temp_filename); return( error_code ); } error_code = MFS_Put_fat(drive_ptr, free_cluster, CLUSTER_EOF); dir_entry_ptr = MFS_Create_directory_entry(drive_ptr, temp_filename, MFS_ATTR_DIR_NAME, &dir_cluster, &dir_index, &error_code); if ( error_code == MFS_NO_ERROR ) { clustod(dir_entry_ptr->HFIRST_CLUSTER, dir_entry_ptr->LFIRST_CLUSTER, free_cluster); drive_ptr->DIR_SECTOR_DIRTY = TRUE; /* ** We shall now create the "." and ".." entries. */ dir_cluster = free_cluster; dir_entry_ptr = MFS_Create_directory_entry(drive_ptr,".", MFS_ATTR_DIR_NAME, &dir_cluster, &dir_index, &error_code); if ( error_code == MFS_NO_ERROR ) { clustod(dir_entry_ptr->HFIRST_CLUSTER, dir_entry_ptr->LFIRST_CLUSTER, free_cluster); drive_ptr->DIR_SECTOR_DIRTY = TRUE; dir_entry_ptr = MFS_Create_directory_entry(drive_ptr,"..", MFS_ATTR_DIR_NAME, &dir_cluster, &dir_index, &error_code); if ( error_code == MFS_NO_ERROR ) { if ( drive_ptr->FAT_TYPE == MFS_FAT32 ) { if ( drive_ptr->BPB32.ROOT_CLUSTER == parent_cluster ) { /* ** Even though the FAT32 root sector can be ** anywhere, it is identified as 0 when referenced ** through a directory entry */ parent_cluster = 0; } } clustod(dir_entry_ptr->HFIRST_CLUSTER, dir_entry_ptr->LFIRST_CLUSTER, parent_cluster); drive_ptr->DIR_SECTOR_DIRTY = TRUE; } } } else { MFS_Put_fat(drive_ptr, free_cluster, CLUSTER_UNUSED); } } else { error_code = MFS_DISK_FULL; } } else { error_code = MFS_PATH_NOT_FOUND; } } else if ( MFS_lfn_dirname_valid(temp_filename) ) { if ( dir_cluster ) { error_code = MFS_FILE_EXISTS; } else { error_code = MFS_CANNOT_CREATE_DIRECTORY; } } else { error_code = MFS_INVALID_PARAMETER; } MFS_free_path(temp_dirname); MFS_free_path(temp_filename); MFS_unlock(drive_ptr,TRUE); return(error_code); }
/*FUNCTION*------------------------------------------------------------------- * * Function Name : MFS_Write_device_sector * Returned Value : error_code * Comments : * Reads or writes consecutive clusters. *END*---------------------------------------------------------------------*/ _mfs_error MFS_Write_device_sector ( MFS_DRIVE_STRUCT_PTR drive_ptr, uint_32 sector_number, /*[IN] sector number to read/write from/to file system medium */ char_ptr buffer_ptr /*[IN/OUT] address of where data is to be stored/written */ ) { uint_32 new_cluster_number, next_cluster, bad_cluster_number; uint_32 i; uint_32 retries, attempts; uint_32 new_sector_number, no_of_sectors; char_ptr temp_buffer_ptr; int_32 num, expect_num, shifter, seek_loc, tmp; _mfs_error error; #if MFSCFG_READ_ONLY_CHECK if (MFS_is_read_only (NULL, drive_ptr)) { return MFS_DISK_IS_WRITE_PROTECTED; } #endif error = MFS_NO_ERROR; MFS_LOG(printf("MFS_Write_device_sector %d\n", sector_number)); if ( sector_number > drive_ptr->BPB.MEGA_SECTORS ) { return(MFS_SECTOR_NOT_FOUND); } attempts = 0; /* Lock device */ ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_LOCK, &drive_ptr->DRV_NUM); if ( drive_ptr->BLOCK_MODE ) { shifter = 0; seek_loc = sector_number; expect_num = 1; } else { shifter = drive_ptr->SECTOR_POWER; seek_loc = sector_number << shifter; expect_num = 1 << shifter; } MFS_device_write_internal(drive_ptr, seek_loc, buffer_ptr, &expect_num, &buffer_ptr, shifter); if ( expect_num > 0 ) { error = drive_ptr->DEV_FILE_PTR->ERROR; #if MFSCFG_MAX_CLUSTER_REMAP_ATTEMPTS /* ** Check to see if the write failed due to a bad sector. If so, ** rewrite cluster by cluster until we find bad cluster, then move ** it. */ temp_buffer_ptr = buffer_ptr; if ( drive_ptr->BLOCK_MODE ) { expect_num = drive_ptr->BPB.SECTORS_PER_CLUSTER; } else { expect_num = drive_ptr->CLUSTER_SIZE_BYTES; } tmp = expect_num; for ( i = 0; i < no_of_clusters; i++ ) { MFS_device_write_internal(drive_ptr, seek_loc, temp_buffer_ptr, &expect_num, &buffer_ptr, shifter); if ( expect_num > 0 ) { error_code = drive_ptr->DEV_FILE_PTR->ERROR; ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_UNLOCK, &drive_ptr->DRV_NUM); bad_cluster_number = cluster_number + i; error_code = MFS_get_cluster_from_fat(drive_ptr, bad_cluster_number, &next_cluster); if ( error_code != MFS_NO_ERROR ) { /* ** This is done so that the unlock at end of function ** works properly */ ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_LOCK, &drive_ptr->DRV_NUM); break; } retries = MFSCFG_MAX_CLUSTER_REMAP_ATTEMPTS; while ( (error_code != MFS_NO_ERROR) && retries-- ) { /* ** Mark old cluster BAD */ error_code = MFS_Put_fat(drive_ptr, bad_cluster_number, CLUSTER_BAD); if ( error_code ) { break; } /* ** Find available fat, update old FAT as invalid, ** update new fat, and write new cluster. */ new_cluster_number = MFS_Find_unused_cluster_from(drive_ptr, bad_cluster_number); if ( new_cluster_number == CLUSTER_INVALID ) { /* ** No more clusters */ return MFS_DISK_FULL; } new_sector_number = drive_ptr->DATA_START_SECTOR + ((uint_32)(new_cluster_number - CLUSTER_MIN_GOOD)) * drive_ptr->BPB.SECTORS_PER_CLUSTER; ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_LOCK, &drive_ptr->DRV_NUM); expect_num = tmp; MFS_device_write_internal(drive_ptr, new_sector_number << shifter, temp_buffer_ptr, &expect_num, &buffer_ptr, shifter); if ( expect_num > 0 ) { error_code = drive_ptr->DEV_FILE_PTR->ERROR; } ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_UNLOCK, &(drive_ptr->DRV_NUM)); bad_cluster_number = new_cluster_number; } if ( error_code != MFS_NO_ERROR ) { return MFS_WRITE_FAULT; } /* ** update chain, */ error_code = MFS_Put_fat(drive_ptr, new_cluster_number, next_cluster); if ( error_code ) { return(error_code); } if ( handle->PREVIOUS_CLUSTER ) { error_code = MFS_Put_fat(drive_ptr,handle->PREVIOUS_CLUSTER, new_cluster_number ); if ( error_code ) { return(error_code); } } else { clustod(handle->DIR_ENTRY.HFIRST_CLUSTER, handle->DIR_ENTRY.LFIRST_CLUSTER, new_cluster_number); } handle->PREVIOUS_CLUSTER = new_cluster_number; ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_LOCK, &drive_ptr->DRV_NUM); } else { handle->PREVIOUS_CLUSTER = cluster_number+i; } temp_buffer_ptr += drive_ptr->CLUSTER_SIZE_BYTES; sector_number += drive_ptr->BPB.SECTORS_PER_CLUSTER; } #endif } /* Unlock device under MFS */ ioctl(drive_ptr->DEV_FILE_PTR, IO_IOCTL_DEV_UNLOCK, &drive_ptr->DRV_NUM); switch ( error ) { case IO_ERROR_WRITE_PROTECTED: error = MFS_DISK_IS_WRITE_PROTECTED; break; case IO_ERROR_WRITE: error = MFS_WRITE_FAULT; break; case IO_ERROR_WRITE_ACCESS: error = MFS_SECTOR_NOT_FOUND; break; case IO_ERROR_READ: error = MFS_READ_FAULT; break; case IO_ERROR_READ_ACCESS: error = MFS_SECTOR_NOT_FOUND; break; default: break; } return(error); }
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; } } } } }