/*************************************************************************** * 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 */ /** */ /** Directory */ /** */ /**************************************************************************/ /**************************************************************************/ #define FX_SOURCE_CODE /* Include necessary system files. */ #include "fx_api.h" #include "fx_system.h" #include "fx_directory.h" #include "fx_file.h" #include "fx_utility.h" #ifdef FX_ENABLE_FAULT_TOLERANT #include "fx_fault_tolerant.h" #endif /* FX_ENABLE_FAULT_TOLERANT */ /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _fx_directory_create PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function first attempts to find the specified directory. */ /* If found, the create request is invalid and an error is returned */ /* to the caller. After the file name verification is made, a search */ /* for a free directory entry will be made. If nothing is available, */ /* an error will be returned to the caller. Otherwise, if all is */ /* okay, an empty directory will be created. */ /* */ /* INPUT */ /* */ /* media_ptr Media control block pointer */ /* directory_name Directory name */ /* */ /* OUTPUT */ /* */ /* return status */ /* */ /* CALLS */ /* */ /* _fx_directory_entry_write Write the new directory entry */ /* _fx_directory_name_extract Pickup next part of name */ /* _fx_directory_search Search for the file name in */ /* the directory structure */ /* _fx_directory_free_search Search for a free directory */ /* entry */ /* _fx_utility_FAT_flush Flush written FAT entries */ /* _fx_utility_FAT_entry_read Read a FAT entry */ /* _fx_utility_FAT_entry_write Write a FAT entry */ /* _fx_utility_logical_sector_flush Flush the written log sector */ /* _fx_utility_logical_sector_read Read logical 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 */ /* */ /* 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_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name) { UINT status; ULONG FAT_index; ULONG FAT_value; UINT sectors; ULONG total_clusters; CHAR *work_ptr; ULONG64 logical_sector; ULONG i; FX_DIR_ENTRY dir_entry; FX_DIR_ENTRY sub_dir_entry; FX_DIR_ENTRY search_directory; FX_INT_SAVE_AREA #ifndef FX_MEDIA_STATISTICS_DISABLE /* Increment the number of times this service has been called. */ media_ptr -> fx_media_directory_creates++; #endif /* Determine if the supplied name is less than the maximum supported name size. The maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h. */ i = 0; work_ptr = (CHAR *)directory_name; while (*work_ptr && (i < FX_MAX_LONG_NAME_LEN)) { /* Determine if the character designates a new path. */ if ((*work_ptr == '\\') || (*work_ptr == '/')) { /* Yes, reset the name size. */ i = 0; } /* Check for leading spaces. */ else if ((*work_ptr != ' ') || (i != 0)) { /* No leading spaces, increment the name size. */ i++; } /* Move to the next character. */ work_ptr++; } /* Determine if the supplied name is valid. */ if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN)) { /* Return an invalid name value. */ return(FX_INVALID_NAME); } /* Setup pointer to media name buffer. */ dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; /* Setup search directory pointer to another media name buffer. */ search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; /* Clear the short name string. */ dir_entry.fx_dir_entry_short_name[0] = 0; /* Clear the search short name string. */ search_directory.fx_dir_entry_short_name[0] = 0; /* Check the media to make sure it is open. */ if (media_ptr -> fx_media_id != FX_MEDIA_ID) { /* Return the media not opened error. */ return(FX_MEDIA_NOT_OPEN); } /* If trace is enabled, insert this event into the trace buffer. */ FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_CREATE, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) /* Protect against other threads accessing the media. */ FX_PROTECT /* Check for write protect at the media level (set by driver). */ if (media_ptr -> fx_media_driver_write_protect) { /* Release media protection. */ FX_UNPROTECT /* Return write protect error. */ return(FX_WRITE_PROTECT); } /* Make sure there is at least one cluster remaining for the new file. */ if (!media_ptr -> fx_media_available_clusters) { /* Release media protection. */ FX_UNPROTECT /* Return a no-space error. */ return(FX_NO_MORE_SPACE); } /* Search the system for the supplied directory name. */ status = _fx_directory_search(media_ptr, directory_name, &dir_entry, &search_directory, &work_ptr); /* Determine if the search was successful. */ if (status == FX_SUCCESS) { /* Release media protection. */ FX_UNPROTECT /* Directory found - Return the error code. */ return(FX_ALREADY_CREATED); } /* Determine if there is anything left after the name. */ if (_fx_directory_name_extract(work_ptr, &dir_entry.fx_dir_entry_name[0])) { /* Release media protection. */ FX_UNPROTECT /* Extra information after the file name, return an invalid name error. */ return(FX_INVALID_PATH); } #ifdef FX_ENABLE_FAULT_TOLERANT /* Start transaction. */ _fx_fault_tolerant_transaction_start(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Find a free slot for the new directory. */ status = _fx_directory_free_search(media_ptr, &search_directory, &dir_entry); /* Determine if the search was successful. */ 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 code. */ return(status); } /* Now allocate a cluster for our new sub-directory entry. */ FAT_index = media_ptr -> fx_media_cluster_search_start; total_clusters = media_ptr -> fx_media_total_clusters; /* Loop to find the first available cluster. */ do { /* Make sure we don't go past the FAT table. */ if (!total_clusters) { #ifdef FX_ENABLE_FAULT_TOLERANT FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Something is wrong with the media - the desired clusters were not found in the FAT table. */ return(FX_NO_MORE_SPACE); } /* Read FAT entry. */ status = _fx_utility_FAT_entry_read(media_ptr, FAT_index, &FAT_value); /* 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 bad status. */ return(status); } /* Decrement the total cluster count. */ total_clusters--; /* Determine if the FAT entry is free. */ if (FAT_value == FX_FREE_CLUSTER) { /* Move cluster search pointer forward. */ media_ptr -> fx_media_cluster_search_start = FAT_index + 1; /* Determine if this needs to be wrapped. */ if (media_ptr -> fx_media_cluster_search_start >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) { /* Wrap the search to the beginning FAT entry. */ media_ptr -> fx_media_cluster_search_start = FX_FAT_ENTRY_START; } /* Break this loop. */ break; } else { /* FAT entry is not free... Advance the FAT index. */ FAT_index++; /* Determine if we need to wrap the FAT index around. */ if (FAT_index >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) { /* Wrap the search to the beginning FAT entry. */ FAT_index = FX_FAT_ENTRY_START; } } } while (FX_TRUE); /* Decrease the number of available clusters for the media. */ media_ptr -> fx_media_available_clusters--; /* Defer the writing of the FAT entry until the directory's first sector has been properly written. If a power loss occurs prior to writing the FAT entry, it will not result in a lost cluster. */ /* Populate the directory entry. */ /* Isolate the file name. */ _fx_directory_name_extract(work_ptr, &dir_entry.fx_dir_entry_name[0]); /* Lockout interrupts for time/date access. */ FX_DISABLE_INTS /* Set time and date stamps. */ dir_entry.fx_dir_entry_time = _fx_system_time; dir_entry.fx_dir_entry_date = _fx_system_date; /* Restore interrupts. */ FX_RESTORE_INTS /* Set the attributes for the file. */ dir_entry.fx_dir_entry_attributes = FX_DIRECTORY; /* Set file size to 0. */ dir_entry.fx_dir_entry_file_size = 0; /* Set the cluster to EOF. */ dir_entry.fx_dir_entry_cluster = FAT_index; /* Is there a leading dot? */ if (dir_entry.fx_dir_entry_name[0] == '.') { /* Yes, toggle the hidden attribute bit. */ dir_entry.fx_dir_entry_attributes |= FX_HIDDEN; } /* In previous versions, the new directory was written here. It is now at the bottom of the file - after the FAT and the initial sub-directory is written out. This makes the directory create more fault tolerant. */ /* Calculate the first sector of the sub-directory file. */ 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)); /* Pickup the number of sectors for the initial sub-directory cluster. */ sectors = media_ptr -> fx_media_sectors_per_cluster; /* Read the first logical sector associated with the allocated cluster. */ status = _fx_utility_logical_sector_read(media_ptr, logical_sector, media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); /* Determine if an error occurred. */ 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 error code. */ return(status); } /* Clear the entire first sector of the new sub-directory cluster. */ work_ptr = (CHAR *)media_ptr -> fx_media_memory_buffer; i = 0; while (i < media_ptr -> fx_media_bytes_per_sector) { /* Clear 4 bytes. */ *((ULONG *)work_ptr) = (ULONG)0; /* Increment pointer. */ work_ptr = work_ptr + sizeof(ULONG); /* Increment counter. */ i = i + (ULONG)sizeof(ULONG); } #ifdef FX_ENABLE_FAULT_TOLERANT if (media_ptr -> fx_media_fault_tolerant_enabled == FX_TRUE) { /* Write back. */ status = _fx_utility_logical_sector_write(media_ptr, logical_sector, media_ptr -> fx_media_memory_buffer, (ULONG)1, FX_DIRECTORY_SECTOR); /* Determine if an error occurred. */ if (status != FX_SUCCESS) { FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); /* Release media protection. */ FX_UNPROTECT /* Return error code. */ return(status); } /* Force flush the internal logical sector cache. */ status = _fx_utility_logical_sector_flush(media_ptr, logical_sector, sectors, FX_TRUE); /* Determine if the write was successful. */ if (status != FX_SUCCESS) { FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); /* Release media protection. */ FX_UNPROTECT /* Return the error code. */ return(status); } } #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Determine if there are more sectors to clear in the first cluster of the new sub-directory. */ if (sectors > 1) { /* Yes, invalidate all cached sectors that are contained in the newly allocated first cluster of the directory. */ #ifdef FX_ENABLE_FAULT_TOLERANT if (media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE) #endif /* FX_ENABLE_FAULT_TOLERANT */ { /* Flush the internal logical sector cache. */ status = _fx_utility_logical_sector_flush(media_ptr, logical_sector + 1, ((ULONG64)(sectors - 1)), FX_TRUE); /* Determine if the write was successful. */ 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 code. */ return(status); } } /* Clear all additional sectors of new sub-directory. */ sectors--; while (sectors) { #ifndef FX_MEDIA_STATISTICS_DISABLE /* Increment the number of driver write sector(s) requests. */ media_ptr -> fx_media_driver_write_requests++; #endif /* Build Write request to the driver. */ media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; media_ptr -> fx_media_driver_status = FX_IO_ERROR; media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; #ifdef FX_DRIVER_USE_64BIT_LBA media_ptr -> fx_media_driver_logical_sector = logical_sector + ((ULONG)sectors); #else media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector + ((ULONG)sectors); #endif media_ptr -> fx_media_driver_sectors = 1; media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; /* Set the system write flag since we are writing a directory sector. */ media_ptr -> fx_media_driver_system_write = FX_TRUE; /* If trace is enabled, insert this event into the trace buffer. */ FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, ((ULONG)logical_sector) + ((ULONG)sectors), 1, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) /* Invoke the driver to write the sector. */ (media_ptr -> fx_media_driver_entry) (media_ptr); /* Clear the system write flag. */ media_ptr -> fx_media_driver_system_write = FX_FALSE; /* Determine if an error occurred. */ if (media_ptr -> fx_media_driver_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 error code. */ return(media_ptr -> fx_media_driver_status); } /* Decrease the number of sectors to clear. */ sectors--; } } /* Now setup the first sector with the initial sub-directory information. */ /* Copy the base directory entry to the sub-directory entry. */ sub_dir_entry = dir_entry; /* Setup pointer to media name buffer. */ sub_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + (FX_MAX_LONG_NAME_LEN * 3); /* Set the directory entry name to all blanks. */ work_ptr = &sub_dir_entry.fx_dir_entry_name[0]; for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) { *work_ptr++ = ' '; } sub_dir_entry.fx_dir_entry_long_name_present = 0; /* Now build the "." directory entry. */ sub_dir_entry.fx_dir_entry_name[0] = '.'; sub_dir_entry.fx_dir_entry_name[1] = 0; sub_dir_entry.fx_dir_entry_log_sector = logical_sector; sub_dir_entry.fx_dir_entry_byte_offset = 0; /* Write the directory's first entry. */ status = _fx_directory_entry_write(media_ptr, &sub_dir_entry); /* Check for error condition. */ 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 error status. */ return(status); } /* Now build the ".." directory entry. */ /* Determine if the search directory is the root. */ if (search_directory.fx_dir_entry_name[0]) { /* Previous directory is not the root directory. */ /* Copy into the working directory entry. */ sub_dir_entry = search_directory; /* Copy the date and time from the actual sub-directory. */ sub_dir_entry.fx_dir_entry_time = dir_entry.fx_dir_entry_time; sub_dir_entry.fx_dir_entry_date = dir_entry.fx_dir_entry_date; /* Adjust pointer to media name buffer. */ sub_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + (FX_MAX_LONG_NAME_LEN * 3); /* Change the name to ".." */ work_ptr = &sub_dir_entry.fx_dir_entry_name[0]; for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) { *work_ptr++ = ' '; } sub_dir_entry.fx_dir_entry_name[0] = '.'; sub_dir_entry.fx_dir_entry_name[1] = '.'; sub_dir_entry.fx_dir_entry_name[2] = 0; sub_dir_entry.fx_dir_entry_long_name_present = 0; /* Set file size to 0. */ sub_dir_entry.fx_dir_entry_file_size = 0; /* Change the logical sector for this entry. */ sub_dir_entry.fx_dir_entry_log_sector = logical_sector; } else { /* Just modify the current directory since the parent directory is the root. */ sub_dir_entry.fx_dir_entry_name[1] = '.'; sub_dir_entry.fx_dir_entry_name[2] = 0; /* Clear the cluster to indicate the root directory. */ sub_dir_entry.fx_dir_entry_cluster = 0; } /* Setup the byte offset. */ sub_dir_entry.fx_dir_entry_byte_offset = FX_DIR_ENTRY_SIZE; /* Write the directory's second entry. */ status = _fx_directory_entry_write(media_ptr, &sub_dir_entry); /* Check for error condition. */ 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 error status. */ return(status); } /* Write an EOF in the found FAT entry. */ status = _fx_utility_FAT_entry_write(media_ptr, FAT_index, 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 bad status. */ return(status); } #ifdef FX_FAULT_TOLERANT /* Flush the cached individual FAT entries */ _fx_utility_FAT_flush(media_ptr); #endif /* Now write out the new directory entry. */ status = _fx_directory_entry_write(media_ptr, &dir_entry); #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); } /* End transaction. */ status = _fx_fault_tolerant_transaction_end(media_ptr); #endif /* FX_ENABLE_FAULT_TOLERANT */ /* Release media protection. */ FX_UNPROTECT /* Directory create is complete, return status. */ return(status); }