-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfileSystem.c
More file actions
1191 lines (1016 loc) · 34.9 KB
/
Copy pathfileSystem.c
File metadata and controls
1191 lines (1016 loc) · 34.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* fileSystem.c
*
* Modified on: 03/05/2023
* Author: wlee447
*
* Complete this file.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "device.h"
#include "fileSystem.h"
#define UNALLOCATED 65534 // decimal value for an unallocated block in the file allocation table.
#define END_OF_FILE 65535 // decimal value for EoF in the file allocation table.
/* The file system error number. */
int file_errno = 0;
/* Index for the block containing the root directory file */
int root_block_idx = -1;
struct BlockEntry {
int idx;
int value;
};
struct DirectoryEntry {
int startBlockIdx;
int filesize;
};
#define MAX_OPEN_FILES 1022 // Assignment brief only allows for max 1024 blocks. Each file/dir at least requires 1 block. Sys area takes up atleast 2.
struct FilePointer {
int startBlockIdx; // Serves as unique ID
int offset;
};
// Global file pointer list
struct FilePointer filePointers[MAX_OPEN_FILES * sizeof(struct FilePointer)];
int nOpenFiles = 0; // Number of open files
/**
* @brief Gets the Root Index
*
* @return int
*/
int getRootIndex() {
if (root_block_idx == -1) {
root_block_idx = 1 + ((5 + numBlocks() * 2 + (BLOCK_SIZE - 1)) / BLOCK_SIZE);
}
return root_block_idx;
}
void resetBlocks() {
// printf("\n = CLEARING BLOCKS = \n");
unsigned char buffer[BLOCK_SIZE] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
// unsigned char buffer[BLOCK_SIZE] = "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO";
for (int i = 0; i < numBlocks(); i++) {
blockWrite(i, buffer);
}
}
/**
* @brief Returns 1 if a given device is formatted in WFS. Otherwise, return 0.
* Returns -1 on error.
*
* This function only gives an estimate, (only checks the file headers are there) but
* that was deemed to be enough for the purposes of this assignment.
*
* @return int
*/
int isFormatted() {
unsigned char data[BLOCK_SIZE];
if (blockRead(1, data) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
return (data[0] == 'W') && (data[1] == 'F') && (data[2] == 'S');
}
/**
* @brief decodes the given characters from the base256 storage format.
* NOTE: valid literal values range from 1 to 65535 (mapping to 0 to 65534) since 00000000 is the NULL terminator.
*
* @return decoded integer or -1 if error.
*/
int _getDecoded(unsigned char c1, unsigned char c0) {
int result = (unsigned char)c1 * 256 + (unsigned char)c0;
if (result < 0 || result > 65535) {
file_errno = EOTHER;
return -1;
}
return result;
}
/**
* @brief encodes the given integer in range 0 -> 65535 to characters in the base 256 storage format.
*
* @param n integer to convert
* @param result pointer to an char array of length 2
*
* @return 0 if successful, -1 if error.
*/
int _encode(int n, unsigned char *result) {
if (n < 0 || n > 65535) {
file_errno = EOTHER;
return -1;
}
result[0] = (n % 256);
result[1] = (int)((n - (n % 256)) / 256);
return 0;
}
int _getRootSize() {
if (isFormatted() != 1) {
file_errno = EOTHER;
return -5;
}
unsigned char buffer[BLOCK_SIZE];
if (blockRead(1, buffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -2;
}
return _getDecoded(buffer[3], buffer[4]);
}
int _setRootSize(int size) {
unsigned char buffer[BLOCK_SIZE];
if (blockRead(1, buffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
unsigned char encoded[2];
_encode(size, encoded);
buffer[3] = encoded[1];
buffer[4] = encoded[0];
if (blockWrite(1, buffer)) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
return 0;
}
int _loadFAT(unsigned char *fat) {
fat[0] = '\0';
// Read FAT
int fatLength = 5 + numBlocks() * 2;
int remainingLength = fatLength;
while (remainingLength > 0) {
// Read block
unsigned char readBuffer[BLOCK_SIZE] = {'\0'};
if (blockRead(1, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
// Get rid of FS header & root size
if (remainingLength == fatLength) {
if (remainingLength > BLOCK_SIZE) {
memcpy((char *)fat, (char *)(readBuffer + 4), BLOCK_SIZE - 5);
remainingLength -= BLOCK_SIZE;
} else {
memcpy((char *)fat, (char *)(readBuffer + 4), remainingLength - 5);
remainingLength -= remainingLength;
}
}
// Do others normally.
if (remainingLength > BLOCK_SIZE) {
memcpy((char *)fat, (char *)readBuffer, BLOCK_SIZE);
remainingLength -= BLOCK_SIZE;
} else {
memcpy((char *)fat, (char *)readBuffer, remainingLength);
remainingLength -= remainingLength;
}
}
return 0;
}
struct BlockEntry getBlockEntry(int block) {
struct BlockEntry entry = {-1, -1};
unsigned char c1, c0;
int c1_block = (5 + 2 * (block)) / BLOCK_SIZE + 1;
int c1_offset = (5 + 2 * (block)) % BLOCK_SIZE;
int c0_block = (6 + 2 * (block)) / BLOCK_SIZE + 1;
int c0_offset = (6 + 2 * (block)) % BLOCK_SIZE;
// Read C1
unsigned char readBuffer[BLOCK_SIZE];
if (blockRead(c1_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return entry;
}
c1 = readBuffer[c1_offset];
if (c1_block == c0_block) {
c0 = readBuffer[c0_offset];
} else {
// Read c0
if (blockRead(c0_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return entry;
}
c0 = readBuffer[c0_offset];
}
entry.idx = block;
entry.value = _getDecoded(c1, c0);
return entry;
}
int setBlockEntry(struct BlockEntry entry) {
// Determine block to change
int c1_block = (5 + 2 * (entry.idx)) / BLOCK_SIZE + 1;
int c1_offset = (5 + 2 * (entry.idx)) % BLOCK_SIZE;
int c0_block = (6 + 2 * (entry.idx)) / BLOCK_SIZE + 1;
int c0_offset = (6 + 2 * (entry.idx)) % BLOCK_SIZE;
unsigned char encoded[2];
_encode(entry.value, encoded);
// Modify C1
unsigned char readBuffer[BLOCK_SIZE];
if (blockRead(c1_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
readBuffer[c1_offset] = encoded[1];
// Maybe modify c0 as well.
if (c1_block == c0_block) {
readBuffer[c0_offset] = encoded[0];
}
// Write C1
if (blockWrite(c1_block, readBuffer)) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
// Modify and write c0
if (c1_block != c0_block) {
if (blockRead(c0_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
readBuffer[c0_offset] = encoded[0];
if (blockWrite(c0_block, readBuffer)) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
}
return 0;
}
/**
* @brief After a device restart, file pointers (which are memory constructs) are lost. We must regenerate a file pointer list everytime it is restarted (with offsets reset back to 0).
*
*/
void regenerateFilePointers() {
// printf("Regenerating file pointers\n");
unsigned char *fat = malloc(2 * numBlocks() + 1);
if (_loadFAT(fat) != 0) {
file_errno = EOTHER;
free(fat);
return;
}
int *visited = calloc(numBlocks(), sizeof(int)); // visited array of blocks
for (int i = getRootIndex(); i < numBlocks(); i++) {
int candidateStartIdx = i;
struct BlockEntry entry = getBlockEntry(i);
if (entry.value != UNALLOCATED && visited[i] != 1) {
visited[i] = 1;
int iterationDepth = 0;
// traverse linked list
while (entry.value != END_OF_FILE) {
visited[entry.value] = 1;
entry = getBlockEntry(entry.value);
iterationDepth++;
if (iterationDepth > numBlocks()) {
printf("ERROR: Detected loop while regenerating file pointers. May indicate malformed file allocation table.");
file_errno = EOTHER;
free(visited);
free(fat);
return;
}
}
// Recovery file pointers
filePointers[nOpenFiles].startBlockIdx = candidateStartIdx;
filePointers[nOpenFiles].offset = 0;
nOpenFiles++;
}
}
free(visited);
free(fat);
}
/*
* Formats the device for use by this file system.
* The volume name must be < 64 bytes long.
* All information previously on the device is lost.
* Also creates the root directory "/".
* Returns 0 if no problem or -1 if the call failed.
*/
int format(char *volumeName) {
resetBlocks(); // Optional - useful for testing.
// Clear file pointers
memset(filePointers, 0, sizeof(filePointers));
nOpenFiles = 0;
// Check block number validity
int reserved_blocks = 1 + ((5 + numBlocks() * 2 + (BLOCK_SIZE - 1)) / BLOCK_SIZE); // always round up
if ((numBlocks() - reserved_blocks) < 0) {
file_errno = ENOROOM;
return -1;
}
// Check volume name validiity
if (strlen(volumeName) > (BLOCK_SIZE - 1)) {
file_errno = EBADVOLNAME;
return -1;
}
// Set volume name to first block
unsigned char buffer[BLOCK_SIZE];
strncpy((char *)buffer, volumeName, BLOCK_SIZE);
if (blockWrite(0, buffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
// Assign WFS header (used to quickly check if a device has been formatted before)
buffer[0] = 'W';
buffer[1] = 'F';
buffer[2] = 'S';
// Initialise root directory size to 0
buffer[3] = (unsigned char)0;
buffer[4] = (unsigned char)0;
if (blockWrite(1, buffer) == -1) {
file_errno = EBADDEV;
return -1;
}
/**
* Create FAT - every 2 characters represents both an 'allocated' flag and the next block value in linked list.
*
* ==============
* ENCODING
* --------------
* ALL VALUES INCLUSIVE.
*
* 0-65533 : 0-65533 : NEXT BLOCK INDEX
* 65534 : 65534 : UNALLOCATED BLOCK
* 65535 : 65535 : END OF FILE
* ==============
*/
for (int i = 0; i < numBlocks(); i++) {
struct BlockEntry entry = {i, UNALLOCATED};
// Set to allocated if it is a reserved block or the root (i.e. last reserved block idx + 1).
if (i <= reserved_blocks) {
entry.value = END_OF_FILE;
}
setBlockEntry(entry);
}
return 0;
}
/*
* Returns the volume's name in the result.
* Returns 0 if no problem or -1 if the call failed.
*/
int volumeName(char *result) {
if (isFormatted() != 1) {
file_errno = EOTHER;
return -1;
}
if (blockRead(0, (unsigned char *)result) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
return 0;
}
int _min(int n0, int n1) {
if (n0 < n1) {
return n0;
} else {
return n1;
}
}
/**
* @brief Reads a specified amount of data starting at the specified block.
*
* @param startBlockIdx
* @param length amount of characters to read (excl. null terminator)
* @param result
* @return int 0 for success, -1 for error.
*/
int _read(int startBlockIdx, int length, int offset, unsigned char *result) {
result[0] = '\0';
int remainingLength = length;
int remainingOffset = offset;
int blockIdx = startBlockIdx;
do {
// Read block
unsigned char readBuffer[BLOCK_SIZE] = {'\0'};
if (blockRead(blockIdx, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
if (remainingOffset > BLOCK_SIZE) {
// Offset by whole block
remainingOffset -= BLOCK_SIZE;
} else {
// Copy block with partial or no offset
memcpy((char *)result + (length - remainingLength), (char *)readBuffer + remainingOffset, _min(BLOCK_SIZE - remainingOffset, remainingLength));
remainingLength -= _min(BLOCK_SIZE - remainingOffset, remainingLength);
remainingOffset -= remainingOffset;
}
// Lookup next block in FAT
blockIdx = getBlockEntry(blockIdx).value;
} while (remainingLength > 0 && (blockIdx != END_OF_FILE && blockIdx != UNALLOCATED));
if (remainingOffset > 0) {
printf("EoF Reached.\n");
file_errno = EOTHER;
return -3;
} else if (remainingLength != 0) {
printf("ERROR! End of file with remaining length: %i\n", remainingLength);
file_errno = EOTHER;
return -6;
}
return 0;
}
int _allocateNewBlock(int lastBlockIdx, int *newBlockIdx) {
// Search for free block
unsigned char *fat = malloc(2 * numBlocks() + 1);
*newBlockIdx = -1;
if (_loadFAT(fat) != 0) {
file_errno = EOTHER;
free(fat);
return -1;
}
for (int i = 0; i < numBlocks(); i++) {
struct BlockEntry entry = getBlockEntry(i);
if (entry.value == UNALLOCATED) {
// Set new block -> END_OF_FILE
entry.value = END_OF_FILE;
if (setBlockEntry(entry) != 0) {
free(fat);
return -1;
}
*newBlockIdx = entry.idx;
if (lastBlockIdx > 0) {
// Set last block - > new block
struct BlockEntry updatedLastBlockEntry = {lastBlockIdx, entry.idx};
if (setBlockEntry(updatedLastBlockEntry) != 0) {
free(fat);
return -1;
}
}
// printf("Allocated new block: %i\n", *newBlockIdx);
free(fat);
return 0;
}
}
free(fat);
file_errno = ENOROOM;
printDevError("NO ROOM\n");
return -1;
}
int _append(int startBlockIdx, int currentLength, unsigned char *data, int dataLength) {
// Traverse to last block
int blockIdx = startBlockIdx;
while (getBlockEntry(blockIdx).value != END_OF_FILE) {
blockIdx = getBlockEntry(blockIdx).value;
}
unsigned char buffer[BLOCK_SIZE];
if (blockRead(blockIdx, buffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
// Start appending
int bufferPos = currentLength % BLOCK_SIZE;
int dataPos = 0;
while (dataPos < dataLength) {
// Check if block is full
if (bufferPos > BLOCK_SIZE - 1) {
// Commit current block
if (blockWrite(blockIdx, buffer)) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
// Assign new block
int newBlockIdx;
if (_allocateNewBlock(blockIdx, &newBlockIdx) != 0) {
return -1;
}
blockIdx = newBlockIdx;
bufferPos = 0;
}
buffer[bufferPos] = data[dataPos];
dataPos++;
bufferPos++;
}
// Commit last block
if (blockWrite(blockIdx, buffer)) {
file_errno = EBADDEV;
printDevError("device err");
return -1;
}
return 0;
}
/**
* @brief Get the DirectoryEntry from given directory file data
*
* @param cwdData char array of directory file data.
* @param targetName file/directory name to retrieve the address for.
* @param type 'F' for file, 'D' for directory
* @return DirectoryEntry struct with all values -1 if not found, block index (address) and its length otherwise.
*/
struct DirectoryEntry
_getAddressFromDirectoryFile(unsigned char *cwdData, int cwdLength, unsigned char *targetName, char type) {
// Get target
char target[9];
strncpy(target, (char *)targetName, 7);
target[7] = type;
target[8] = '\0';
// Parse & search
struct DirectoryEntry addr = {-1, -1};
for (int i = 0; i < cwdLength; i += 12) {
char candidate[9];
strncpy(candidate, (char *)cwdData + i, 7);
candidate[7] = cwdData[i + 7];
target[8] = '\0';
if (strncmp(target, candidate, 8) == 0) {
// Found target file/directory - return addr
addr.startBlockIdx = _getDecoded(cwdData[i + 8], cwdData[i + 9]);
addr.filesize = _getDecoded(cwdData[i + 10], cwdData[i + 11]);
return addr;
}
}
// Not found
return addr;
}
/**
* @brief Get the DirectoryEntry From directory
*
* @param cwd start index of the block containing the working directory file
* @param targetName file/directory name to retrieve the address for.
* @param type 'F' for file, 'D' for directory
* @return DirectoryEntry struct with all values -1 if not found, block index (address) and its length otherwise.
*/
struct DirectoryEntry
getAddressFromDirectory(int cwd, int cwdLength, unsigned char *targetName, char type) {
// Read directory
if (cwdLength > 0) {
unsigned char *data = malloc(cwdLength);
if (_read(cwd, cwdLength, 0, data) != 0) {
file_errno = EOTHER;
struct DirectoryEntry addr = {-1, -1};
free(data);
return addr;
}
struct DirectoryEntry result = _getAddressFromDirectoryFile(data, cwdLength, targetName, type);
free(data);
return result;
}
// empty directory - not found.
struct DirectoryEntry addr = {-1, -1};
return addr;
}
/**
* @brief Allocates a new block for the given file and makes the directory file entry.
*
* NOTE: Does not increase the size of the parent's directory size in grandparent directory file.
*
* @param fileName
* @param type
* @param parentDirBlock
* @param parentDirLength
* @return int
*/
int _createFile(unsigned char *fileName, unsigned char type, int parentDirBlock, int parentDirLength, int *newBlockIdx) {
// Allocate new block
if (_allocateNewBlock(-1, newBlockIdx) != 0) {
return -1;
}
// Append to directory file
unsigned char directoryEntry[12] = {' ', ' ', ' ', ' ', ' ', ' ', ' '};
strncpy((char *)directoryEntry, (char *)fileName, 7);
// Set type
directoryEntry[7] = type;
// Set start block
unsigned char encoded[2];
_encode(*newBlockIdx, encoded);
directoryEntry[8] = encoded[1];
directoryEntry[9] = encoded[0];
// Set file size (initially 0)
directoryEntry[10] = (unsigned char)0;
directoryEntry[11] = (unsigned char)0;
if (_append(parentDirBlock, parentDirLength, directoryEntry, 12) != 0) {
return -1;
}
return 0;
}
int _updateFilesize(int dirStartBlock, int dirLength, unsigned char *targetName, char type, int filesize) {
// Read directory
unsigned char *data = malloc(dirLength);
if (_read(dirStartBlock, dirLength, 0, data) != 0) {
file_errno = EOTHER;
free(data);
return -1;
}
// Get target
char target[9];
strncpy(target, (char *)targetName, 7);
target[7] = type;
// Parse & search
for (int i = 0; i < dirLength; i += 12) {
if (strncmp(target, (char *)data + i, 8) == 0) {
// Found target file/directory - calculate block & offset
unsigned char encoded[2];
_encode(filesize, encoded);
int c1_block = dirStartBlock + (i + 10) / BLOCK_SIZE;
int c1_offset = (i + 10) % BLOCK_SIZE;
int c0_block = dirStartBlock + (i + 11) / BLOCK_SIZE;
int c0_offset = (i + 11) % BLOCK_SIZE;
// Make change
// Modify C1
unsigned char readBuffer[BLOCK_SIZE];
if (blockRead(c1_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
free(data);
return -1;
}
readBuffer[c1_offset] = encoded[1];
// Maybe modify c0 as well.
if (c1_block == c0_block) {
readBuffer[c0_offset] = encoded[0];
}
// Write C1
if (blockWrite(c1_block, readBuffer)) {
file_errno = EBADDEV;
printDevError("device err");
free(data);
return -1;
}
// Modify and write c0
if (c1_block != c0_block) {
if (blockRead(c0_block, readBuffer) == -1) {
file_errno = EBADDEV;
printDevError("device err");
free(data);
return -1;
}
readBuffer[c0_offset] = encoded[0];
if (blockWrite(c0_block, readBuffer)) {
file_errno = EBADDEV;
printDevError("device err");
free(data);
return -1;
}
}
free(data);
return 0;
}
}
free(data);
// Not found
file_errno = EOTHER;
return -1;
}
/*
* Makes a file with a fully qualified pathname starting with "/".
* It automatically creates all intervening directories.
* Pathnames can consist of any printable ASCII characters (0x20 - 0x7e)
* including the space character.
* The occurrence of "/" is interpreted as starting a new directory
* (or the file name).
* Each section of the pathname must be between 1 and 7 bytes long (not
* counting the "/"s).
* The pathname cannot finish with a "/" so the only way to create a directory
* is to create a file in that directory. This is not true for the root
* directory "/" as this needs to be created when format is called.
* The total length of a pathname is limited only by the size of the device.
* Returns 0 if no problem or -1 if the call failed.
*/
int create(char *pathName) {
unsigned char nameBuffer[8] = {'\0'};
int cwdAddress = getRootIndex();
int cwdLength = _getRootSize();
unsigned char parentNameBuffer[8];
int cwdParentAddress; // Start block number of cwd parent
int cwdParentLength; // Length of dir file for cwd parent
// Navigate and create requested directories.
for (int i = 1; pathName[i] != '\0'; i++) {
unsigned char c = pathName[i];
if (c == '/') {
// (Create if necessary and) Navigate to the directory specified.
struct DirectoryEntry newDirAddr;
// Read cwd file
if (cwdLength > 0) {
unsigned char *data = malloc(cwdLength);
if (_read(cwdAddress, cwdLength, 0, data) != 0) {
free(data);
return -9;
}
// Get new directory file address
newDirAddr = _getAddressFromDirectoryFile(data, cwdLength, nameBuffer, 'D');
free(data);
} else {
newDirAddr.startBlockIdx = -1;
}
if (newDirAddr.startBlockIdx == -1) {
// Create directory
int newDirStartIdx;
if (_createFile(nameBuffer, 'D', cwdAddress, cwdLength, &newDirStartIdx) != 0) {
return -2;
}
// We must update the cwd parent's dir file to increase the filesize record of cwd
// If it is root, we know where filesize is.
if (cwdAddress == getRootIndex()) {
_setRootSize(cwdLength + 12);
} else {
if (_updateFilesize(cwdParentAddress, cwdParentLength, parentNameBuffer, 'D', cwdLength + 12) != 0) {
return -3;
}
}
// Navigate to new directory
cwdParentAddress = cwdAddress;
cwdParentLength = cwdLength + 12;
cwdAddress = newDirStartIdx;
cwdLength = 0;
} else {
// 'Navigate' to directory
cwdParentAddress = cwdAddress;
cwdParentLength = cwdLength;
cwdAddress = newDirAddr.startBlockIdx;
cwdLength = newDirAddr.filesize;
}
strncpy((char *)parentNameBuffer, (char *)nameBuffer, 7);
nameBuffer[0] = '\0'; // Clear name buffer
} else {
// Still processing name. Add to buffer and keep going.
strncat((char *)nameBuffer, (char *)&c, 1);
}
}
// File creation requested, create file.
if (nameBuffer != '\0') {
int newBlockIdx; // Throwaway variable
if (_createFile(nameBuffer, 'F', cwdAddress, cwdLength, &newBlockIdx) != 0) {
return -4;
}
// We must update the cwd parent's dir file to increase the filesize record of cwd
// If it is root, we know where filesize is.
if (cwdAddress == getRootIndex()) {
_setRootSize(cwdLength + 12);
} else {
if (_updateFilesize(cwdParentAddress, cwdParentLength, parentNameBuffer, 'D', cwdLength + 12) != 0) {
return -5;
}
}
// Create file pointer
filePointers[nOpenFiles].startBlockIdx = newBlockIdx;
filePointers[nOpenFiles].offset = 0;
nOpenFiles++;
return 0;
} else {
file_errno = EOTHER;
return -1;
}
}
/**
* Recurse down the directory tree and retrieve the size
*/
int _getDirectorySize(int dirAddr, int dirLength) {
int sum = dirLength;
// Read request dir
if (dirLength > 0) {
unsigned char *directory = malloc(dirLength);
if (_read(dirAddr, dirLength, 0, directory) != 0) {
file_errno = EOTHER;
free(directory);
return -1;
}
for (int i = 0; i < dirLength; i += 12) {
if (directory[i + 7] == 'D') {
sum += _getDirectorySize(_getDecoded(directory[i + 8], directory[i + 9]), _getDecoded(directory[i + 10], directory[i + 11]));
} else {
sum += _getDecoded(directory[i + 10], directory[i + 11]);
}
}
free(directory);
}
return sum;
}
/*
* Returns a list of all files in the named directory.
* The "result" string is filled in with the output.
* The result looks like this
/dir1:
file1 42
file2 0
* The fully qualified pathname of the directory followed by a colon and
* then each file name followed by a tab "\t" and then its file size.
* Each file on a separate line.
* The directoryName is a full pathname.
*/
void list(char *result, char *directoryName) {
result[0] = '\0';
unsigned char nameBuffer[8] = {'\0'};
int cwdAddress = getRootIndex();
int cwdLength = _getRootSize();
// Navigate requested directory.
for (int i = 1; ((i < strlen(directoryName)) || (directoryName[i] != '\0')); i++) {
unsigned char c = directoryName[i];
if (c == '/') {
// Get directory file address
struct DirectoryEntry dirAddr = getAddressFromDirectory(cwdAddress, cwdLength, nameBuffer, 'D');
if (dirAddr.startBlockIdx == -1) {
file_errno = ENOSUCHFILE;
return;
} else {
// 'Navigate' to directory
cwdAddress = dirAddr.startBlockIdx;
cwdLength = dirAddr.filesize;
}
nameBuffer[0] = '\0'; // Clear name buffer
} else {
// Still processing name. Add to buffer and keep going.
strncat((char *)nameBuffer, (char *)&c, 1);
}
}
// Navigate to request directory
if (nameBuffer[0] != '\0') {
struct DirectoryEntry dirAddr = getAddressFromDirectory(cwdAddress, cwdLength, nameBuffer, 'D');
if (dirAddr.startBlockIdx == -1) {
file_errno = ENOSUCHFILE;
return;
} else {
// 'Navigate' to directory
cwdAddress = dirAddr.startBlockIdx;
cwdLength = dirAddr.filesize;
}
}
// Print dir contents
strncat(result, directoryName, strlen(directoryName) + 1);
strcat(result, ":\n");
// Read request dir
if (cwdLength > 0) {
unsigned char *data = malloc(cwdLength);
if (_read(cwdAddress, cwdLength, 0, data) != 0) {
free(data);
return;
}
for (int i = 0; i < cwdLength; i += 12) {
strncat(result, (char *)data + i, 7); // directory/file name
strcat(result, ":\t");
int filesize = _getDecoded(data[i + 10], data[i + 11]);
char filesizeFormatted[6]; // max filesize is 256^2 -> I.e. 5 chars max.
if (data[i + 7] == 'D') {
sprintf(filesizeFormatted, "%i", _getDirectorySize(_getDecoded(data[i + 8], data[i + 9]), filesize));
} else {
sprintf(filesizeFormatted, "%i", filesize);
}
strncat(result, filesizeFormatted, 5); // size
strcat(result, "\n"); // each file on newline
}