-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathbfs.c
455 lines (321 loc) · 13.2 KB
/
bfs.c
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
// ============================================================================
// bfs.c
// ============================================================================
#include "bfs.h"
// ============================================================================
// Allocate a free disk block for the file whose Inode number is 'inum' and
// assign it to FBN 'fbn' in the file's Inode. On success, return the DBN
// allocated. On failure, abort
// ============================================================================
i32 bfsAllocBlock(i32 inum, i32 fbn) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
if (fbn < 0) FATAL(EBADFBN);
if (fbn > MAXFBN) FATAL(EBADFBN);
// Grab the next free block in the BFS disk
i32 dbn = bfsFindFreeBlock();
// Update the corresponding Inode, or IndirectBlock
i8 buf8[BYTESPERBLOCK] = {0}; // 1-block buffer
bioRead(DBNINODES, buf8);
Inode* pinodes = (Inode*)buf8; // array of Inodes
Inode* pinode = &pinodes[inum]; // target Inode
if (fbn < NUMDIRECT) { // in direct[] array?
pinode->direct[fbn] = dbn;
bioWrite(DBNINODES, buf8);
return dbn;
} else { // in indirect block?
i16 buf16[I16SPERBLOCK]= {0};
i32 dbnIndirect = pinode->indirect; // DBN of indirect block
if (dbnIndirect == 0) { // not yet allocated
dbnIndirect = bfsFindFreeBlock();
pinode->indirect = dbn;
}
bioRead(dbnIndirect, buf16);
buf16[fbn - NUMDIRECT] = dbn;
bioWrite(dbnIndirect, buf16);
}
return dbn; // allocated DBN
}
// ============================================================================
// Create file 'fname'. Find a free inum; ie, free slot in the Directory.
// Leave the size of the file as zero, until the user performs a write, or a
// seek into the file. On success, return the file's inum. On failure, abort
// ============================================================================
i32 bfsCreateFile(str fname) {
if (fname == NULL) FATAL(ENULLPTR);
if (strlen(fname) > FNAMESIZE - 1) FATAL(EBIGFNAME); // fname too big
i8 buf[BYTESPERBLOCK] = {0};
bioRead(DBNDIR, buf);
Dir* dir = (Dir*)buf;
for (int inum = 0; inum < NUMINODES; ++inum) { // search Directory
if (strlen(dir->fname[inum]) == 0) { // free slot
strcpy(dir->fname[inum], fname);
bioWrite(DBNDIR, dir);
bfsRefOFT(inum);
return inum;
}
}
FATAL(EDIRFULL); // Directory full
return 0; // pacify compiler
}
// ============================================================================
// Dereference file with Inode number 'inum' in the Open File Table. If
// refcount reaches 0, free up that entry in the OFT
// ============================================================================
i32 bfsDerefOFT(i32 inum) {
i32 ofte = bfsFindOFTE(inum);
--g_oft[ofte].refs;
if (g_oft[ofte].refs == 0) {
g_oft[ofte].inum = 0;
g_oft[ofte].curs = 0;
}
return 0;
}
// ============================================================================
// Extend file 'inum' out to FBN 'fbn'
// ============================================================================
i32 bfsExtend(i32 inum, i32 fbn) {
i32 size = bfsGetSize(inum);
i32 fbnLast = (size + 1) / BYTESPERBLOCK;
for (i32 f = fbnLast; f <= fbn; ++f) {
bfsAllocBlock(inum, f);
}
return 0;
}
// ============================================================================
// Use Inode to find the DBN used to store file block 'fbn'. Return ENODBN
// if not yet mapped
// ============================================================================
i32 bfsFbnToDbn(i32 inum, i32 fbn) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
if (fbn < 0) FATAL(EBADFBN);
if (fbn > MAXFBN) FATAL(EBADFBN);
Inode inode;
bfsReadInode(inum, &inode);
if (fbn < NUMDIRECT) { // in direct[] array?
i32 dbn = inode.direct[fbn];
return (dbn == 0) ? ENODBN : dbn;
}
// fbn is not in direct, so check indirect block. If it doesn't exist,
// then allocate an empty indirect block. But return ENODBN for the
// caller to handle grabing a new data block.
if (inode.indirect == 0) { // no indirect block yet allocated
i32 dbn = bfsFindFreeBlock();
inode.indirect = dbn;
bfsWriteInode(inum, &inode);
return ENODBN;
}
// Check the indirect block
i16 buf[NUMINDIRECT] = {0};
bioRead(inode.indirect, buf);
i32 dbn = buf[fbn - NUMDIRECT];
return (dbn == 0) ? ENODBN : dbn;
}
// ============================================================================
// Convert FileDescriptor (user-visible) to Inum (internal)
// ============================================================================
i32 bfsFdToInum(i32 fd) {
i32 inum = fd - INUMTOFD;
if (inum < 0) FATAL(EBADINUM);
return inum;
}
// ============================================================================
// Find 'inum' in the Open File Table (OFT). If not found, create an entry.
// Return the index within the OFT. On failure, EOFTFULL
// ============================================================================
i32 bfsFindOFTE(i32 inum) {
for (int i = 0; i < NUMOFTENTRIES; ++i) {
if (g_oft[i].inum == inum) return i;
}
// Not found, so look for an empty OFTE
for (int i = 0; i < NUMOFTENTRIES; ++i) {
if (g_oft[i].inum == 0) {
g_oft[i].inum = inum;
g_oft[i].curs = 0;
g_oft[i].refs = 1;
return i;
}
}
FATAL(EOFTFULL); // no-return
return 0; // pacify compiler
}
// ============================================================================
// Allocate the next free block from the Freelist. Adjust Freelist
// accordingly. On success, return DBN. FATAL otherwise
// ============================================================================
i32 bfsFindFreeBlock() {
i8 buf8[BYTESPERBLOCK] = {0};
bioRead(DBNSUPER, buf8);
Super* super = (Super*)buf8;
i32 dbn = super->firstFree;
if (dbn == 0) FATAL(EDISKFULL);
i16 buf16[I16SPERBLOCK] = {0}; // for next free block
bioRead(dbn, buf16);
super->firstFree = buf16[0]; // new head of Freelist
bioWrite(DBNSUPER, buf8); // update SuperBlock
return dbn;
}
// ============================================================================
// Initialize the Freelist
// ============================================================================
i32 bfsInitFreeList() {
i16 buf[I16SPERBLOCK] = {0};
i32 ret = 0;
for (int dbn = NUMMETA; dbn < BLOCKSPERDISK - 1; ++dbn) {
buf[0] = dbn + 1;
bioWrite(dbn, (i8*)buf);
}
buf[0] = 0;
bioWrite(BLOCKSPERDISK - 1, (i8*)buf); // end of Freelist
return ret;
}
// ============================================================================
// Write the initial Dir block, of all zeroes, into DBN 2
// ============================================================================
i32 bfsInitDir(FILE* fp) {
if (fp == NULL) FATAL(ENULLPTR);
i8 buf[BYTESPERBLOCK] = {0};
return bioWrite(DBNDIR, buf);
}
// ============================================================================
// Write the initial Inodes block, of all zeroes, into DBN 1
// ============================================================================
i32 bfsInitInodes(FILE* fp) {
if (fp == NULL) FATAL(ENULLPTR);
i8 buf[BYTESPERBLOCK] = {0};
return bioWrite(DBNINODES, buf);
}
// ============================================================================
// Initialize the Open File Table to all zeroes
// ============================================================================
i32 bfsInitOFT() {
for (i32 i = 0; i < NUMOFTENTRIES; ++i) {
g_oft[i].inum = 0;
g_oft[i].curs = 0;
g_oft[i].refs = 0;
}
return 0;
}
// ============================================================================
// Write the initial Super block into DBN 0
// ============================================================================
i32 bfsInitSuper(FILE* fp) {
if (fp == NULL) FATAL(ENULLPTR);
Super sb;
sb.numBlocks = BLOCKSPERDISK; // eg: 100
sb.numInodes = NUMINODES; // eg: 8
sb.firstFree = NUMMETA; // eg: 3
i8 buf[BYTESPERBLOCK] = {0};
memcpy(buf, &sb, sizeof(Super));
return bioWrite(DBNSUPER, buf);
}
// ============================================================================
// Convert between inum (internal) and FileDescriptor (user-visible)
// ============================================================================
i32 bfsInumToFd(i32 inum) { return inum + INUMTOFD; }
// ============================================================================
// Lookup 'fname' in the Directory. If found, return its inum. If not,
// return EFNF
// ============================================================================
i32 bfsLookupFile(str fname) {
if (fname == NULL) FATAL(ENULLPTR);
i8 buf[BYTESPERBLOCK] = {0};
bioRead(DBNDIR, buf);
Dir* dir = (Dir*)buf;
for (int inum = 0; inum < NUMINODES; ++inum) {
if (strcmp(fname, dir->fname[inum]) == 0) {
bfsRefOFT(inum);
return inum;
}
}
return EFNF;
}
// ============================================================================
// Read FBN 'fbn' for the file whose inum is 'inum' into 'buf'
// ============================================================================
i32 bfsRead(i32 inum, i32 fbn, i8* buf) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
if (fbn < 0) FATAL(EBADFBN);
if (fbn > MAXFBN) FATAL(EBADFBN);
i32 dbn = bfsFbnToDbn(inum, fbn);
bioRead(dbn, buf);
return 0;
}
// ============================================================================
// Read the Inodes block. Extract and return the Inode whose number is 'inum'.
// On success, return 0. On failure, abort
// ============================================================================
i32 bfsReadInode(i32 inum, Inode* inode) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
if (inode == NULL) FATAL(ENULLPTR);
i8 buf[BYTESPERBLOCK] = {0};
bioRead(DBNINODES, buf);
Inode* inodes = (Inode*)buf;
memcpy(inode, &inodes[inum], sizeof(Inode));
return 0;
}
// ============================================================================
// Reference file with Inode number 'inum' in the Open File Table
// ============================================================================
i32 bfsRefOFT(i32 inum) {
i32 ofte = bfsFindOFTE(inum);
++g_oft[ofte].refs;
return 0;
}
// ============================================================================
// Set cursor position for the file open on File Descriptor 'fd' to 'newCurs'
// ============================================================================
i32 bfsSetCursor(i32 inum, i32 newCurs) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
i32 ofte = bfsFindOFTE(inum);
g_oft[ofte].curs = newCurs;
return 0;
}
// ============================================================================
// Return the cursor position for the file open on File Descriptor 'fd'
// ============================================================================
i32 bfsTell(i32 fd) {
i32 inum = bfsFdToInum(fd);
i32 ofte = bfsFindOFTE(inum);
return g_oft[ofte].curs;
}
// ============================================================================
// Return the size of the file whose Inode number is 'inum'
// ============================================================================
i32 bfsGetSize(i32 inum) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
Inode inode;
bfsReadInode(inum, &inode);
return inode.size;
}
// ============================================================================
// Set size of file 'inum' to 'size
// ============================================================================
i32 bfsSetSize(i32 inum, i32 size) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
Inode inode;
bfsReadInode(inum, &inode);
inode.size = size;
bfsWriteInode(inum, &inode);
return 0;
}
// ============================================================================
// Update the Inodes block on disk with the info in 'inode'
// ============================================================================
i32 bfsWriteInode(i32 inum, Inode* inode) {
if (inum < 0) FATAL(EBADINUM);
if (inum > MAXINUM) FATAL(EBADINUM);
if (inode == NULL) FATAL(ENULLPTR);
i8 buf[BYTESPERBLOCK];
bioRead(DBNINODES, buf);
Inode* inodes = (Inode*)buf;
memcpy(&inodes[inum], inode, sizeof(Inode));
bioWrite(DBNINODES, buf);
return 0;
}