-
Notifications
You must be signed in to change notification settings - Fork 2
/
FileSystem.java
301 lines (267 loc) · 10 KB
/
FileSystem.java
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
// Edited By Chris Knakal and Hyungjin Lee
// CSS 430 Program 5
// 5/30/2016
public class FileSystem
{
private final int SEEK_SET = 0;
private final int SEEK_CUR = 1;
private final int SEEK_END = 2;
private final int ERROR = -1;
private final static int MAX_FILESIZE = 136704; // bytes
private SuperBlock superBlock;
private Directory directory;
private FileTable fileTable;
public FileSystem(int diskBlocks)
{
// create superblock and format disk with diskBlocks # of Inodes
superBlock = new SuperBlock(diskBlocks);
// create directory and register "/" in directory 0
directory = new Directory(superBlock.totalInodes);
// create file table and store directory in file table
fileTable = new FileTable(directory);
// directory reconstruction -- read directory from Disk
FileTableEntry dirEntry = open("/", "r");
int dirSize = fsize(dirEntry);
if (dirSize > 0)
{
byte[] dirData = new byte[dirSize];
read(dirEntry, dirData);
directory.bytes2directory(dirData);
}
close(dirEntry);
}
// sync file system metadata to the disk
// includes superblock and directory
void sync()
{
// write directory back to disk
FileTableEntry dirEntry = open ("/", "w");
byte[] dirData = directory.directory2bytes();
write(dirEntry, dirData);
close(dirEntry);
// write superblock back to disk
superBlock.sync();
}
// Formats the disk with maximum number of files/Inodes to be created
boolean format(int fileCount)
{
while(!fileTable.fempty())
{
// check that no FileTableEntries are in the FileTable
// wait for the file table to empty
}
superBlock.format(fileCount);
directory = new Directory(superBlock.totalInodes);
fileTable = new FileTable(directory);
return true;
}
// Opens the specified file in the given mode
FileTableEntry open(String filename, String mode)
{
FileTableEntry entry = fileTable.falloc(filename, mode);
if (entry != null && mode.equals("w")) // entry was allocated properly and mode is write
{
if (!deallocateAllBlocks(entry)) // couldn't deallocate all blocks
{
entry = null;
}
}
return entry;
}
// Closes the file
boolean close(FileTableEntry entry)
{
synchronized (entry)
{
entry.count--; // decrement the entry count
if (entry.count > 0) // other threads still hold access to this entry
{
return true;
}
}
return fileTable.ffree(entry); // free this entry from the table
}
// return size of the file in bytes
int fsize(FileTableEntry entry)
{
// decrement Inode count
// if count is 0 after decrementing, write Inode to disk
synchronized (entry)
{
return entry.inode.length;
}
}
// updates the seek pointer in the file table entry
int seek(FileTableEntry entry, int offset, int whence)
{
if (entry == null)
{
SysLib.cerr("File Table Entry is invalid");
return ERROR;
}
synchronized (entry)
{
switch (whence) {
case SEEK_SET: // set to offset bytes from beginning of file
entry.seekPtr = offset;
break;
case SEEK_CUR: // set to offset bytes from current position
entry.seekPtr += offset;
break;
case SEEK_END: // set to offset bytes from end of file
entry.seekPtr = fsize(entry) + offset;
break;
}
entry.seekPtr = Math.min(entry.seekPtr, fsize(entry)); // clamp to size
entry.seekPtr = Math.max (0, entry.seekPtr); // or clamp to 0
}
return entry.seekPtr;
}
// Reads up to buffer.length bytes from the file indicated by fd,
// starting at the position currently pointed to by the seek pointer
// Returns the number of bytes read or -1 for error
int read(FileTableEntry entry, byte[] buffer)
{
// The mode for entry should be r or w+
if (entry == null || entry.mode.equals("w") || entry.mode.equals("a"))
{
return ERROR;
}
synchronized (entry)
{
int filesize = fsize(entry);
int bytesRead = 0;
int bufferRemaining = buffer.length;
while (bufferRemaining > 0 && entry.seekPtr < filesize)
{
int blockID = entry.inode.getBlockID(entry.seekPtr); // block to read
if (blockID != -1)
{
// read the whole block
byte[] blockData = new byte[Disk.blockSize];
SysLib.rawread(blockID, blockData);
int startPoint = entry.seekPtr % Disk.blockSize;
// Calculate how much to read from the data
// must be less than filesize, block size, and remaining buffer size
int blockDataToRead = Disk.blockSize - startPoint;
int fileDataLeft = filesize - entry.seekPtr;
int sizeToRead = Math.min(Math.min(blockDataToRead, fileDataLeft), bufferRemaining);
// copy block data over to buffer
System.arraycopy(blockData, startPoint, buffer, bytesRead, sizeToRead);
// adjust seek pointer, total bytes read, and size of remaining buffer
entry.seekPtr += sizeToRead;
bytesRead += sizeToRead;
bufferRemaining -= sizeToRead;
}
}
return bytesRead;
}
}
int write(FileTableEntry entry, byte[] buffer)
{
// The mode for entry should be w, a, or w+
if (entry == null || entry.mode.equals("r"))
{
return ERROR;
}
synchronized (entry)
{
int filesize = fsize(entry);
int bytesWritten = 0;
int bufferRemaining = buffer.length;
while (bufferRemaining > 0 && filesize < MAX_FILESIZE)
{
int blockID = entry.inode.getBlockID(entry.seekPtr);
// Need to find free block and give it to inode
while (blockID == -1)
{
int freeBlock = superBlock.getBlock();
if (!entry.inode.addBlock((short)freeBlock))
{
superBlock.returnBlock(freeBlock);
return ERROR;
}
blockID = entry.inode.getBlockID(entry.seekPtr);
}
// read the whole block
byte[] blockData = new byte[Disk.blockSize];
SysLib.rawread(blockID, blockData);
int startPoint = entry.seekPtr % Disk.blockSize;
// calculate how much to write to block
// must be less than block size and buffer remaining size
int blockSizeLeft = Disk.blockSize - startPoint;
int sizeToWrite = Math.min(blockSizeLeft, bufferRemaining);
// copy buffer data over to the blockData and write to disk
System.arraycopy(buffer, bytesWritten, blockData, startPoint, sizeToWrite);
SysLib.rawwrite(blockID, blockData);
// adjust seek pointer, total bytes written, and size of remaining buffer
entry.seekPtr += sizeToWrite;
bytesWritten += sizeToWrite;
bufferRemaining -= sizeToWrite;
// adjust the file size
if (entry.seekPtr > filesize)
{
entry.inode.length = entry.seekPtr;
}
}
// update inode on disk
entry.inode.toDisk(entry.iNumber);
return bytesWritten;
}
}
// delete file
// if the file is currently open by another thread, it will wait
// new attempts to open the file will fail
boolean delete(String filename)
{
// search the directory for the filename
short iNumber = directory.namei(filename);
// if the filename was found, get the Inode from the FileTable
// set the Inode's flag to 4 (delete pending)
if (iNumber != -1)
{
Inode inode = fileTable.getInode(iNumber);
inode.flag = 4;
if (inode.count == 0)
{
directory.ifree(iNumber);
}
return true;
}
return false;
}
// clears and frees all the blocks the inode was pointing to
private boolean deallocateAllBlocks(FileTableEntry entry)
{
if (entry != null && entry.inode.count == 1)
{
// release indirect blocks and indirect block itself
byte[] indirectData = entry.inode.readIndirectData();
if (indirectData != null)
{
int offset = 0;
int block = SysLib.bytes2short(indirectData, offset);
while (block != -1 && offset < Disk.blockSize)
{
superBlock.returnBlock(block);
offset+=2;
block = SysLib.bytes2short(indirectData, offset);
}
}
superBlock.returnBlock(entry.inode.indirect);
// release direct blocks
for (int i = 0; i < Inode.directSize; i++)
{
int blockID = entry.inode.direct[i];
if (blockID != -1)
{
superBlock.returnBlock(blockID);
entry.inode.direct[i] = -1;
}
}
entry.inode.toDisk(entry.iNumber);
return true;
}
return false;
}
}