· 6 years ago · Apr 24, 2019, 11:40 PM
1
2/*
3 * Program: Assignment 7
4 *
5 * Author: Ben Lane
6 *
7 * zID: z1806979
8 *
9 * Due Date: 4/25/2019
10 *
11 * Purpose: Simulate a FAT-12 file system.
12 */
13#include <fstream>
14#include <string>
15#include <sstream>
16#include <vector>
17#include <algorithm>
18#include <iterator>
19
20#define BLOCK_SIZE 512
21#define NUMBER_OF_FAT_ENTRIES 240
22#define DIRECTORY_BLOCK_MAX 12
23#define NUMBER_OF_ENTRIES_PER_LINE 12
24#define HOWOFTEN 6
25
26using namespace std;
27
28// A single file entry in the directory.
29struct Entry {
30 string fileName = "";
31 int fileSize = 0;
32 int firstBlock = -1;
33};
34
35vector<int> fatTable(4096, 0); // File table.
36vector<Entry> directoryList = vector<Entry>(); // List of directories.
37
38// Function prototypes.
39int newFile(const string&, int);
40int deleteFile(const string&);
41int modifyFile(const string&, int);
42int renameFile(const string&, const string&);
43int copyFile(const string&, const string&);
44void printFAT();
45void printDirectories();
46
47int main() {
48 ifstream inFile; // Data file to be read.
49 string dataLine; // A single line from data file.
50 string actionType, fileName, fileSize; // dataLine tokens.
51 string oldFileName, newFileName; // Used for renaming files.
52
53 int returnValue;
54
55 // Put initial files (directories) in system.
56 newFile(".", 512);
57 newFile("..", 0);
58
59 printDirectories();
60 printFAT();
61
62 // Open data file.
63 inFile.open("../data7.txt");
64
65 if (!inFile) {
66 fprintf(stderr, "Could not open file. Exiting.");
67 exit(0);
68 }
69
70 int fileReadIteration = 0;
71
72 // While file still has data, read it in and parse it.
73 while(getline(inFile, dataLine)) {
74 // Convert dataLine to an input stream.
75 istringstream dataLineStream(dataLine);
76
77 // Decide which action to take according to the first letter of the dataLine.
78 switch(dataLine[0]) {
79 case 'N':
80 fprintf(stderr, "Transaction: Add a new file.\n");
81
82 // Parse in action type, file name, and fileSize.
83 dataLineStream >> actionType >> fileName >> fileSize;
84
85 // Add file to file system.
86 returnValue = newFile(fileName, stoi(fileSize));
87
88 if (returnValue) {
89 fprintf(stderr, "Successfully added a new file, %s, of size %s\n", fileName.c_str(), fileSize.c_str());
90
91 // When root directory entry gets MORE than 12 files, add a block.
92 if (directoryList.size() % DIRECTORY_BLOCK_MAX == 1) {
93 // Start at first block of directory list.
94 for (unsigned i = 0; i < directoryList.size(); i = fatTable[i]) {
95 // Look for last block for root file.
96 if (fatTable[i] == -1) {
97 // Search for next free block.
98 for (unsigned j = i; i < fatTable.size(); j++) {
99 // If block is free, use it. Make it the terminating block.
100 if (fatTable[j] == 0) {
101 // Set new-found free block's value to -1, marking it as last block.
102 fatTable[j] = -1;
103
104 // Set the value of previous -1 block to the index value of the new -1 block.
105 fatTable[i] = j;
106
107 break;
108 }
109 }
110 }
111 }
112
113 directoryList[0].fileSize += BLOCK_SIZE;
114 }
115 } else {
116 fprintf(stderr, "Could not add a new file, %s, of size %s\n", fileName.c_str(), fileSize.c_str());
117 }
118
119 break;
120 case 'D':
121 fprintf(stderr, "Transaction: Delete a file.\n");
122
123 // Parse in action type, file name, and fileSize.
124 dataLineStream >> actionType >> fileName;
125
126 returnValue = deleteFile(fileName);
127
128 if (returnValue)
129 fprintf(stderr, "Successfully deleted a file, %s\n", fileName.c_str());
130
131 break;
132 case 'M':
133 fprintf(stderr, "Transaction: Modify a file.\n");
134
135 // Parse in action type, file name, and fileSize.
136 dataLineStream >> actionType >> fileName >> fileSize;
137
138 // Modify file.
139 returnValue = modifyFile(fileName, stoi(fileSize));
140
141 // If modifyFile returned success, then tell user!
142 if (returnValue)
143 fprintf(stderr, "Successfully modified a file, %s\n", fileName.c_str());
144
145 break;
146 case 'R':
147 fprintf(stderr, "Transaction: Rename a file.\n");
148
149 // Parse in action type, file name to change, and the new name the file will have.
150 dataLineStream >> actionType >> oldFileName >> newFileName;
151
152 returnValue = renameFile(oldFileName, newFileName);
153
154 if (returnValue)
155 fprintf(stderr, "Successfully changed the file name %s to %s.\n", oldFileName.c_str(), newFileName.c_str());
156
157 break;
158 case 'C':
159 fprintf(stderr, "Transaction: Copy a file.\n");
160
161 // Parse in action type, file name to change, and the new name the file will have.
162 dataLineStream >> actionType >> oldFileName >> newFileName;
163
164 returnValue = copyFile(oldFileName, newFileName);
165
166 if (returnValue)
167 fprintf(stderr, "Successfully copied an existing file, %s, to a new file, %s\n", oldFileName.c_str(), newFileName.c_str());
168
169 break;
170 }
171
172 fileReadIteration++;
173
174 // Print out information once every HOWOFTEN transactions.
175 if (fileReadIteration % HOWOFTEN == 0) {
176 fprintf(stderr, "\n");
177 printDirectories();
178 printFAT();
179 }
180 }
181
182 fprintf(stderr, "\nEnd of FAT simulation\n\n");
183
184 printDirectories();
185 printFAT();
186
187 return 0;
188}
189
190/*
191 * Function: newFile
192 *
193 * Arguments: string File name.
194 * int File size.
195 *
196 * Return: void
197 *
198 * Purpose: Puts a new file into the file system.
199 */
200int newFile(const string& fileName, int fileSize) {
201 Entry newEntry; // New file entry.
202
203 unsigned nextFreeBlock = 0; // The next free block index location in the fatTable.
204
205 // Look to see if file by that name already exists in the directory list.
206 auto it = find_if(directoryList.begin(), directoryList.end(), [fileName](const Entry entry){return entry.fileName == fileName;});
207
208 if (it == directoryList.end()) {
209 // Create new entry.
210 newEntry.fileName = fileName;
211 newEntry.fileSize = fileSize;
212
213 int bytesRemaining = fileSize;
214
215 if (fileSize == 0) {
216 newEntry.firstBlock = -1;
217
218 // Add file to directory list.
219 directoryList.push_back(newEntry);
220
221 return 1;
222 }
223
224 // Find first empty block.
225 for (unsigned i = 0; i < fatTable.size(); i++) {
226 // If fatTable entry is 0, it is free.
227 if (fatTable[i] == 0) {
228 // If new file is 512 bytes or less right off the bat, make it's only block -1 and stop looping.
229 if (fileSize <= 512) {
230 fatTable[i] = -1;
231
232 newEntry.firstBlock = i;
233
234 break;
235 }
236
237 if (bytesRemaining > 512) {
238 // Get fatTable index of next empty block.
239 for (nextFreeBlock = i + 1; nextFreeBlock < fatTable.size(); nextFreeBlock++) {
240 if (fatTable[nextFreeBlock] == 0) {
241 // Set the previous block's value in the table to the nextFreeBlock's table index.
242 fatTable[i] = nextFreeBlock;
243
244 break;
245 }
246 }
247
248 // Record the first block used by the entry.
249 if (bytesRemaining == fileSize) {
250 newEntry.firstBlock = i;
251 }
252
253 // Decrement bytes remaining by 512.
254 bytesRemaining -= BLOCK_SIZE;
255 } else {
256 fatTable[i] = -1;
257
258 break;
259 }
260 }
261 }
262
263 } else {
264 // Tell user that the file already exists.
265 fprintf(stderr, "File %s already exists.\n", fileName.c_str());
266
267 return 0;
268 }
269
270 // Add file to directory list.
271 directoryList.push_back(newEntry);
272
273 return 1;
274}
275
276/*
277 * Function: deleteFile
278 *
279 * Arguments: string& Name of file to delete.
280 *
281 * Return: void
282 *
283 * Purpose: Remove file from directory listing and file system.
284 */
285int deleteFile(const string& fileName) {
286 int next = 0;
287
288 // Look to see if file by that name already exists in the directory list.
289 auto it = find_if(directoryList.begin(), directoryList.end(), [fileName](const Entry entry){return entry.fileName == fileName;});
290
291 // If file is not found, print an error message.
292 if (it == directoryList.end()) {
293 fprintf(stderr, "Error! The file name, %s, was not found.\n", fileName.c_str());
294
295 return 0;
296 } else if (it->firstBlock == -1) { // If file is of size 0, then just remove it from directory listing.
297 // Remove file from directory listing.
298 directoryList.erase(it);
299
300 return 1;
301 } else { // Else, remove file from directory listing and deallocate space it was using.
302 // Set fatTable index value to wherever the file's first block is located.
303 int i = it->firstBlock;
304
305 // If file is smaller than 512 bytes (so it has just a -1 block), then just set that block to free.
306 if (fatTable[i] == -1) {
307 fatTable[i] = 0;
308 } else { // Else, iterate through fatTable, freeing up blocks until terminating -1 is found.
309 do {
310 // Find next block.
311 next = fatTable[i];
312
313 // Set it to 0.
314 fatTable[i] = 0;
315
316 // Next block will be at next.
317 i = next;
318 } while (fatTable[i] != -1);
319
320 // Set terminating -1 block to 0.
321 fatTable[i] = 0;
322 }
323
324 // Remove file from directory listing.
325 directoryList.erase(it);
326
327 return 1;
328 }
329}
330
331/*
332 * Function: modifyFile
333 *
334 * Arguments: none
335 *
336 * Return: void
337 *
338 * Purpose: Expands an existing file's size.
339 */
340int modifyFile(const string& fileName, int fileSize) {
341 Entry newEntry; // New file entry.
342
343 // Look to see if file by that name already exists in the directory list.
344 auto it = find_if(directoryList.begin(), directoryList.end(), [fileName](const Entry entry){return entry.fileName == fileName;});
345
346 if (it == directoryList.end()) {
347 fprintf(stderr, "Error! The file name, %s, was not found.\n", fileName.c_str());
348
349 return 0;
350 } else {
351 // Make a new file.
352 newFile("Temp File", fileSize);
353
354 // Delete old file.
355 deleteFile(fileName);
356
357 // Change new file to old file's name
358 renameFile("Temp File", fileName);
359
360 return 1;
361 }
362}
363
364/*
365 * Function: renameFile
366 *
367 * Arguments: const string& Name of the file to rename.
368 *
369 * Return: void
370 *
371 * Purpose: Renames a file.
372 */
373int renameFile(const string& oldFileName, const string& newFileName) {
374 auto fileExistCheck = find_if(directoryList.begin(), directoryList.end(), [oldFileName](const Entry entry){return entry.fileName == oldFileName;});
375 auto duplicateNameCheck = find_if(directoryList.begin(), directoryList.end(), [newFileName](const Entry entry){return entry.fileName == newFileName;});
376
377 // If old file doesn't exist, warn user.
378 if (fileExistCheck == directoryList.end()) {
379 fprintf(stderr, "Error! The file name, %s, was not found.\n", oldFileName.c_str());
380
381 return 0;
382 } else if (duplicateNameCheck != directoryList.end()) { // Else, also check if new name collides with another file that already has that name.
383 fprintf(stderr, "Error! The file name, %s, already exists.\n", newFileName.c_str());
384
385 return 0;
386 } else { // Else, rename file.
387 auto entryIterator = find_if(directoryList.begin(), directoryList.end(), [oldFileName](const Entry entry){return entry.fileName == oldFileName;});
388
389 entryIterator->fileName = newFileName;
390
391 return 1;
392 }
393}
394
395/*
396 * Function: copyFile
397 *
398 * Arguments: const string& Name of the file to rename.
399 * const string& Name to change old file into.
400 *
401 * Return: void
402 *
403 * Purpose: Copies a file.
404 */
405int copyFile(const string& fileName, const string& newFileName) {
406 auto fileExistCheck = find_if(directoryList.begin(), directoryList.end(), [fileName](const Entry entry){return entry.fileName == fileName;});
407 auto duplicateNameCheck = find_if(directoryList.begin(), directoryList.end(), [newFileName](const Entry entry){return entry.fileName == newFileName;});
408
409 // If old file doesn't exist, warn user.
410 if (fileExistCheck == directoryList.end()) {
411 fprintf(stderr, "Error! The file name, %s, was not found.\n", fileName.c_str());
412
413 return 0;
414 } else if (duplicateNameCheck != directoryList.end()) { // Else, also check if new name collides with another file that already has that name.
415 fprintf(stderr, "Error! The file name, %s, already exists.\n", newFileName.c_str());
416
417 return 0;
418 } else { // Else, create new file with new name.
419 newFile(newFileName, fileExistCheck->fileSize);
420
421 return 1;
422 }
423}
424
425/*
426 * Function: printFAT
427 *
428 * Arguments: none
429 *
430 * Return: void
431 *
432 * Purpose: Prints out content of the FAT.
433 */
434void printFAT() {
435 int minEntry = 0; // Starting entry in range.
436 int maxEntry = 11; // Ending entry in range.
437 int entry = 0; // Entry index in FAT.
438
439 fprintf(stderr, "Contents of the File Allocation Table\n");
440
441 // Print out what's in the FAT.
442 for (int i = 0; i < NUMBER_OF_FAT_ENTRIES / NUMBER_OF_ENTRIES_PER_LINE; i++) {
443 // Print row label denoting which entries are on this row.
444 fprintf(stderr, "#%03d-%03d", minEntry, maxEntry);
445
446 // Print each FAT entry, 12 entries per row.
447 for (int j = 0; j < NUMBER_OF_ENTRIES_PER_LINE; j++) {
448 fprintf(stderr, "\t%d", fatTable[entry]);
449
450 entry++;
451 }
452
453 fprintf(stderr, "\n");
454
455 // Move to next section of blocks.
456 minEntry += NUMBER_OF_ENTRIES_PER_LINE;
457 maxEntry += NUMBER_OF_ENTRIES_PER_LINE;
458 }
459
460 fprintf(stderr, "\n");
461}
462
463/*
464 * Function: printDirectories
465 *
466 * Arguments: none
467 *
468 * Return: void
469 *
470 * Purpose: Prints out content of directory.
471 */
472void printDirectories() {
473 int fileCount = 0; // How many files in system.
474 int totalSize = 0; // Total size of files in bytes.
475
476 int blockNum = 0;
477
478 fprintf(stderr, "Directory Listing\n");
479
480 // List each file in the directory.
481 for (const Entry& entry : directoryList) {
482 fprintf(stderr, "File name: %-15s File Size: \t %10d\n", entry.fileName.c_str(), entry.fileSize);
483 //entry.firstBlock == -1 ? fprintf(stderr, "Cluster(s) in use: (none)\n") : fprintf(stderr, "Cluster(s) in use:");
484
485 if (entry.firstBlock == -1 ) {
486 fprintf(stderr, "Cluster(s) in use: (none)\n");
487 } else {
488 fprintf(stderr, "Cluster(s) in use: ");
489
490 // Start at first block of file.
491 blockNum = entry.firstBlock;
492
493 // Traverse entry's list of blocks used, starting with firstBlock.
494 while (fatTable[blockNum] != -1) {
495 // Print fatTable index.
496 fprintf(stderr, "%d ", blockNum);
497
498 blockNum = fatTable[blockNum];
499 }
500
501 // Print terminating block fatTable index.
502 fprintf(stderr, "%d ", blockNum);
503
504 fprintf(stderr, "\n");
505 }
506 }
507
508 // Count how many files there are and what the total size of them is.
509 fileCount = directoryList.size();
510
511 // Accumulate file sizes to get total size of files.
512 for (const Entry& entry : directoryList) {
513 totalSize += entry.fileSize;
514 }
515
516 fprintf(stderr, "Files: %5d Size: %8d bytes\n", fileCount, totalSize);
517
518 fprintf(stderr, "\n");
519}