/** * stasher.c: A simple RAM file server in the internet domain * TCP using with port number starting from 9090 * * author: Bon Adriel Aseniero * base code provided by TA, Ali Abedi */ #include #include #include #include #include #include #include #include // Default port number #define def_port 9090 #define TOTAL_STORAGE_SIZE 1048576 #define MAX_FILE_SIZE 4096 #define MAX_FILE_NAME 256 typedef struct FileRecord FILE_RECORD; typedef struct FreeSpace FREE_SPACE; char mem_storage[TOTAL_STORAGE_SIZE]; // the storage structure acting as memory FILE_RECORD *StartFile = NULL; // the current head of the file linked list FILE_RECORD *CurrentFile = NULL; // the current tail of the file linked list FREE_SPACE *FirstFreeBlock = NULL; // the first Free Block of RAM FREE_SPACE *LastFreeBlock = NULL; // the last Free Block of RAM int num_of_files = 0; // the number of files in the system // the file in storage struct FileRecord { char fileName[MAX_FILE_NAME]; // File name char time[25]; // Time the file is stored int size; // the file size int views; // The number of times the file is viewed int start_addr; // The starting address of the file in memory int end_addr; // the ending address of the file in memory FILE_RECORD *nextFile; // the next file on the system }; // contains the free blocks of memory in the system struct FreeSpace { int start; int end; int totalFreeSpace; FREE_SPACE *nextFreeBlock; }; /** * Prints an error message and exits * char *msg a pointer to the char array containing the error message */ void error( char *msg ) { perror( msg ); exit( 1 ); } /** * Separates the file name form a given command string * returns 1 if there is a file name, 0 if not * stores the filename to the addressed pointed by fileName * string input */ int getFileName( char *cmd, char *fileName ) { int fileNameFound = 0; char tmp[256]; bzero( tmp, 256 ); if ( strncmp( cmd, "STASH", 5 ) == 0 ) { strcpy( tmp, &cmd[6] ); // assuming that the filename starts 6 chars into the string command int lastChar = getLastIndex( tmp ); strncpy( fileName, tmp, lastChar ); if ( ! fileName[0] == '\0' ) fileNameFound = 1; } else if ( strncmp( cmd, "VIEW", 4 ) == 0 ) { strcpy( tmp, &cmd[5] ); // assuming that the filename starts 6 chars into the string command int lastChar = getLastIndex( tmp ); strncpy( fileName, tmp, lastChar ); if ( ! fileName[0] == '\0' ) fileNameFound = 1; } else if ( strncmp( cmd, "REMOVE", 6 ) == 0 ) { strcpy( tmp, &cmd[7] ); // assuming that the filename starts 6 chars into the string command int lastChar = getLastIndex( tmp ); strncpy( fileName, tmp, lastChar ); if ( fileName[0] != '\0' || fileName[0] != '\n' ) fileNameFound = 1; } return fileNameFound; } /** * Returns the last index of the given array of chars * which is not a special Null terminating character or * new line character */ int getLastIndex( char *cmd ) { int i = 0; for ( i = 0; i < strlen( cmd ); ++i ) { if ( cmd[i] == '\n' || cmd[i] == '\0' ) break; } // printf( "Last Index at %d\n", i ); return i; } /** * Retrieves the file size of a given file name * */ long getFileSize( char *fileName ) { struct stat st; stat( fileName, &st ); printf( "size: %d\n", st.st_size ); return st.st_size; } /** * Finds an empty block in which a file with a given size can * fit in. Returns 1 if free space is found, 0 if not. */ int findSpace( int size, int *start, int *end ) { if ( size == 0 ) return 1; if ( FirstFreeBlock == NULL ) { // Create the first Free block FREE_SPACE *tmp = malloc( sizeof( FREE_SPACE ) ); tmp->start = size; tmp->end = TOTAL_STORAGE_SIZE; tmp->totalFreeSpace = TOTAL_STORAGE_SIZE - size; tmp->nextFreeBlock = NULL; FirstFreeBlock = tmp; LastFreeBlock = tmp; // set the start and end indexes of the free space to be taken up *start = 0; *end = size-1; printf( "NULL firstBlock. Space found at start %d to %d\n", start, end ); return 1; } else { // No free space available if ( FirstFreeBlock->totalFreeSpace == 0 ) { return 0; } // If the free space will be taken up else if ( size == FirstFreeBlock->totalFreeSpace ) { *start = FirstFreeBlock->start; *end = FirstFreeBlock->end; printf( "Size == . Space found at start %d to %d\n", *start, *end ); if ( FirstFreeBlock->nextFreeBlock != NULL ) FirstFreeBlock = FirstFreeBlock->nextFreeBlock; else { FirstFreeBlock->start = 0; FirstFreeBlock->end = 0; FirstFreeBlock->totalFreeSpace = 0; } return 1; } else if ( size < FirstFreeBlock->totalFreeSpace ) { *start = FirstFreeBlock->start; *end = *start + size; printf( "Size < . Space found at start %d to %d\n", *start, *end ); FirstFreeBlock->start = *end; FirstFreeBlock->totalFreeSpace = ( FirstFreeBlock->totalFreeSpace ) - size; return 1; } else if ( size > FirstFreeBlock->totalFreeSpace ) { printf( "Size > . Space not found... moving on...\n" ); while ( FirstFreeBlock->nextFreeBlock != NULL ) { LastFreeBlock = FirstFreeBlock; FirstFreeBlock = FirstFreeBlock->nextFreeBlock; LastFreeBlock->nextFreeBlock = NULL; int ret = findSpace( size, &start, &end ); if ( ret == 1 ) return 1; } return 0; } } } /** * Searches for a file Name in the file system. * returns 1 if found, 0 if not */ int searchFileName( char *fileName ) { FILE_RECORD *tmp = StartFile; while ( tmp != NULL ) { if ( strcmp( fileName, tmp->fileName ) == 0 ) return 1; else tmp = tmp->nextFile; } return 0; } /** * Searches for a file in the file system. * returns it if found, NULL if not */ FILE_RECORD* getFile( char *fileName ) { FILE_RECORD *tmp = StartFile; while ( tmp != NULL ) { if ( strcmp( fileName, tmp->fileName ) == 0 ) return tmp; else tmp = tmp->nextFile; } return NULL; } /** * Opens a given file name and stores it into the RAM file server * returns a message whether the file has been stashed or not */ char* stash( char *fileName ) { FILE *file; if ( searchFileName( fileName ) == 1 ) { return "File already in system\n"; } // Check if file exists if ( ( file = fopen( fileName, "r" ) ) == NULL ) { printf( "File Not Found...\n" ); return "File Not Found\n"; } // Check if the file is within the limit file size int fileSize = getFileSize( fileName ); if ( fileSize > MAX_FILE_SIZE ) { char* msg = "Max File Size reached! File not stashed.\n"; printf( msg ); return( msg ); } // Create a record for the file FILE_RECORD *newFile; newFile = malloc( sizeof( FILE_RECORD ) ); bzero( newFile->fileName, MAX_FILE_NAME ); // Store the file name strncpy( newFile->fileName, fileName, strlen( fileName ) ); newFile->size = fileSize; // Store the file size bzero( newFile->time, 25 ); // Retrieve current time and store it time_t now; time( &now ); char *curTime = ctime( &now ); strncpy( newFile->time, curTime, strlen( curTime ) ); // Store the contents of the file into the RAM Storage int start, end; int isFound = findSpace( fileSize, &start, &end ); printf( "Start: %d\tEnd: %d\n", start, end ); if ( ! isFound ) { return "No space in RAM system\n"; } newFile->start_addr = start; newFile->end_addr = end; int i = start; char byte; while( ( byte = fgetc( file ) ) != EOF ) { mem_storage[i] = byte; ++i; } // Append the new record into the Linked List if ( StartFile == NULL ) { StartFile = newFile; CurrentFile = newFile; StartFile->nextFile = NULL; CurrentFile->nextFile = NULL; } else { CurrentFile->nextFile = newFile; CurrentFile = newFile; } ++num_of_files; return "File Successfully stashed in memory.\n"; } /** * Opens the file in memory and displays it on the screen. * Returns the contents of the file. */ char* view( char *fileName ) { // See if the file is in the system if ( searchFileName( fileName ) == 0 ) return "File Not Found.\n"; char buffer[TOTAL_STORAGE_SIZE]; bzero( buffer, TOTAL_STORAGE_SIZE ); FILE_RECORD *file = getFile( fileName ); if ( file->size == 0 ) return "\n"; // Read the contents of the file in Memory int i, j; for ( i = file->start_addr, j = 0; i < file->end_addr; ++i, ++j ) { buffer[j] = mem_storage[i]; } // increase the number of views file->views = ++( file->views ); return buffer; } /** * Deletes a file in the file system */ char* remove_file( char *fileName ) { // See if the File is in the system if ( searchFileName( fileName ) == 0 ) return "File Not Found.\n"; // If it is then Find it and delete it from the link FILE_RECORD *delete = NULL; FILE_RECORD *previous = NULL; FILE_RECORD *current = StartFile; while ( current != NULL ) { if ( strcmp( current->fileName, fileName ) == 0 ) { // if it's the start file if ( previous == NULL ) { delete = current; StartFile = current->nextFile; } else { delete = current; previous->nextFile = current->nextFile; current = current->nextFile; } break; } else { previous = current; current = current->nextFile; } } // once find, mark its location in memory as free space if ( delete != NULL ) { FREE_SPACE *newFreeSpace = malloc( sizeof( FREE_SPACE ) ); newFreeSpace->start = delete->start_addr; newFreeSpace->end = delete->end_addr; newFreeSpace->totalFreeSpace = delete->size; newFreeSpace->nextFreeBlock = FirstFreeBlock; FirstFreeBlock = newFreeSpace; } --num_of_files; return "File has been deleted successfully.\n"; } /** * Lists all of the files in the file system and their information * on the screen */ char* list() { char numFiles[TOTAL_STORAGE_SIZE]; sprintf( numFiles, "%d", num_of_files ); char *list = " FILES IN THE SYSTEM\n[filename]\t[size]\t[views]\t[date stored]\n"; strcat( numFiles, list ); if ( num_of_files == 0 ) return numFiles; // Retrieve all of the Files in the system and take the needed info // for displaying on screen FILE_RECORD *tmp = StartFile; int i; for ( i = 0; i < num_of_files; ++i ) { char size[1000]; char views[1000]; char format[10]; strcat( numFiles, tmp->fileName ); sprintf( format, "%-5s", "" ); strcat( numFiles, format ); sprintf( size, "\t%-5d", tmp->size ); strcat( numFiles, size ); sprintf( views, "\t%d\t", tmp->views ); strcat( numFiles, views ); strcat( numFiles, tmp->time ); if ( tmp->nextFile != NULL ) tmp = tmp->nextFile; } return numFiles; } /** * Evaluates the command received and returns the appropriate message */ char* processRequest( char *buffer ) { char fileName[256]; char out_buffer[TOTAL_STORAGE_SIZE]; bzero( out_buffer, MAX_FILE_SIZE ); // Evaluate the command if ( strncmp( buffer, "STASH", 5 ) == 0 ) { bzero( fileName, 256 ); if ( getFileName( buffer, fileName ) ) { printf( "Stashing %s...\n", fileName ); strcpy( out_buffer, stash( fileName ) ); } else strcpy( out_buffer, "File Name Error\n\n" ); } else if ( strncmp( buffer, "LIST", 4 ) == 0 ) { printf( "Listing Files...\n" ); strcpy( out_buffer, list() ); } else if ( strncmp( buffer, "VIEW", 4 ) == 0 ) { bzero( fileName, 256 ); if ( getFileName( buffer, fileName ) ) { printf( "Viewing %s...\n", fileName ); strcpy( out_buffer, view( fileName ) ); } else strcpy( out_buffer, "File Name Error\n\n" ); } else if ( strncmp( buffer, "REMOVE", 6 ) == 0 ) { bzero( fileName, 256 ); if ( getFileName( buffer, fileName ) ) { printf( "Removing %s...\n", fileName ); strcpy( out_buffer, remove_file( fileName ) ); } else strcpy( out_buffer, "File Name Error\n\n" ); } return out_buffer; } int main(int argc, char *argv[]) { int sockfd, newsockfd, portno, clilen; char buffer[256]; char outBuffer[TOTAL_STORAGE_SIZE]; struct sockaddr_in serv_addr, cli_addr; int n; // some socket programming provided by TA portno = def_port; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) error( "ERROR opening socket" ); bzero( (char *) &serv_addr, sizeof( serv_addr ) ); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; // Find a suitable port number starting from port number 9090 while ( 1 ) { serv_addr.sin_port = htons( portno ); if ( bind( sockfd, (struct sockaddr *) &serv_addr, sizeof( serv_addr ) ) < 0 ) { printf( "ERROR on binding, finding different port\n" ); ++portno; } else break; } printf( "Port in use: %d\n\n", portno ); // clear the memory bzero( mem_storage, TOTAL_STORAGE_SIZE ); // While loop ensuring the server will run forever until // closed forcibly by a user using the EXIT command int terminate = 0; while ( !terminate ) { // listen for connection from client listen( sockfd, 5 ); clilen = sizeof( cli_addr ); newsockfd = accept( sockfd, (struct sockaddr *) &cli_addr, &clilen ); if ( newsockfd < 0 ) error( "ERROR on accept" ); // Inner While loop ensuring the server will process all // commands sent by a client until the client disconnects // (via DONE command) or closes the server (EXIT command) while ( 1 ) { bzero( buffer, 256 ); bzero( outBuffer, MAX_FILE_SIZE ); n = read( newsockfd, buffer, 255 ); if ( n < 0 ) error( "ERROR reading from socket" ); printf( "Command received: %s\n", buffer ); if ( strncmp( buffer, "EXIT", 4 ) == 0 ) { terminate = 1; break; } else if ( strncmp( buffer, "DONE", 4 ) == 0 ) break; else strcpy( outBuffer, processRequest( buffer ) ); n = write( newsockfd, outBuffer, strlen( outBuffer ) ); if ( n < 0 ) error( "ERROR writing to socket" ); } } // Display a closing message that the server is closing printf( "Server closed; Good bye!\n" ); return 0; }