@@ -0,0 +1,297 @@
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include "directory_entries.h"
#include "fat_entries.h"

fileData getFileData(FILE* filePtr, uint64_t byteAddress) {
fileData thisFileData;

// 1. jump to where the file data is
fseek(filePtr, byteAddress, SEEK_SET);

// 2. get the directory name
fread(&(thisFileData.fileName), READ_RESOLUTION, DN_SIZE, filePtr);
thisFileData.fileName[SH_DIRNAME_SIZE] = '\0';

// 3. get the file attributes
fread(&(thisFileData.fileAttributes), READ_RESOLUTION, DA_SIZE, filePtr);

// 4. get the create time
fseek(filePtr, DCT_OFFSET - (DA_OFFSET + DA_SIZE), SEEK_CUR);
fread(&(thisFileData.createTime), READ_RESOLUTION, DCT_SIZE, filePtr);
thisFileData.createTime = le16toh(thisFileData.createTime);

// 5. get the create date
fread(&(thisFileData.createDate), READ_RESOLUTION, DCD_SIZE, filePtr);
thisFileData.createDate = le16toh(thisFileData.createDate);

// 6. get the last access date
fread(&(thisFileData.lastAccessDate), READ_RESOLUTION, DLAD_SIZE, filePtr);
thisFileData.lastAccessDate = le16toh(thisFileData.lastAccessDate);

// 7. get the first cluster number high order word
fread(&(thisFileData.firstClusterNumHI), READ_RESOLUTION, DFCH_SIZE, filePtr);
thisFileData.firstClusterNumHI = le16toh(thisFileData.firstClusterNumHI);

// 8. get the last write time
fread(&(thisFileData.writeTime), READ_RESOLUTION, DWT_SIZE, filePtr);
thisFileData.writeTime = le16toh(thisFileData.writeTime);

// 9. get the last write date
fread(&(thisFileData.writeDate), READ_RESOLUTION, DWD_SIZE, filePtr);
thisFileData.writeDate = le16toh(thisFileData.writeDate);

// 10. get the first cluster number low order word
fread(&(thisFileData.firstClusterNumLO), READ_RESOLUTION, DFCL_SIZE, filePtr);
thisFileData.firstClusterNumLO = le16toh(thisFileData.firstClusterNumLO);

// 11. get the file size
fread(&(thisFileData.fileSize), READ_RESOLUTION, DFS_SIZE, filePtr);
thisFileData.fileSize = le32toh(thisFileData.fileSize);

return thisFileData;
}

BOOL isReadOnly(fileData thisFileData) {
if (((thisFileData.fileAttributes & 1) == 1) && (thisFileData.fileAttributes & 15) != 15) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isSystemFile(fileData thisFileData) {
if (((thisFileData.fileAttributes & 2) == 2) && (thisFileData.fileAttributes & 15) != 15) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isHiddenFile(fileData thisFileData) {
if (((thisFileData.fileAttributes & 4) == 4) && (thisFileData.fileAttributes & 15) != 15) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isVolumeLabel(fileData thisFileData) {
if (((thisFileData.fileAttributes & 8) == 8) && (thisFileData.fileAttributes & 15) != 15) {
return TRUE;
}
else {
return FALSE;
}
}


BOOL isLongName(fileData thisFileData) {
if ((thisFileData.fileAttributes & 15) == 15) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isDirectory(fileData thisFileData) {
if ((thisFileData.fileAttributes & 16) == 16) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isFile(fileData thisFileData) {
if ((thisFileData.fileAttributes & 32) == 32) {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isEndOfDirectory(fileData thisFileData) {
if (thisFileData.fileName[0] == '\0') {
return TRUE;
}
else {
return FALSE;
}
}

BOOL isEmptyDirectoryEntry(fileData thisFileData) {
if (thisFileData.fileName[0] == -27) {
return TRUE;
}
else {
return FALSE;
}
}

uint64_t checkFileExists(FILE* filePtr, bootSector thisBootSector, char* fileName, uint32_t currentClusterNumber) {
fileData thisFileData; //
FATEntry thisFATEntry;
uint32_t firstSectorOfCluster; // the first sector of the current cluster we're on
uint32_t sectorNumber;
uint64_t sectorNumberInBytes;
uint64_t currentByteAddress;

printf("checkFile gets %s\n", fileName);
printf("checkFile gets %d\n", currentClusterNumber);

while (currentClusterNumber != EOC) {
firstSectorOfCluster = getFirstSectorOfCluster(thisBootSector, currentClusterNumber);

for (sectorNumber = firstSectorOfCluster; sectorNumber < (firstSectorOfCluster + thisBootSector.sectorsPerCluster); sectorNumber++) {
sectorNumberInBytes = convertSectorNumToBytes(thisBootSector, sectorNumber);

for (currentByteAddress = sectorNumberInBytes; currentByteAddress < (sectorNumberInBytes + thisBootSector.bytesPerSector); currentByteAddress += DIR_ENTRY_SIZE) {
thisFileData = getFileData(filePtr, currentByteAddress);

if (isEndOfDirectory(thisFileData)) {
return -1;
}

// if this is not a volume lable, long name, or deleted entry
if (!isVolumeLabel(thisFileData) && !isLongName(thisFileData) && !isEmptyDirectoryEntry(thisFileData)) {
// check to see if the current directory matches our search and return byte address of entry if it does
//TODO: fix this
/**
FIX THIS strlen or strcmp
*/
if (strncmp(thisFileData.fileName, fileName, strlen(fileName) - 1) == 0)
{
return currentByteAddress;
}
}
}
}

// determine the next currentClusterNumber
thisFATEntry = getFATEntry(filePtr, thisBootSector, currentClusterNumber);
currentClusterNumber = thisFATEntry.nextClusterNumber;
}
}

uint32_t getFirstClusterOfEntry(bootSector thisBootSector, fileData thisFileData) {

uint32_t firstClusterEntry;

//Get the high word from the file and shift it left 16 bits
firstClusterEntry = thisFileData.firstClusterNumHI;
firstClusterEntry = firstClusterEntry << 16;

//Get the low word from the file and add it to the first cluster entry
firstClusterEntry += thisFileData.firstClusterNumLO;

return firstClusterEntry;
}

time getCreateTime(fileData thisFileData) {
time thisTime;
thisTime = convertToTimeStruct(thisFileData.createTime);
return thisTime;
}

date getCreateDate(fileData thisFileData) {
date thisDate;
thisDate = convertToDateStruct(thisFileData.createDate);
return thisDate;
}

date getLastAccessDate(fileData thisFileData) {
date thisDate;
thisDate = convertToDateStruct(thisFileData.lastAccessDate);
return thisDate;
}

time getWriteTime(fileData thisFileData) {
time thisTime;
thisTime = convertToTimeStruct(thisFileData.writeTime);
return thisTime;
}

date getWriteDate(fileData thisFileData) {
date thisDate;
thisDate = convertToDateStruct(thisFileData.writeDate);
return thisDate;
}

date convertToDateStruct(uint16_t machineRepresentation) {
date thisDate;
uint16_t mask;

// 1. make a bit mask for bits 5-15
mask = ~(~0 << 5);

// 2. take bitwise AND of this and the machine represenation
thisDate.day = mask & machineRepresentation;

// 3. shift 5 bits to the right to put month in the lowest order
machineRepresentation = machineRepresentation >> 5;

// 4. make a bit mask for bits 4-15
mask = ~(~0 << 4);

// 5. take bitwise AND of this and the machine represenation
thisDate.month = mask & machineRepresentation;

// 6. shift 4 bits to the right to put year in lowest order
machineRepresentation = machineRepresentation >> 4;

// 7. make a bit mask for 7-15
mask = ~(~0 << 7);

// 8. take bitwise AND of this and the machine representation
thisDate.year = mask & machineRepresentation;

// 9. add epoch to the calculated year
thisDate.year += EPOCH_YEAR;

return thisDate;
}

time convertToTimeStruct(uint16_t machineRepresentation) {
time thisTime;
uint16_t mask;

// 1. make a bit mask for bits 5-15
mask = ~(~0 << 5);

// 2. take a bitwise AND of this and the machine representation
thisTime.seconds = mask & machineRepresentation;

// 3. multiply seconds by 2 (because granularity is 2 seconds)
thisTime.seconds *= 2;

// 4. shift 5 bits to the right to put minutes in lowest order
machineRepresentation = machineRepresentation >> 5;

// 5. make a bit mask for 6-15
mask = ~(~0 << 6);

// 6. take a bitwise AND of this and the machine representation
thisTime.minutes = mask & machineRepresentation;

// 7. shift 6 bits to the right to put hours in the lowest order
machineRepresentation = machineRepresentation >> 6;

// 8. make a bit mask for 5-15
mask = ~(~0 << 5);

// 9. take a bitwise AND of this and the machine representation
thisTime.hours = mask & machineRepresentation;

return thisTime;
}




@@ -0,0 +1,205 @@
#ifndef DIRECTORY_ENTRIES_H
#define DIRECTORY_ENTRIES_H
////////////////////////////////////////////////////////////////////////////
// INCLUDES ////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

#include <stdint.h>
#include "boot_sector.h"
////////////////////////////////////////////////////////////////////////////
// MACROS //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

#ifndef BOOL
#define BOOL int
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define DN_OFFSET 0 // directory name
#define DN_SIZE 11
#define DA_OFFSET 11 // directory attributes
#define DA_SIZE 1
#define DCT_OFFSET 14 // create time
#define DCT_SIZE 2
#define DCD_OFFSET 16 // create date
#define DCD_SIZE 2
#define DLAD_OFFSET 18 // last access date
#define DLAD_SIZE 2
#define DFCH_OFFSET 20 // first cluster number high order word
#define DFCH_SIZE 2
#define DWT_OFFSET 22 // write time
#define DWT_SIZE 2
#define DWD_OFFSET 24 // write date
#define DWD_SIZE 2
#define DFCL_OFFSET 26 // first cluster number low order word
#define DFCL_SIZE 2
#define DFS_OFFSET 28 // file size
#define DFS_SIZE 4
#define SH_FILE_NAME 11 // short file name size in characters

#define READ_RESOLUTION 1 // the resolution we want to read at in bytes
#define SH_DIRNAME_SIZE 11 // the number of characters in a short directory name
#define DIR_ENTRY_SIZE 32 // the size of each entry within a directory in bytes
#define EPOCH_YEAR 1980 // the year that all years are calculated from in fat32

/////////////////////////////////////////////////////////////////////////////
// TYPE DEFINITONS //////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

typedef struct {
char fileName[SH_FILE_NAME+1]; // the name of the file
uint8_t fileAttributes; // all attributes associated with file
uint16_t createTime; // time file was created
uint16_t createDate; // date file was created
uint16_t lastAccessDate; // date file was last accessed
uint16_t firstClusterNumHI; // the higher value word of the first cluster number
uint16_t writeTime; // time file was last written to
uint16_t writeDate; // date file was last written to
uint16_t firstClusterNumLO; // the lower value word of the first cluster number
uint32_t fileSize; // the size of the file in bytes
uint64_t byteAddress; // holds the byte address of the directory entry structure
} fileData;

typedef struct {
int day;
int month;
int year;
} date;

typedef struct {
int hours;
int minutes;
int seconds;
} time;

//////////////////////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS /////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

// In: byteAddress (uint64_t)
// Out: thisFileData (fileData (struct))
// Purpose: to place all file metadata based on a byte address in a struct and return it
// Note: handles file metadata blocks that are 32 bits (4 bytes) in size
fileData getFileData(FILE* filePtr, uint64_t byteAddress);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the file is read only
// Notes:
BOOL isReadOnly(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is a system file
// Notes:
BOOL isSystemFile(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is a hidden file/directory
// Notes:
BOOL isHiddenFile(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is the volume label of the volume
// Notes:
BOOL isVolumeLabel(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is part of a long name
// Notes: fileAttributes is really a byte we are representing as an int
BOOL isLongName(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is a directory
// Notes:
BOOL isDirectory(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is a file
// Notes:
BOOL isFile(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to detemine if we have reached the end of the directory (no more entries left)
// Notes:
BOOL isEndOfDirectory(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if a given directory entry is empty
// Notes: this applies to directories/files that were deleted
BOOL isEmptyDirectoryEntry(fileData thisFileData);

// In: filePtr (FILE*), fileName (char*), currentByteAddress (uint64_t)
// Out: directoryEntryAddress (uint64_t)
// Purpose: to determine if a file exists in the current directory
// Notes: this applies to directories/files that were deleted; it should be noted
// that the address returned is the address oof the directory entry NOT the
// actual file contents; -1 is returned if the file doesn't exist
uint64_t checkFileExists(FILE* filePtr, bootSector thisBootSector, char* fileName, uint32_t currentClusterNumber);

// In: thisFileData (fileData (struct))
// Out: boolean
// Purpose: to determine if the current file directory is a file
// Notes: fileAttributes is really a byte we are representing as an int
uint32_t getFirstClusterOfEntry(bootSector thisBootSector, fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: thisTime (time (struct))
// Purpose: to get the create time of the directory entry
// Notes:
time getCreateTime(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: thisDate (date (struct))
// Purpose: to get the create date of the directory entry
// Notes:
date getCreateDate(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: thisDate (date (struct))
// Purpose: to get the last access date of the directory entry
// Notes:
date getLastAccessDate(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: thisTime (time (struct))
// Purpose: to get the last write time of the directory entry
// Notes:
time getWriteTime(fileData thisFileData);

// In: thisFileData (fileData (struct))
// Out: thisDate (date (struct))
// Purpose: to get the last write date of the directory entry
// Notes:
date getWriteDate(fileData thisFileData);

// In: machineRepresentation (uint16_t)
// Out: thisDate (date (struct))
// Purpose: to convert the machine representation of the date into a human readable date
// Notes: specific bits are extracted through shifting and using & for masking
date convertToDateStruct(uint16_t machineRepresentation);

// In: machineRepresentation (uint16_t)
// Out: thisTime (time (struct))
// Purpose: to convert the machine representation of a time to a human-readable version
// Notes: same logic as convertToDateStruct
time convertToTimeStruct(uint16_t machineRepresentation);

#endif /* DIRECTORY_ENTRIES_H */



@@ -0,0 +1,44 @@
#include "equations.h"

uint32_t getRootDirectorySectors(bootSector thisBootSector) {
return (((thisBootSector.rootEntryCount * 32) + (thisBootSector.bytesPerSector - 1)) / thisBootSector.bytesPerSector);
}

uint32_t getFirstDataSector(bootSector thisBootSector) {
return (thisBootSector.reservedSectorCount + (thisBootSector.numOfFATs * thisBootSector.fatSize) + thisBootSector.rootDirectorySectors);
}

uint32_t getFirstSectorOfCluster(bootSector thisBootSector, uint32_t numOfCluster) {
return (((numOfCluster - 2) * thisBootSector.sectorsPerCluster) + thisBootSector.firstDataSector);
}

uint32_t getThisFATSectorNumber(bootSector thisBootSector, uint32_t numOfCluster) {
uint64_t fatOffset;
fatOffset = numOfCluster * 4;
return (thisBootSector.reservedSectorCount + (fatOffset / thisBootSector.bytesPerSector));
}

uint16_t getThisFATEntryOffset(bootSector thisBootSector, uint32_t numOfCluster) {
uint64_t fatOffset;
fatOffset = numOfCluster * 4;
return (fatOffset % thisBootSector.bytesPerSector);
}

uint64_t convertSectorNumToBytes(bootSector thisBootSector, uint32_t sectorNumber) {
return sectorNumber * thisBootSector.bytesPerSector;
}

uint64_t convertClusterNumToBytes(bootSector thisBootSector, uint32_t clusterNumber) {
uint32_t firstSectorOfCluster;
uint64_t clusterNumberInBytes;

firstSectorOfCluster = getFirstSectorOfCluster(thisBootSector, clusterNumber);
clusterNumberInBytes = convertSectorNumToBytes(thisBootSector, firstSectorOfCluster);
return clusterNumberInBytes;
}

uint32_t convertBytesToClusterNum(bootSector thisBootSector, uint64_t byteAddress) {
uint32_t firstDataSector;
firstDataSector = getFirstDataSector(thisBootSector);
return ((((byteAddress / thisBootSector.bytesPerSector) - firstDataSector) / thisBootSector.sectorsPerCluster) + 2);
}
@@ -0,0 +1,63 @@
#ifndef EQUATIONS_H
#define EQUATIONS_H
///////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

#include <stdint.h>
#include "boot_sector.h"

///////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS //////////////////////////////////////
///////////////////////////////////////////////////////////////

// In: thisBootSector (bootSector (struct))
// Out: numOfSectors (uint32_t)
// Purpose: to get the number of sectors in the root directory
// Notes:
uint32_t getRootDirectorySectors(bootSector thisBootSector);

// In: thisBootSector (bootSector (struct))
// Out: sectorNumber (uint32_t)
// Purpose: to get the first sector number of the data region
// Notes:
uint32_t getFirstDataSector(bootSector thisBootSector);

// In: thisBootSector (bootSector (struct))
// Out: sectorNumber (uint32_t)
// Purpose: to get the first sector number of a cluster
// Notes: this is automatically adjusted based on the first data sector number
uint32_t getFirstSectorOfCluster(bootSector thisBootSector, uint32_t numOfCluster);

// In: thisBootSector (bootSector (struct)), numOfCluster (uint32_t)
// Out: sectorNumber (uint32_t)
// Purpose: to get the sector number of the FAT entry for this cluster
// Notes: this applies to the FAT REGION not the data region
uint32_t getThisFATSectorNumber(bootSector thisBootSector, uint32_t numOfCluster);

// In: thisBootSector (bootSector (struct)), numOfCluster (uint32_t)
// Out: fatByteOffset (uint16_t)
// Purpose: to get the byte address offset within the sector number when determining the FAT entry location
// Notes:
uint16_t getThisFATEntryOffset(bootSector thisBootSector, uint32_t numOfCluster);

// In: thisBootSector (bootSector (struct)), sectorNumber (uint32_t)
// Out: sectorNumInBytes (uint64_t)
// Purpose: to return the byte address of a given sector number
// Notes:
uint64_t convertSectorNumToBytes(bootSector thisBootSector, uint32_t sectorNumber);

// In: thisBootSector (bootSector (struct)), clusterNumber (uint32_t)
// Out: clusterNumInBytes (uint64_t)
// Purpose: to return the byte address of a given cluster number
// Notes:
uint64_t convertClusterNumToBytes(bootSector thisBootSector, uint32_t clusterNumber);


// In: thisBootSector (bootSector (struct)), clusterNumber (uint32_t)
// Out: clusterNumInBytes (uint64_t)
// Purpose: to return the byte address of a given cluster number
// Notes:
uint32_t convertBytesToClusterNum(bootSector thisBootSector, uint64_t byteAddress);

#endif /* EQUATIONS_H */
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

@@ -0,0 +1,41 @@
///////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#include "fat_entries.h"
#include <sys/types.h>

///////////////////////////////////////////////////////////////////////////
// FUNCTION DEFINITIONS ///////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
FATEntry getFATEntry(FILE* filePtr, bootSector thisBootSector, uint32_t clusterNumber) {
uint32_t thisFATEntrySectorNumber; // the sector number of thisFATEntry
uint16_t thisFATEntryByteOffset; // the byte offset within the sector of thisFATentry
uint64_t thisFATEntryByteAddress; // the byte address of the fat entry
uint32_t nextClusterNumber; // the next cluster number in the cluster chain
FATEntry thisFATEntry; // the FAT entry structure we create and return

// 1. determine the sector number index of the FAT for clusterNumber
thisFATEntrySectorNumber = getThisFATSectorNumber(thisBootSector, clusterNumber);

// 2. determine the byte offset into the determined sector number
thisFATEntryByteOffset = getThisFATEntryOffset(thisBootSector, clusterNumber);

// 3. convert sector number and offset into byte address
thisFATEntryByteAddress = convertSectorNumToBytes(thisBootSector, thisFATEntrySectorNumber) + thisFATEntryByteOffset;

// 3. read the 4-byte entry
fseek(filePtr, thisFATEntryByteAddress, SEEK_SET);
fread(&nextClusterNumber, READ_RESOLUTION, FAT_ENTRY_SIZE, filePtr);
nextClusterNumber = le32toh(nextClusterNumber);

// 4. build and return the FAT entry
thisFATEntry.currentClusterNumber = clusterNumber;
thisFATEntry.nextClusterNumber = nextClusterNumber;
thisFATEntry.FATEntrySectorNumber = thisFATEntrySectorNumber;
thisFATEntry.FATEntryByteOffset = thisFATEntryByteOffset;
thisFATEntry.FATEntryByteAddress = thisFATEntryByteAddress;

return thisFATEntry;

}

@@ -0,0 +1,36 @@
#ifndef FAT_ENTRIES_H
#define FAT_ENTRIES_H

///////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#include "equations.h"

///////////////////////////////////////////////////////////////////////////
// MACROS /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#define EOC 268435448 // hex: 0x0FFFFFF8
#define FAT_ENTRY_SIZE 4

///////////////////////////////////////////////////////////////////////////
// TYPE DEFINITIONS ///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
typedef struct {
uint32_t currentClusterNumber; // consists of the current cluster number we are on
uint32_t nextClusterNumber; // consists of the next cluster number in the cluster chain
uint32_t FATEntrySectorNumber; // holds the sector number of the FAT entry
uint16_t FATEntryByteOffset; // holds the byte offset FROM the sector number of the FAT entry
uint64_t FATEntryByteAddress; // the byte address of the FAT entry
} FATEntry;

///////////////////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

// In: thisBootSector (bootSector (struct)), clusterNumber (uint32_t)
// Out: thisFatEntry (fatEntry (struct))
// Purpose: to create a fatEntry struct for convenienve in moving through clusters and to abstract cluster chain logic
// Notes:
FATEntry getFATEntry(FILE* filePtr, bootSector thisBootSector, uint32_t clusterNumber);

#endif /* FAT_ENTRIES_H */
@@ -0,0 +1,29 @@
#include <string.h>
#include <stdio.h>
#include "format.h"

char* formatFileName(fileData thisFileData) {
char* name;
char* ext;
static char* toReturn;

name = strtok(thisFileData.fileName, " ");
if (isFile(thisFileData))
{
ext = strtok(NULL, " ");
}

strcpy(toReturn, name);
if (isFile(thisFileData))
{
strcat(toReturn, ".");
strcat(toReturn, ext);
}

return toReturn;

}

//char* formatDirectoryName(char* directoryName) {

//}
@@ -0,0 +1,12 @@
#include "directory_entries.h"
// In: filename (char*)
// Out: formattedFileName (char*)
// Purpose: to get the first sector number of the data region
// Notes:
char* formatFileName(fileData thisFileData);

// In: reservedSectorCount (uint16_t), numOfFATs (uint8_t), fatSize (uint32_t), rootDirectorySectors (uint32_t)
// Out: formatterDirName (char*)
// Purpose: to get the first sector number of the data region
// Notes:
//char* formatDirectoryName(char* directoryName);
@@ -0,0 +1,4 @@
#!/bin/bash
gcc -o fat32_reader fat32_reader.c directory_entries.c equations.c boot_sector.c fat_entries.c
./fat32_reader fat32.img
rm fat32_reader
@@ -0,0 +1,48 @@
//////////////////////////////////////////////////////////////
// INCLUDES //////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
#include "equations.h"

//////////////////////////////////////////////////////////////
// GLOBAL VARIABLES //////////////////////////////////////////
//////////////////////////////////////////////////////////////
bootSector thisBootSector;
FILE* filePtr;

//////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS /////////////////////////////////////
//////////////////////////////////////////////////////////////
void testSetUpBootSector(int testNumber);

//////////////////////////////////////////////////////////////
// MAIN PROGRAM //////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int main(int argc, char** argv) {

if (argc != 2) {
printf("You entered an incorrect amount of arguments for this program. <USAGE: ./fat32_reader fat32.img>\n");
return -1;
}

if ((filePtr = fopen(argv[1], "rb")) == 0) {
perror(argv[1]);
return -1;
}

testSetUpBootSector(4);
}

void testSetUpBootSector(int testNumber) {
thisBootSector = setUpBootSector(filePtr);

printf("bytesPerSector: Expected = 512, Actual = %d\n", thisBootSector.bytesPerSector);
printf("sectorsPerCluster: Expected = 1, Actual = %d\n", thisBootSector.sectorsPerCluster);
printf("reservedSectorCount: Expected = 32, Actual = %d\n", thisBootSector.reservedSectorCount);
printf("numOfFATs: Expected = 2, Actual = %d\n", thisBootSector.numOfFATs);
printf("fatSize: Expected = 1009, Actual = %d\n", thisBootSector.fatSize);
printf("rootClusterNum: Expected = 2, Actual = %d\n", thisBootSector.rootClusterNum);
printf("rootEntryCount: Expected = 0, Actual = %d\n", thisBootSector.rootEntryCount);
printf("totalSectors: Expected = 131072, Actual = %d\n", thisBootSector.totalSectors);
printf("rootDirectorySectors: Expected = 0, Actual = %d\n", thisBootSector.rootDirectorySectors);
printf("firstDataSector: Expected = 2050, Actual = %d\n", thisBootSector.firstDataSector);
}
@@ -0,0 +1,366 @@
////////////////////////////////////////////////////////////
// INCLUDES //////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
#include <stdio.h>
#include "directory_entries.h"
#include <string.h>
#include "equations.h"
/////////////////////////////////////////////////////////////
// MACROS ///////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
#define ENTRIES_COUNT 6

//////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS /////////////////////////////////////
//////////////////////////////////////////////////////////////
void testEndOfDirectory(int testNumber);
void setUpSystem();
void testSetUp();
void testIsEmptyDirectoryEntry(int testNumber);
void testIsLongName(int testNumber);
void testIsDirectory(int testNumber);
void testIsFile(int testNumber);
void testIsVolumeLabel(int testNumber);
void testIsReadOnly(int testNumber);
void testCheckFileExists(int testNumber);
void testGetFirstClusterOfEntry(int testNumber);
void testConvertToDateStruct(int testNumber);
void testConvertToTimeStruct(int testNumber);

//////////////////////////////////////////////////////////////
// GLOBAL VARIABLES //////////////////////////////////////////
//////////////////////////////////////////////////////////////
fileData directoryEntry_1;
fileData directoryEntry_2;
fileData directoryEntry_3;
fileData directoryEntry_4;
fileData directoryEntry_5;
fileData directoryEntry_6;

fileData system[ENTRIES_COUNT];

FILE* filePtr;
bootSector thisBootSector;

//////////////////////////////////////////////////////////////
// MAIN PROGRAM //////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int main(int argc, char** argv) {

if (argc != 2) {
printf("You entered an incorrect amount of arguments for this program. <USAGE: ./fat32_reader fat32.img>\n");
return -1;
}

if ((filePtr = fopen(argv[1], "rb")) == 0) {
perror(argv[1]);
return -1;
}

thisBootSector = setUpBootSector(filePtr);

setUpSystem();
testIsReadOnly(2);
testIsFile(3);
testIsDirectory(5);
testIsEmptyDirectoryEntry(5);
testIsVolumeLabel(1);
testIsLongName(2);
testConvertToDateStruct(1);
testConvertToDateStruct(2);
testConvertToTimeStruct(1);
testConvertToTimeStruct(2);
testCheckFileExists(1);
testCheckFileExists(2);
testCheckFileExists(3);
testCheckFileExists(4);
testCheckFileExists(5);
testGetFirstClusterOfEntry(1);


fclose(filePtr);
}

//////////////////////////////////////////////////////////////
// FUNCTION DEFINITIONS //////////////////////////////////////
//////////////////////////////////////////////////////////////

void setUpSystem() {
// initialize directoryEntry_1
char fileName1[SH_FILE_NAME + 1] = "CHUCKLES";
strcpy(directoryEntry_1.fileName, fileName1);
directoryEntry_1.fileAttributes = 8; // 00001000
directoryEntry_1.createTime = 0;
directoryEntry_1.createDate = 0;
directoryEntry_1.lastAccessDate = 0;
directoryEntry_1.firstClusterNumHI = 0;
directoryEntry_1.firstClusterNumLO = 0;
directoryEntry_1.writeTime = 0;
directoryEntry_1.writeDate = 0;
directoryEntry_1.fileSize = 0;

// initialize directoryEntry_2
char fileName2[SH_FILE_NAME + 1] = "POOP";
strcpy(directoryEntry_2.fileName, fileName2);
directoryEntry_2.fileAttributes = 16; // 00010000
directoryEntry_2.createTime = 0;
directoryEntry_2.createDate = 0;
directoryEntry_2.lastAccessDate = 0;
directoryEntry_2.firstClusterNumHI = 2;
directoryEntry_2.firstClusterNumLO = 43;
directoryEntry_2.writeTime = 0;
directoryEntry_2.writeDate = 0;
directoryEntry_2.fileSize = 0;

// initialize directoryEntry_3
char fileName3[SH_FILE_NAME + 1] = "GHOST.TXT";
strcpy(directoryEntry_3.fileName, fileName3);
directoryEntry_3.fileName[0] = -27;
directoryEntry_3.fileAttributes = 32; // 0010 0000
directoryEntry_3.createTime = 43195; // 1010 1000 1011 1011
directoryEntry_3.createDate = 18613; // 0100 1000 1011 0101
directoryEntry_3.lastAccessDate = 0;
directoryEntry_3.firstClusterNumHI = 0;
directoryEntry_3.firstClusterNumLO = 0;
directoryEntry_3.writeTime = 0;
directoryEntry_3.writeDate = 0;
directoryEntry_3.fileSize = 0;

// initialize directoryEntry_4
char fileName4[SH_FILE_NAME + 1] = "BANANA.TXT";
strcpy(directoryEntry_4.fileName, fileName4);
directoryEntry_4.fileAttributes = 33; // 0010 0001
directoryEntry_4.createTime = 12157; // 0010 1111 0111 1101
directoryEntry_4.createDate = 10830; // 0010 1010 0100 1110
directoryEntry_4.lastAccessDate = 0;
directoryEntry_4.firstClusterNumHI = 0;
directoryEntry_4.firstClusterNumLO = 54;
directoryEntry_4.writeTime = 0;
directoryEntry_4.writeDate = 0;
directoryEntry_4.fileSize = 342;

// initialize directoryEntry_5
char fileName5[SH_FILE_NAME + 1] = "\0";
strcpy(directoryEntry_5.fileName, fileName5);
directoryEntry_5.fileAttributes = 0;
directoryEntry_5.createTime = 0;
directoryEntry_5.createDate = 0;
directoryEntry_5.lastAccessDate = 0;
directoryEntry_5.firstClusterNumHI = 0;
directoryEntry_5.firstClusterNumLO = 0;
directoryEntry_5.writeTime = 0;
directoryEntry_5.writeDate = 0;
directoryEntry_5.fileSize = 0;

// initialize directoryEntry_6
char fileName6[SH_FILE_NAME + 5] = "POOPOO";
strcpy(directoryEntry_6.fileName, fileName6);
directoryEntry_6.fileAttributes = 15;
directoryEntry_6.createTime = 0;
directoryEntry_6.createDate = 0;
directoryEntry_6.lastAccessDate = 0;
directoryEntry_6.firstClusterNumHI = 0;
directoryEntry_6.firstClusterNumLO = 0;
directoryEntry_6.writeTime = 0;
directoryEntry_6.writeDate = 0;
directoryEntry_6.fileSize = 0;

system[0] = directoryEntry_1;
system[1] = directoryEntry_2;
system[2] = directoryEntry_3;
system[3] = directoryEntry_4;
system[4] = directoryEntry_5;
system[5] = directoryEntry_6;
}

// tests: setUpSystem()
// from: here
void testSetUp() {
int i;
fileData thisFileData;

printf("Testing set up...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
thisFileData = system[i];
printf("%s\n", thisFileData.fileName);
printf("%d\n", thisFileData.fileAttributes);
printf("%d\n", thisFileData.createTime);
printf("%d\n", thisFileData.createDate);
printf("%d\n", thisFileData.lastAccessDate);
printf("%d\n", thisFileData.firstClusterNumHI);
printf("%d\n", thisFileData.firstClusterNumLO);
printf("%d\n", thisFileData.writeTime);
printf("%d\n", thisFileData.writeDate);
printf("%d\n", thisFileData.fileSize);
printf("\n\n");
}
}

// tests: isEndOfDirectory()
// from: booleans.h
void testEndOfDirectory(int testNumber) {
int i; // looping variable

printf("Testing end of directory...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
if (isEndOfDirectory(system[i])) {
printf("HOLY CRAP! IT IS THE END!\n");
}
else {
printf("Nope... it's not over yet.\n");
}
}
}

void testIsDirectory(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 1, 0, 0, 0, 0};
int actualValue;
int i;

printf("Testing is directory...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isDirectory(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);
}
}

void testIsFile(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 1, 1, 0, 0};
int actualValue;
int i;

printf("Testing is file...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isFile(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);

}
}

void testIsEmptyDirectoryEntry(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 1, 0, 0, 0};
int actualValue;
int i;

printf("Testing is empty directory entry...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isEmptyDirectoryEntry(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);

}
}

void testIsReadOnly(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 0, 1, 0, 0};
int actualValue;
int i;


printf("Testing is read only...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isReadOnly(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);

}
}

void testIsVolumeLabel(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {1, 0, 0, 0, 0, 0};
int actualValue;
int i;

printf("Testing is volume label...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isVolumeLabel(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);

}
}

void testIsLongName(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 0, 0, 0, 1};
int actualValue;
int i;

printf("Testing is long name...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = isLongName(system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);
}
}

void testCheckFileExists(int testNumber) {
int directoryEntryAddress;

printf("Testing checkFileExists()...\n");

if (testNumber == 1) {
printf("Running test 1...\n");
directoryEntryAddress = checkFileExists(filePtr, thisBootSector, "FSINFO TXT", thisBootSector.rootClusterNum);
printf("Expected Value = 1049664; Actual Value = %d\n", directoryEntryAddress);
}
else if (testNumber == 2) {
printf("Running test 2...\n");
directoryEntryAddress = checkFileExists(filePtr, thisBootSector, "DIR", thisBootSector.rootClusterNum);
printf("Expected Value = 1049920; Actual Value = %d\n", directoryEntryAddress);
}
else if (testNumber == 3) {
printf("Running test 3...\n");
directoryEntryAddress = checkFileExists(filePtr, thisBootSector, ".ECRET TXT", thisBootSector.rootClusterNum);
printf("Expected Value = -1; Actual Value = %d\n", directoryEntryAddress);
}
else if (testNumber == 4) {
printf("Running test 4...\n");
directoryEntryAddress = checkFileExists(filePtr, thisBootSector, "CHUCKLES", thisBootSector.rootClusterNum);
printf("Expected Value = -1; Actual Value = %d\n", directoryEntryAddress);
}
else if (testNumber == 5) {
printf("Running test 5...\n");
directoryEntryAddress = checkFileExists(filePtr, thisBootSector, "POOPOOBA", thisBootSector.rootClusterNum);
printf("Expected Value = -1; Actual Value = %d\n", directoryEntryAddress);
}
}

void testGetFirstClusterOfEntry(int testNumber) {
int i;
int expectedValues[ENTRIES_COUNT] = {0, 131115, 0, 54, 0, 0};
uint32_t actualValue;

printf("Testing getFirstClusterOfEntry()...\n");
for (i = 0; i < ENTRIES_COUNT; i++) {
actualValue = getFirstClusterOfEntry(thisBootSector, system[i]);
printf("Expected Value = %d; Actual Value = %d\n", expectedValues[i], actualValue);
}
}

void testConvertToDateStruct(int testNumber) {
date thisDate;

printf("Testing convertToDateStruct()...\n");
if (testNumber == 1) {
printf("Running test 1...\n");
thisDate = convertToDateStruct(directoryEntry_3.createDate);
printf("Expected = 05-21-2016; Actual = %02d-%02d-%02d\n", thisDate.month, thisDate.day, thisDate.year);
}
else if (testNumber == 2) {
printf("Running test 2...\n");
thisDate = convertToDateStruct(directoryEntry_4.createDate);
printf("Expected = 02-14-2001; Actual = %02d-%02d-%02d\n", thisDate.month, thisDate.day, thisDate.year);
}
}

void testConvertToTimeStruct(int testNumber) {
time thisTime;

printf("Testing convertToTimeStruct()... \n");
if (testNumber == 1) {
printf("Running test 1...\n");
thisTime = convertToTimeStruct(directoryEntry_3.createTime);
printf("Expected = 21:05:54; Actual = %02d:%02d:%02d\n", thisTime.hours, thisTime.minutes, thisTime.seconds);
}
else if (testNumber == 2) {
printf("Running test 2...\n");
thisTime = convertToTimeStruct(directoryEntry_4.createTime);
printf("Expected = 05:59:58; Actual = %02d:%2d:%02d\n", thisTime.hours, thisTime.minutes, thisTime.seconds);
}
}
@@ -0,0 +1,129 @@
#include <stdio.h>
#include <stdint.h>
#include "equations.h"
//#include "boot_sector.h" INHERITED FROM EQUATIONS

////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES ////////////////////////////////////////////
////////////////////////////////////////////////////////////////
FILE* filePtr;
bootSector thisBootSector;

////////////////////////////////////////////////////////////////
// FUNCTION DELCARATIONS ///////////////////////////////////////
////////////////////////////////////////////////////////////////
void testGetRootDirectorySectors(int testNumber);
void testGetFirstDataSector(int testNumber);
void testGetFirstSectorOfCluster(int testNumber);
void testGetThisFATSectorNumber(int testNumber);
void testGetThisFATEntryOffset(int testNumber);
void testConvertSectorNumToBytes(int testNumber);
void testConvertClusterNumToBytes(int testNumber);

////////////////////////////////////////////////////////////////
// MAIN PROGRAM ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {

// set up file pointer
if (argc != 2) {
printf("You entered an incorrect amount of arguments for this program. <USAGE: ./fat32_reader fat32.img>\n");
return -1;
}

if ((filePtr = fopen(argv[1], "rb")) == 0) {
perror(argv[1]);
return -1;
}

// set up the boot sector
thisBootSector = setUpBootSector(filePtr);

// run the actual tests!
testGetRootDirectorySectors(1);
testGetFirstDataSector(1);
testGetFirstSectorOfCluster(1);
testGetThisFATSectorNumber(1);
testGetThisFATEntryOffset(1);
testConvertSectorNumToBytes(1);
testConvertClusterNumToBytes(1);
testConvertClusterNumToBytes(2);

return 0;
}

////////////////////////////////////////////////////////////////
// FUNCTION DEFINITIONS ////////////////////////////////////////
////////////////////////////////////////////////////////////////

// tests: getRootDirectorySectors()
// from: equations.c
void testGetRootDirectorySectors(int testNumber) {
uint32_t rootDirectorySectors;
printf("Testing getRootDirectorySectors()...\n");
rootDirectorySectors = getRootDirectorySectors(thisBootSector);
printf("Expected Value = 0; Actual Value = %d\n", rootDirectorySectors);
}

// tests: getGetFirstDataSector()
// from: equations.void testGetFirstDataSector(int testNumber) {
void testGetFirstDataSector(int testNumber) {
uint32_t firstDataSectorNumber;
printf("Testing getFirstDataSector()...\n");
firstDataSectorNumber = getFirstDataSector(thisBootSector);
printf("Expected Value = 2050; Actual Value = %d\n", firstDataSectorNumber);

}

// tests: getFirstSectorOfCluster()
// from: equations.c
void testGetFirstSectorOfCluster(int testNumber) {
uint32_t firstSectorOfCluster;
printf("Testing getFirstSectorOfCluster()...\n");
firstSectorOfCluster = getFirstSectorOfCluster(thisBootSector, 4);
printf("Expected Value = 2052; Actual Value = %d\n", firstSectorOfCluster);
}

// tests: getThisFATSectorNumber()
// from: equations.c
void testGetThisFATSectorNumber(int testNumber) {
uint32_t thisFATSectorNumber;
printf("Testing getThisFATSectorNumber()...\n");
thisFATSectorNumber = getThisFATSectorNumber(thisBootSector, 150);
printf("Expected Value = 33; Actual Value = %d\n", thisFATSectorNumber);

}

// tests: getThisFATEntryOffset()
// from: equations.c
void testGetThisFATEntryOffset(int testNumber) {
uint16_t thisFATEntryOffset;
printf("Testing getThisFATEntryOffset()...\n");
thisFATEntryOffset = getThisFATEntryOffset(thisBootSector, 150);
printf("Expected Value = 88; Actual Value = %d\n", thisFATEntryOffset);

}

// tests: ConvertSectorNumToBytes()
// from: equations.c
void testConvertSectorNumToBytes(int testNumber) {
uint64_t sectorNumberInBytes;
printf("Testing convertSectorNumToBytes()...\n");
sectorNumberInBytes = convertSectorNumToBytes(thisBootSector, 45);
printf("Expected Value = 23040; Actual Value = %d\n", sectorNumberInBytes);

}

void testConvertClusterNumToBytes(int testNumber) {
uint64_t clusterNumberInBytes;

printf("Testing convertClusterNumToBytes()...\n");
if (testNumber == 1) {
clusterNumberInBytes = convertClusterNumToBytes(thisBootSector, 403);
printf("Expected Value = 1254912; Actual Value = %d\n", clusterNumberInBytes);
}
else if (testNumber == 2) {
clusterNumberInBytes = convertClusterNumToBytes(thisBootSector, 2);
printf("Expected Value = 1049600; Actual Value = %d\n", clusterNumberInBytes);
}
}
@@ -0,0 +1,77 @@
///////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#include "fat_entries.h"

///////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES ///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
FILE* filePtr;
bootSector thisBootSector;

///////////////////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

// In: thisBootSector (bootSector (struct)), clusterNumber (uint32_t)
// Out: thisFatEntry (fatEntry (struct))
// Purpose: to create a fatEntry struct for convenienve in moving through clusters and to abstract cluster chain logic
// Notes:
void testGetFATEntry(int testNumber);

///////////////////////////////////////////////////////////////////////////
// MAIN PROGRAM ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {

if (argc != 2) {
printf("You entered an incorrect amount of arguments for this program. <USAGE: ./fat32_reader fat32.img>\n");
return -1;
}

if ((filePtr = fopen(argv[1], "rb")) == 0) {
perror(argv[1]);
return -1;
}

thisBootSector = setUpBootSector(filePtr);

testGetFATEntry(1);
testGetFATEntry(2);
testGetFATEntry(3);
testGetFATEntry(4);

return 0;
}

///////////////////////////////////////////////////////////////////////////
// FUNCTION DEFINITONS ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
void testGetFATEntry(int testNumber) {
FATEntry thisFATEntry;


printf("Testing getFATEntry()...\n");

if (testNumber == 1) {
printf("Running test 1...\n");
thisFATEntry = getFATEntry(filePtr, thisBootSector, 4);
printf("Cluster Number: 4 | Expected = 5; Actual = %d\n", thisFATEntry.nextClusterNumber);
}
else if (testNumber == 2) {
printf("Running test 2...\n");
thisFATEntry = getFATEntry(filePtr, thisBootSector, 13);
printf("Cluster Number: 13 | Expected = 14; Actual = %d\n", thisFATEntry.nextClusterNumber);
}
else if (testNumber == 3 ) {
printf("Running test 3...\n");
thisFATEntry = getFATEntry(filePtr, thisBootSector, 514);
printf("Cluster Number: 514 | Expected = 515; Actual = %d\n", thisFATEntry.nextClusterNumber);
}
else if (testNumber == 4) {
printf("Running test 4...\n");
thisFATEntry = getFATEntry(filePtr, thisBootSector, 2);
printf("Cluster Number: 2 | Expected = %d; Actual = %d\n", EOC, thisFATEntry.nextClusterNumber);
}
}