@@ -15,6 +15,7 @@
#include <stdint.h>
#include <unistd.h>
#include <math.h>
#include "directory_entries.h"

//////////////////////////////////////////////////////
// MACROS ////////////////////////////////////////////
@@ -44,63 +45,9 @@
#define TS_OFFSET 32 // total sectors
#define TS_SIZE 4

// macros for directory entry data structure
#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

// general system macros
#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_DIRNAME_SIZE+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;

//////////////////////////////////////////////////////////
// GLOBAL VARIABLES //////////////////////////////////////
@@ -134,10 +81,6 @@ uint32_t getFirstSectorOfCluster(uint32_t numOfCluster);
uint32_t getThisFATSectorNumber(uint32_t numOfCluster);
uint16_t getThisFATEntryOffset(uint32_t numOfCluster);
uint64_t convertSectorNumToBytes(uint32_t sectorNumber);
fileData getFileData(uint64_t byteAddress);

date convertToDateStruct(uint16_t machineRepresentation);
time convertToTimeStruct(uint16_t machineRepresentation);

/* This is the main function of your project, and it will be run
* first before all other functions.
@@ -258,7 +201,7 @@ int main(int argc, char *argv[])

}

/* Close the file/
/* Close the file */
fclose(filePtr);

return 0; /* Success */
@@ -306,18 +249,16 @@ void printVolumeName() {

while (sectorByteCounter < bytesPerSector) {
// jumps to starting byte address of each directory entry
thisFileData = getFileData(byteAddress);
thisFileData.fileName[SH_DIRNAME_SIZE] = '\0';
thisFileData = getFileData(filePtr, byteAddress);
thisFileData.fileName[SH_FILE_NAME] = '\0';

if ((thisFileData.fileAttributes & 8) == 8) {
if (isVolumeLabel(thisFileData)) {
printf("%s\n", thisFileData.fileName);
}

byteAddress += DIR_ENTRY_SIZE;
sectorByteCounter += DIR_ENTRY_SIZE;
}


}

// In:
@@ -348,14 +289,13 @@ void changeDirectory(char* directory ) {
byteAddress = currentDirectory;

// get file entry data structure at byte adress
thisFileData = getFileData(byteAddress);
thisFileData = getFileData(filePtr, byteAddress);

// while the first character of the fileName in file entry structure is not '\0'
while (thisFileData.fileName[0] != '\0') {
while (!isEndOfDirectory(thisFileData)) {

// print the fileName
if (thisFileData.fileName[0] != -27) {
//TODO: Why is there a magic humber?!?!?!?!?!
if (!isEmptyDirectoryEntry(thisFileData)) {
if (strncmp(thisFileData.fileName, directory, strlen(directory) - 2) == 0)
{
currentDirectory = thisFileData.firstClusterNumHI;
@@ -372,7 +312,7 @@ void changeDirectory(char* directory ) {
byteAddress += 64;

// get file entry data structure at new byte address
thisFileData = getFileData(byteAddress);
thisFileData = getFileData(filePtr, byteAddress);
}

if (cwdSet==0)
@@ -395,22 +335,21 @@ void listFiles() {
byteAddress = currentDirectory;

// get file entry data structure at byte adress
thisFileData = getFileData(byteAddress);
thisFileData = getFileData(filePtr, byteAddress);

// while the first character of the fileName in file entry structure is not '\0'
while (thisFileData.fileName[0] != '\0') {
while (!isEndOfDirectory(thisFileData)) {

// print the fileName
if (thisFileData.fileName[0] != -27) {
//TODO: Why is there a magic number??!?!?!?!?
if (!isEmptyDirectoryEntry(thisFileData) && !isVolumeLabel(thisFileData) && !isLongName(thisFileData)) {
printf("%s\n", thisFileData.fileName);
}

// get new byte address
byteAddress += 64;
byteAddress += 32;

// get file entry data structure at new byte address
thisFileData = getFileData(byteAddress);
thisFileData = getFileData(filePtr, byteAddress);
}
}

@@ -466,135 +405,6 @@ uint16_t getThisFATEntryOffset(uint32_t numOfCluster) {
return (fatOffset % bytesPerSector);
}

// 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(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;
}

// 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) {
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;
}

// 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) {
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;
}

uint64_t convertSectorNumToBytes(uint32_t sectorNumber) {
return sectorNumber * bytesPerSector;
}
@@ -0,0 +1,3 @@
#!/bin/bash
gcc -o fat32_reader fat32_reader.c directory_entries.c
./fat32_reader fat32.img
@@ -8,7 +8,7 @@
/////////////////////////////////////////////////////////////
// MACROS ///////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
#define ENTRIES_COUNT 5
#define ENTRIES_COUNT 6

//////////////////////////////////////////////////////////////
// FUNCTION DECLARATIONS /////////////////////////////////////
@@ -17,7 +17,7 @@ void testEndOfDirectory(int testNumber);
void setUpSystem();
void testSetUp();
void testIsEmptyDirectoryEntry(int testNumber);
void testHasLongName(int testNumber);
void testIsLongName(int testNumber);
void testIsDirectory(int testNumber);
void testIsFile(int testNumber);
void testIsVolumeLabel(int testNumber);
@@ -32,15 +32,21 @@ fileData directoryEntry_2;
fileData directoryEntry_3;
fileData directoryEntry_4;
fileData directoryEntry_5;
fileData directoryEntry_6;

fileData system[ENTRIES_COUNT];

//////////////////////////////////////////////////////////////
// MAIN PROGRAM //////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int main() {
setUpSystem();
testIsReadOnly(1);
setUpSystem();
testIsReadOnly(2);
testIsFile(3);
testIsDirectory(5);
testIsEmptyDirectoryEntry(5);
testIsVolumeLabel(1);
testIsLongName(2);
}

//////////////////////////////////////////////////////////////
@@ -77,7 +83,7 @@ void setUpSystem() {
// initialize directoryEntry_3
char fileName3[SH_FILE_NAME + 1] = "GHOST.TXT";
strcpy(directoryEntry_3.fileName, fileName3);
directoryEntry_3.fileName[0] = 5;
directoryEntry_3.fileName[0] = -27;
directoryEntry_3.fileAttributes = 32; // 00100000
directoryEntry_3.createTime = 0;
directoryEntry_3.createDate = 0;
@@ -113,12 +119,26 @@ void setUpSystem() {
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()
@@ -127,6 +147,7 @@ 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);
@@ -147,7 +168,8 @@ void testSetUp() {
// 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");
@@ -159,21 +181,23 @@ void testEndOfDirectory(int testNumber) {
}

void testIsDirectory(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 1, 0, 0, 0};
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};
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);
@@ -182,10 +206,11 @@ void testIsFile(int testNumber) {
}

void testIsEmptyDirectoryEntry(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 1, 0, 0};
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);
@@ -194,10 +219,12 @@ void testIsEmptyDirectoryEntry(int testNumber) {
}

void testIsReadOnly(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {0, 0, 0, 1, 0};
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);
@@ -206,15 +233,27 @@ void testIsReadOnly(int testNumber) {
}

void testIsVolumeLabel(int testNumber) {
int expectedValues[ENTRIES_COUNT] = {1, 0, 0, 0, 0};
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);
}
}