User space device driver implementation

alex_liu
hdd_file_io.c

// Includes #include <malloc.h> #include <string.h>   // Project Includes #include <hdd_file_io.h> #include <hdd_driver.h> #include <cmpsc311_log.h> #include <cmpsc311_util.h>   // Defines (you can ignore these) #define MAX_HDD_FILEDESCR 1024 #define HIO_UNIT_TEST_MAX_WRITE_SIZE 1024 #define HDD_IO_UNIT_TEST_ITERATIONS 10240   // Student Defines #define NUM_ENTRIES 5 //Let max number of open files be 5 for now #define ISOLATE_RESULT 2147483648 //This represents 1 followed by 32 0's. Used to isolate R     // Type for UNIT test interface (you can ignore this) typedef enum {     HIO_UNIT_TEST_READ   = 0,     HIO_UNIT_TEST_WRITE  = 1,     HIO_UNIT_TEST_APPEND = 2,     HIO_UNIT_TEST_SEEK   = 3, } HDD_UNIT_TEST_TYPE;     // Struct to store file metadata struct myFile {     int16_t handle;     char *path;     int32_t seekPos;     int32_t blockID; };   // For this first assignment, we will assume no more then 5 files will be open at a time struct myFile openFiles[NUM_ENTRIES];   //This array will store 0 for an available entry, 1 for filled, //allowing us to keep track of what handles are available int filledLocation[NUM_ENTRIES] = {0,0,0,0,0};   //Set to 1 when program starts, in order to initialize device only once int programStarted = 0;   // // Implementation   //////////////////////////////////////////////////////////////////////////////// // // Function     : generateHDDCommand // Description  : Given parameters for hdd_data_lane, prepares the command to be sent // // Inputs       : Opcode, Block Size, Block ID // Outputs      : HddBitCmd //   int64_t generateHDDCommand(int opCode,int blockSize, int blockID) {     HddBitCmd sendCommand = 0;     //Using bitwise operators, piece together the command into the     //format described in hdd_driver.h     sendCommand = sendCommand + opCode;     sendCommand = sendCommand << 26;     sendCommand = sendCommand + blockSize;     sendCommand = sendCommand << 4;     sendCommand = sendCommand << 32;     sendCommand = sendCommand + blockID;       return sendCommand; }   //////////////////////////////////////////////////////////////////////////////// // // Function     : hdd_open // Description  : Opens a file, assigns a file handle for use in later functions // // Inputs       : Pointer for the file path // Outputs      : A file handle, or -1 if file already opened // int16_t hdd_open(char *path) {     //If HDD has not been initialized, attempt to initialize it now     if (programStarted == 0) {         if (hdd_initialize() == -1) {             //Can't initialize!             return -1;         } else {             //Initialization successful. Set the boolean to true so it does not try again             programStarted = 1;         }     }     //First check if file is open by checking matches with currently open files     for (int i = 0; i < NUM_ENTRIES; ++i) {         if (filledLocation[i] == 1) {             if (strcmp((openFiles[i].path),path) == 0) {                 //File already opened!                 return -1;             }         } else {             //File will be opened. handle will be array index             //Block ID of -1 will indicate that the file has not been opened             filledLocation[i] = 1;             openFiles[i].handle = i;             (openFiles[i].path) = path;             openFiles[i].seekPos = 0;             openFiles[i].blockID = -1;               //Return handle (array index)             return i;                     }     }     //If all file spots full, return -1. Should not happen in this program.     return -1; }   //////////////////////////////////////////////////////////////////////////////// // // Function     : hdd_close // Description  : Closes the file associated with the file handle, deletes all related block contents // // Inputs       : File handle integer // Outputs      : 0 if successful, -1 if unsuccessful // int16_t hdd_close(int16_t fh) {     //Make sure file is open     if (filledLocation[fh] == 0) {         //File not open!         return -1;     }       //Delete the block associated with the file handle     if (hdd_delete_block(openFiles[fh].blockID) == -1) {         return -1;     }       //Allow that location to be rewritten     filledLocation[fh] = 0;     openFiles[fh].blockID = 0;     openFiles[fh].handle = 0;     openFiles[fh].path = "";     openFiles[fh].seekPos = 0;       return 0; }   //////////////////////////////////////////////////////////////////////////////// // // Function     : hdd_read // Description  : Reads a count number of bytes from the current seek position //                for a file handle fh into the buffer data // // Inputs       : File handle, data buffer, count number of bytes to read // Outputs      : Number of bytes read, or -1 if unsuccessful // int32_t hdd_read(int16_t fh, void * data, int32_t count) {     // Make sure file is open     if (filledLocation[fh] == 0) {         //File not open!         return -1;     }     //Create variable for HDD return command     HddBitResp returnCommand = 0;     int64_t result = 0;     int32_t blockSize = hdd_read_block_size(openFiles[fh].blockID);     int32_t blockSeek = openFiles[fh].seekPos;       //Create new character array to use as temporary buffer     char *blockData = malloc(blockSize);       //Using helper function, call data lane using our read parameters     returnCommand = hdd_data_lane(generateHDDCommand(HDD_BLOCK_READ,blockSize,openFiles[fh].blockID),blockData);       result = returnCommand & ISOLATE_RESULT;     result = result >> 32;       if (result == 0) {           if (count > blockSize - blockSeek) {             //Attempt to read past the end! Read only what is available             memcpy(data,&blockData[blockSeek],blockSize-blockSeek);               //Update seek, free memory, and return bytes read             openFiles[fh].seekPos = blockSize;             free(blockData);             return (blockSize - blockSeek);         } else {             //Read count number of bytes             memcpy(data,&blockData[blockSeek],count);               //Update seek, free memory, return count bytes             openFiles[fh].seekPos += count;             free(blockData);             return count;         }       } else {         //Could not read block!         free(blockData);         return -1;     } }   //////////////////////////////////////////////////////////////////////////////// // // Function     : hdd_write // Description  : Writes count bytes of data buffer into block // // Inputs       : File handle, data buffer, and count number of bytes to write // Outputs      : Count number of written bytes if successful, -1 if unsuccessful // int32_t hdd_write(int16_t fh, void *data, int32_t count) {     // Make sure file is open     if (filledLocation[fh] == 0) {         //File not open!         return -1;     }       // Create a variable for the HDD send command and response.     HddBitResp returnCommand = 0;     int64_t result = 0;       //Two cases:       //Case 1: Block does not exist     if (openFiles[fh].blockID == -1) {           //Create block. Block size is count number of bytes, and 0 for ID, since it does not yet exist         returnCommand = hdd_data_lane(generateHDDCommand(HDD_BLOCK_CREATE,count,0),data);           //Next we need to isolate the result:         result = returnCommand & ISOLATE_RESULT;         result = result >> 32;           if (result == -1) {             //Could not create block             return -1;         }           //Offset our data by the current seek position         openFiles[fh].blockID = (int32_t)returnCommand;         openFiles[fh].seekPos += count;       //Case 2: Block has already been created     } else  {         int64_t blockSize = hdd_read_block_size(openFiles[fh].blockID);         int64_t fileSeek = openFiles[fh].seekPos;           //Need a block-sized buffer to store the current block data         char *blockData = malloc(blockSize);           //Generate and send read command with block size and block ID. Read into our new buffer         returnCommand = hdd_data_lane(generateHDDCommand(HDD_BLOCK_READ,blockSize,openFiles[fh].blockID),blockData);         result = returnCommand & ISOLATE_RESULT;         result = result >> 32;           if (result == -1) {             //Could not read!             return -1;         }         if ((count+openFiles[fh].seekPos) > blockSize) {             //Need a new buffer for all of the combined data             char *newData = malloc(fileSeek + count);             //Copy this block data into the new data block             memcpy(newData, blockData, blockSize);               //Finally, copy the count bytes of the old buffer into this new buffer             memcpy (&newData[fileSeek],data,count);               //Now, we create the new block with combined block size of count bytes + seek position             returnCommand = hdd_data_lane(generateHDDCommand(HDD_BLOCK_CREATE,(count+fileSeek),0),newData);             result = returnCommand & ISOLATE_RESULT;             result = result >> 32;               if (result == -1) {                 //Could not create block!                 return -1;             }               //Finally, delete the old block             if(hdd_delete_block(openFiles[fh].blockID) == -1) {                 //Could not delete old block!                 return -1;             }               openFiles[fh].blockID = (int32_t)returnCommand;             openFiles[fh].seekPos += count;               free(blockData);             free(newData);           } else {             //Current block size is large enough, but overwrite             //will rewrite entire block. Need to prepare a new data buffer               //Copy the data buffer to appropriate seek position in new buffer             memcpy(&blockData[fileSeek],data,count);               //Generate and send the overwrite command using block size and block ID,             //using our newly updated block data buffer             returnCommand = hdd_data_lane(generateHDDCommand(HDD_BLOCK_OVERWRITE,blockSize,openFiles[fh].blockID),blockData);               //Next we need to isolate the result:             result = returnCommand & ISOLATE_RESULT;             result = result >> 32;                         free(blockData);             if (result == -1) {                 //Overwrite error!                 return -1;             }             //Update seek             openFiles[fh].seekPos += count;         }     }     return count; }   //////////////////////////////////////////////////////////////////////////////// // // Function     : hdd_seek // Description  : Moves the current seek position of an opened file to //                position loc // // Inputs       : File handle, new seek location // Outputs      : -1 if out of range or file not open, 0 if successful // int32_t hdd_seek(int16_t fh, uint32_t loc) {     //Make sure file is open     if (filledLocation[fh] == 0) {         //File not open!         return -1;     }       if (loc > hdd_read_block_size(openFiles[fh].blockID)) {     //Out of range!!         return -1;     }     //Update seek position     openFiles[fh].seekPos = loc;     return 0; }         //////////////////////////////////////////////////////////////////////////////// // // Function     : hddIOUnitTest // Description  : Perform a test of the HDD IO implementation // // Inputs       : None // Outputs      : 0 if successful or -1 if failure // // STUDENTS DO NOT MODIFY CODE BELOW UNLESS TOLD BY TA AND/OR INSTRUCTOR // int hddIOUnitTest(void) {       // Local variables     uint8_t ch;     int16_t fh, i;     int32_t cio_utest_length, cio_utest_position, count, bytes, expected;     char *cio_utest_buffer, *tbuf;     HDD_UNIT_TEST_TYPE cmd;     char lstr[1024];       // Setup some operating buffers, zero out the mirrored file contents     cio_utest_buffer = malloc(HDD_MAX_BLOCK_SIZE);     tbuf = malloc(HDD_MAX_BLOCK_SIZE);     memset(cio_utest_buffer, 0x0, HDD_MAX_BLOCK_SIZE);     cio_utest_length = 0;     cio_utest_position = 0;       // Start by opening a file     fh = hdd_open("temp_file.txt");     if (fh == -1) {         logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : Failure open operation.");         return(-1);     }       // Now do a bunch of operations     for (i=0; i<HDD_IO_UNIT_TEST_ITERATIONS; i++) {           // Pick a random sendCommand         if (cio_utest_length == 0) {             cmd = HIO_UNIT_TEST_WRITE;         } else {             cmd = getRandomValue(HIO_UNIT_TEST_READ, HIO_UNIT_TEST_SEEK);         }           // Execute the sendCommand         switch (cmd) {           case HIO_UNIT_TEST_READ: // read a random set of data             count = getRandomValue(0, cio_utest_length);             logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : read %d at position %d", count, cio_utest_position);             bytes = hdd_read(fh, tbuf, count);             if (bytes == -1) {                 logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : Read failure.");                 return(-1);             }               // Compare to what we expected             if (cio_utest_position+count > cio_utest_length) {                 expected = cio_utest_length-cio_utest_position;             } else {                 expected = count;             }             if (bytes != expected) {                 logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : short/long read of [%d!=%d]", bytes, expected);                 return(-1);             }             if ( (bytes > 0) && (memcmp(&cio_utest_buffer[cio_utest_position], tbuf, bytes)) ) {                   bufToString((unsigned char *)tbuf, bytes, (unsigned char *)lstr, 1024 );                 logMessage(LOG_INFO_LEVEL, "HIO_UTEST R: %s", lstr);                 bufToString((unsigned char *)&cio_utest_buffer[cio_utest_position], bytes, (unsigned char *)lstr, 1024 );                 logMessage(LOG_INFO_LEVEL, "HIO_UTEST U: %s", lstr);                   logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : read data mismatch (%d)", bytes);                 return(-1);             }             logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : read %d match", bytes);                 // update the position pointer             cio_utest_position += bytes;             break;           case HIO_UNIT_TEST_APPEND: // Append data onto the end of the file             // Create random block, check to make sure that the write is not too large             ch = getRandomValue(0, 0xff);             count =  getRandomValue(1, HIO_UNIT_TEST_MAX_WRITE_SIZE);             if (cio_utest_length+count < HDD_MAX_BLOCK_SIZE) {                   // Log, seek to end of file, create random value                 logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : append of %d bytes [%x]", count, ch);                 logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : seek to position %d", cio_utest_length);                 if (hdd_seek(fh, cio_utest_length)) {                     logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : seek failed [%d].", cio_utest_length);                     return(-1);                 }                 cio_utest_position = cio_utest_length;                 memset(&cio_utest_buffer[cio_utest_position], ch, count);                   // Now write                 bytes = hdd_write(fh, &cio_utest_buffer[cio_utest_position], count);                 if (bytes != count) {                     logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : append failed [%d].", count);                     return(-1);                 }                 cio_utest_length = cio_utest_position += bytes;             }             break;           case HIO_UNIT_TEST_WRITE: // Write random block to the file             ch = getRandomValue(0, 0xff);             count =  getRandomValue(1, HIO_UNIT_TEST_MAX_WRITE_SIZE);             // Check to make sure that the write is not too large             if (cio_utest_length+count < HDD_MAX_BLOCK_SIZE) {                 // Log the write, perform it                 logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : write of %d bytes [%x]", count, ch);                 memset(&cio_utest_buffer[cio_utest_position], ch, count);                 bytes = hdd_write(fh, &cio_utest_buffer[cio_utest_position], count);                 if (bytes!=count) {                     logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : write failed [%d].", count);                     return(-1);                 }                 cio_utest_position += bytes;                 if (cio_utest_position > cio_utest_length) {                     cio_utest_length = cio_utest_position;                 }             }             break;           case HIO_UNIT_TEST_SEEK:             count = getRandomValue(0, cio_utest_length);             logMessage(LOG_INFO_LEVEL, "HDD_IO_UNIT_TEST : seek to position %d", count);             if (hdd_seek(fh, count)) {                 logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : seek failed [%d].", count);                 return(-1);             }             cio_utest_position = count;             break;           default: // This should never happen             CMPSC_ASSERT0(0, "HDD_IO_UNIT_TEST : illegal test sendCommand.");             break;           }     }       // Close the files and cleanup buffers, assert on failure     if (hdd_close(fh)) {         logMessage(LOG_ERROR_LEVEL, "HDD_IO_UNIT_TEST : Failure read comparison block.", fh);         return(-1);     }     free(cio_utest_buffer);     free(tbuf);       // Return successfully     return(0); }