/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** FileX Component */ /** */ /** File */ /** */ /**************************************************************************/ /**************************************************************************/ #define FX_SOURCE_CODE /* Include necessary system files. */ #include "fx_api.h" #include "fx_system.h" #include "fx_file.h" #include "fx_utility.h" #include "fx_directory.h" #ifdef FX_ENABLE_FAULT_TOLERANT #include "fx_fault_tolerant.h" #endif /* FX_ENABLE_FAULT_TOLERANT */ /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _fx_file_extended_allocate PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function attempts to allocate the number of consecutive */ /* clusters required to satisfy the user's request. If there are */ /* enough clusters, the clusters are allocated and linked to the file. */ /* Otherwise, if there are not enough consecutive clusters, an error */ /* code is returned to the caller. */ /* */ /* INPUT */ /* */ /* file_ptr File control block pointer */ /* size Number of bytes to allocate */ /* */ /* OUTPUT */ /* */ /* return status */ /* */ /* CALLS */ /* */ /* _fx_directory_entry_write Update directory entry */ /* _fx_utility_FAT_entry_read Read a FAT entry */ /* _fx_utility_FAT_entry_write Write a FAT entry */ /* _fx_utility_FAT_flush Flush written FAT entries */ /* _fx_utility_logical_sector_flush Flush the written log sector */ /* _fx_fault_tolerant_transaction_start Start fault tolerant */ /* transaction */ /* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ /* _fx_fault_tolerant_recover Recover FAT chain */ /* _fx_fault_tolerant_reset_log_file Reset the log file */ /* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ /* */ /* CALLED BY */ /* */ /* Application Code */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 05-19-2020 William E. Lamie Initial Version 6.0 */ /* 09-30-2020 William E. Lamie Modified comment(s), */ /* resulting in version 6.1 */ /* */ /**************************************************************************/ UINT _fx_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size) { UINT status; ULONG i; UINT found; ULONG bytes_per_cluster; ULONG FAT_index; ULONG FAT_value; ULONG clusters; FX_MEDIA *media_ptr; #ifdef TX_ENABLE_EVENT_TRACE TX_TRACE_BUFFER_ENTRY *trace_event; ULONG trace_timestamp; #endif /* First, determine if the file is still open. */ if (file_ptr -> fx_file_id != FX_FILE_ID) { /* Return the file not open error status. */ return(FX_NOT_OPEN); } /* Setup pointer to media structure. */ media_ptr = file_ptr -> fx_file_media_ptr; #ifndef FX_MEDIA_STATISTICS_DISABLE /* Increment the number of times this service has been called. */ media_ptr -> fx_media_file_allocates++; #endif /* Make sure this file is open for writing. */ if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) { /* Return the access error exception - a write was attempted from a file opened for reading! */ return(FX_ACCESS_ERROR); } /* Determine if the requested allocation is for zero bytes. */ if (size == 0) { /* Return a successful completion - nothing needs to be done. */ return(FX_SUCCESS); } /* Setup pointer to associated media control block. */ media_ptr = file_ptr -> fx_file_media_ptr; /* If trace is enabled, insert this event into the trace buffer. */ FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_ALLOCATE, file_ptr, size, file_ptr -> fx_file_current_available_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) /* Protect against other threads accessing the media. */ FX_PROTECT #ifdef FX_ENABLE_FAULT_TOLERANT /* Start transaction. */ _fx_fault_tolerant_transaction_start(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Check for write protect at the media level (set by driver). */ if (media_ptr -> fx_media_driver_write_protect) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return write protect error. */ return(FX_WRITE_PROTECT); } /* Calculate the number of bytes per cluster. */ bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * ((ULONG)media_ptr -> fx_media_sectors_per_cluster); /* Check for invalid value. */ if (bytes_per_cluster == 0) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Invalid media, return error. */ return(FX_MEDIA_INVALID); } /* Calculate the number of consecutive clusters needed to satisfy this request. */ clusters = (ULONG)(((size + bytes_per_cluster - 1) / bytes_per_cluster)); /* Determine if cluster count is 0. */ if (clusters == 0) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Size overflow when rounding to the next cluster, return an error status. */ return(FX_NO_MORE_SPACE); } /* Determine if there are enough available clusters on the media. */ if (clusters > media_ptr -> fx_media_available_clusters) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Not enough clusters, return an error status. */ return(FX_NO_MORE_SPACE); } /* Determine if the requested file allocation would exceed the physical limit of the file. */ if (((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) < file_ptr -> fx_file_current_available_size) || ((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) > 0xFFFFFFFFULL)) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the no more space error, since the new file size would be larger than the 32-bit field to represent it in the file's directory entry. */ return(FX_NO_MORE_SPACE); } /* Now we need to find the consecutive clusters. */ FAT_index = FX_FAT_ENTRY_START; found = FX_FALSE; while (FAT_index <= (media_ptr -> fx_media_total_clusters - clusters + FX_FAT_ENTRY_START)) { /* Determine if enough consecutive FAT entries are available. */ i = 0; do { /* Read a FAT entry. */ status = _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value); /* Check for a successful status. */ if (status != FX_SUCCESS) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the error status. */ return(status); } /* Determine if the entry is free. */ if (FAT_value != FX_FREE_CLUSTER) { break; } /* Otherwise, increment the consecutive FAT indices. */ i++; } while (i < clusters); /* Determine if we found enough FAT entries. */ if (i >= clusters) { /* Yes, we have found enough FAT entries - set the found flag and get out of this loop. */ found = FX_TRUE; break; } else { /* Position to the next possibly free FAT entry. */ FAT_index = FAT_index + i + 1; } } /* Determine if we found enough consecutive clusters to satisfy the request. */ if (found) { #ifdef FX_ENABLE_FAULT_TOLERANT if (media_ptr -> fx_media_fault_tolerant_enabled) { /* Record the action that is about to take place. This information would aid the undo process should fault condition happens. */ media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, file_ptr -> fx_file_last_physical_cluster, FAT_index, media_ptr -> fx_media_fat_last, media_ptr -> fx_media_fat_last); } #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Update the link pointers in the new clusters. */ for (i = 0; i < (clusters - 1); i++) { /* Update the cluster links. Since the allocation is sequential, we just have to link each FAT entry to the next one. */ status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1); /* Check for a bad status. */ if (status != FX_SUCCESS) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the error status. */ return(status); } } /* Now place an EOF in the last cluster entry. */ status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + clusters - 1, media_ptr -> fx_media_fat_last); /* Check for a bad status. */ if (status != FX_SUCCESS) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the error status. */ return(status); } #ifdef FX_FAULT_TOLERANT /* Flush the cached individual FAT entries */ _fx_utility_FAT_flush(media_ptr); #endif /* Actually link up the new clusters to the file. */ /* Determine if there are already clusters allocated for this file. */ if (file_ptr -> fx_file_total_clusters) { /* Linkup the last cluster. */ status = _fx_utility_FAT_entry_write(media_ptr, file_ptr -> fx_file_last_physical_cluster, FAT_index); /* Check for a bad status. */ if (status != FX_SUCCESS) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the error status. */ return(status); } /* Determine if we are adding a sector after a write filled the previously allocated cluster exactly. */ if ((file_ptr -> fx_file_current_relative_sector >= (media_ptr -> fx_media_sectors_per_cluster - 1)) && (file_ptr -> fx_file_current_logical_offset >= media_ptr -> fx_media_bytes_per_sector)) { /* Yes, we need to adjust all of the pertinent file parameters for access into this newly allocated cluster. */ file_ptr -> fx_file_current_physical_cluster = FAT_index; file_ptr -> fx_file_current_relative_cluster++; file_ptr -> fx_file_current_relative_sector = 0; file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) * ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); file_ptr -> fx_file_current_logical_offset = 0; } } else { /* These new clusters are also the first! Setup the initial file parameters. */ file_ptr -> fx_file_first_physical_cluster = FAT_index; file_ptr -> fx_file_current_physical_cluster = file_ptr -> fx_file_first_physical_cluster; file_ptr -> fx_file_current_relative_cluster = 0; file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) * ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); file_ptr -> fx_file_current_logical_offset = 0; file_ptr -> fx_file_current_file_offset = 0; /* Update the first cluster in the directory entry. */ file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = FAT_index; } /* Remember the last physical cluster. */ file_ptr -> fx_file_last_physical_cluster = FAT_index + clusters - 1; /* Check for wrap-around when updating the available size. */ /* Update the available size. */ file_ptr -> fx_file_current_available_size = file_ptr -> fx_file_current_available_size + (bytes_per_cluster * clusters); /* Increment the total clusters for this file. */ file_ptr -> fx_file_total_clusters = file_ptr -> fx_file_total_clusters + clusters; /* Decrease the available clusters on the media. */ media_ptr -> fx_media_available_clusters = media_ptr -> fx_media_available_clusters - clusters; #if defined(FX_UPDATE_FILE_SIZE_ON_ALLOCATE) || defined(FX_ENABLE_FAULT_TOLERANT) /* Set the file size the current size plus what what was added. */ file_ptr -> fx_file_current_file_size += size; /* Copy the new file size into the directory entry. */ file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; #endif #ifdef FX_ENABLE_FAULT_TOLERANT if (media_ptr -> fx_media_fault_tolerant_enabled) { /* Clear undo phase. */ media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff); } #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Update the trace event with the new size. */ FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_ALLOCATE, 0, 0, 0, file_ptr -> fx_file_current_file_size); /* Write the directory entry to the media. */ status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); /* Check for a good status. */ if (status != FX_SUCCESS) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return the error status. */ return(status); } } else { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Not enough contiguous space on the media. Return error status. */ return(FX_NO_MORE_SPACE); } #ifdef FX_FAULT_TOLERANT /* Flush the cached individual FAT entries */ _fx_utility_FAT_flush(media_ptr); #endif /* Flush the internal logical sector cache. */ status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64)(media_ptr -> fx_media_sectors_per_FAT), FX_FALSE); #ifdef FX_ENABLE_FAULT_TOLERANT /* Check for a bad status. */ if (status != FX_SUCCESS) { FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); /* Release media protection. */ FX_UNPROTECT /* Return the bad status. */ return(status); } if (media_ptr -> fx_media_fault_tolerant_enabled) { /* Copy the new file size into the directory entry. */ file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; } /* End transaction. */ status = _fx_fault_tolerant_transaction_end(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Return status to the caller. */ return(status); }