├── .classpath
├── .gitignore
├── .project
├── DISK
├── README.md
├── build.xml
├── mytest.script
├── nbproject
├── build-impl.xml
├── genfiles.properties
├── project.properties
└── project.xml
├── src
├── main
│ └── Main.java
└── minikernel
│ ├── Boot.java
│ ├── Disk.java
│ ├── FastDisk.java
│ ├── FileSys.java
│ ├── FileTester.java
│ ├── Kernel.java
│ ├── Library.java
│ ├── Makefile.sdx
│ └── MyShell.java
└── test1.script
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
15 | * This program creates a Disk and launches the kernel by calling the POWER_ON 16 | * interrupt. When the Kernel returns from the interrupt, we assume it wants 17 | * to shut down. 18 | *
19 | * The program expects four or more command-line arguments: 20 | *
29 | * An example invocation is 30 | *
31 | * java Boot 10 Disk 100 Shell 32 | *33 | * 34 | * @see Kernel 35 | * @see Disk 36 | */ 37 | public class Boot { 38 | /** No public instances. */ 39 | private Boot() {} 40 | 41 | /** Prints a help message and exits. */ 42 | private static void usage() { 43 | err.println("usage: java Boot" 44 | + "
15 | * You may not change this class. 16 | *
17 | * This disk is slow and ornery. 18 | * It contains a number of blocks, all BLOCK_SIZE bytes long. 19 | * All operations occur on individual blocks. 20 | * You can't modify any more or any less data at a time. 21 | *
22 | * To read or write from the disk, call beginRead() or beginWrite(). 23 | * Each of these functions will start the action and return immediately. 24 | * When the action has been completed, the Disk calls Kernel.interrupt() 25 | * to let you know the Disk is ready for more. 26 | *
27 | * It may take a while for the disk to seek from one block to another. 28 | * Seek time is proportional to the difference in block numbers of the 29 | * blocks. 30 | *
31 | * Warning: Don't call beginRead() or beginWrite() while the 32 | * disk is busy! If you don't treat 33 | * the Disk gently, the system will crash! (Just like a real machine!) 34 | *
35 | * This disk saves its contents in the Unix file DISK between runs. 36 | * Since the file can be large, you should get in the habit of removing it 37 | * before logging off. 38 | * 39 | * @see Kernel 40 | */ 41 | public class Disk implements Runnable { 42 | /////////////////////////////////////////// Disk geometry parameters 43 | 44 | /** The size of a disk block in bytes. */ 45 | public static final int BLOCK_SIZE = 512; 46 | 47 | /** Total size of this disk, in blocks. */ 48 | public final int DISK_SIZE; 49 | 50 | /////////////////////////////////////////// Transient internal state 51 | 52 | /** Current location of the read/write head. */ 53 | protected int currentBlock = 0; 54 | 55 | /** The data stored on the disk. */ 56 | protected byte[] data; 57 | 58 | /** An indication of whether an I/O operation is currently in progress. */ 59 | protected boolean busy; 60 | 61 | /** An indication whether the current I/O operation is a write operation. 62 | * Only meaningful if busy == true. 63 | */ 64 | private boolean isWriting; 65 | 66 | /** The block number to be read/written by the current operation. 67 | * Only meaningful if busy == true. 68 | */ 69 | protected int targetBlock; 70 | 71 | /** Memory buffer to/from which current I/O operation is transferring. 72 | * Only meaningful if busy == true. 73 | */ 74 | private byte[] buffer; 75 | 76 | /** A flag set by beginRead or beginWrite to indicate that a request 77 | * has been submitted. 78 | */ 79 | private boolean requestQueued = false; 80 | 81 | /** A count of read operations performed, for statistics. */ 82 | protected int readCount; 83 | 84 | /** A count of write operations performed, for statistics. */ 85 | protected int writeCount; 86 | 87 | /////////////////////////////////////////// Inner classes 88 | 89 | /** The exception thrown when an illegal operation is attempted on the 90 | * disk. 91 | */ 92 | static protected class DiskException extends RuntimeException { 93 | static final long serialVersionUID = 0; 94 | /** Creates a new exception. 95 | * 96 | * @param s the detail string for the exception. 97 | */ 98 | public DiskException(String s) { 99 | super("*** YOU CRASHED THE DISK: " + s); 100 | } 101 | } 102 | 103 | /////////////////////////////////////////// Constructors 104 | 105 | /** Creates a new Disk. 106 | * If a Unix file named DISK exists in the local Unix directory, the 107 | * simulated disk contents are initialized from the Unix file. 108 | * It is an error if the DISK file exists but its size does not match 109 | * "size". 110 | * If there is no DISK file, the first block of the simulated disk is 111 | * cleared to nulls and the rest is filled with random junk. 112 | * 113 | * @param size the total size of this disk, in blocks. 114 | */ 115 | public Disk(int size) { 116 | File diskName = new File("DISK"); 117 | if (diskName.exists()) { 118 | if (diskName.length() != size * BLOCK_SIZE) { 119 | throw new DiskException( 120 | "File DISK exists but is the wrong size"); 121 | } 122 | } 123 | this.DISK_SIZE = size; 124 | if (size < 1) { 125 | throw new DiskException("A disk must have at least one block!"); 126 | } 127 | // NOTE: the "new" operator always clears the result object to nulls 128 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 129 | int count = BLOCK_SIZE; 130 | try { 131 | FileInputStream is = new FileInputStream("DISK"); 132 | is.read(data); 133 | out.printf("Restored %d bytes from file DISK\n", count); 134 | is.close(); 135 | return; 136 | } catch (FileNotFoundException e) { 137 | out.println("Creating new disk"); 138 | this.format(); 139 | } catch (Exception e) { 140 | e.printStackTrace(); 141 | exit(1); 142 | } 143 | //*****************Commented out by Brett Duncan 144 | // byte[] junk = new byte[BLOCK_SIZE]; 145 | // for (int i = 0; i < BLOCK_SIZE; ) { 146 | // junk[i++] = 74; 147 | // junk[i++] = 85; 148 | // junk[i++] = 78; 149 | // junk[i++] = 75; 150 | // } 151 | // for (int i = 1; i < DISK_SIZE; i++) { 152 | // arraycopy( 153 | // junk, 0, 154 | // data, i * BLOCK_SIZE, 155 | // BLOCK_SIZE); 156 | // } 157 | } // Disk(int) 158 | 159 | /////////////////////////////////////////// Methods 160 | 161 | /** 162 | * Formats the disk. 163 | */ 164 | public void format() { 165 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 166 | //*****************Commented out by Brett Duncan 167 | // byte[] junk = new byte[BLOCK_SIZE]; 168 | // for (int i = 0; i < BLOCK_SIZE; ) { 169 | // junk[i++] = 74; 170 | // junk[i++] = 85; 171 | // junk[i++] = 78; 172 | // junk[i++] = 75; 173 | // } 174 | // for (int i = 1; i < DISK_SIZE; i++) { 175 | // arraycopy( 176 | // junk, 0, 177 | // data, i * BLOCK_SIZE, 178 | // BLOCK_SIZE); 179 | // } 180 | } 181 | 182 | /** Saves the contents of this Disk. 183 | * The contents of this disk will be forced out to a file named 184 | * DISK so that they can be restored on the next run of this program. 185 | * This file could be quite big, so delete it before you log out. 186 | * Also prints some statistics on disk operations. 187 | */ 188 | public void flush() { 189 | try { 190 | out.println("Saving contents to DISK file..."); 191 | FileOutputStream os = new FileOutputStream("DISK"); 192 | os.write(data); 193 | os.close(); 194 | out.printf( 195 | "%d read operations and %d write operations performed\n", 196 | readCount, writeCount); 197 | } catch(Exception e) { 198 | e.printStackTrace(); 199 | exit(1); 200 | } 201 | } // flush() 202 | 203 | /** Sleeps for a while to simulate the delay in seeking and transferring 204 | * data. 205 | * @param targetBlock the block number to which we have to seek. 206 | */ 207 | protected void delay(int targetBlock) { 208 | int sleepTime = 10 + Math.abs(targetBlock - currentBlock) / 5; 209 | try { 210 | Thread.sleep(sleepTime); 211 | } catch (Exception e) { 212 | e.printStackTrace(); 213 | } 214 | } // delay(int) 215 | 216 | /** Starts a new read operation. 217 | * @param blockNumber The block number to read from. 218 | * @param buffer A data area to hold the data read. This array must be 219 | * allocated by the caller and have length of at least 220 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 221 | * bytes of the array will be modified. 222 | */ 223 | public synchronized void beginRead(int blockNumber, byte[] buffer) { 224 | if ( 225 | blockNumber < 0 226 | || blockNumber >= DISK_SIZE 227 | || buffer == null 228 | || buffer.length < BLOCK_SIZE) 229 | { 230 | throw new DiskException("Illegal disk read request: " 231 | + " block number " + blockNumber 232 | + " buffer " + buffer); 233 | } 234 | 235 | if (busy) { 236 | throw new DiskException("Disk read attempted " 237 | + " while the disk was still busy."); 238 | } 239 | 240 | isWriting = false; 241 | this.buffer = buffer; 242 | targetBlock = blockNumber; 243 | requestQueued = true; 244 | 245 | notify(); 246 | } // beginRead(int, byte[]) 247 | 248 | /** Starts a new write operation. 249 | * @param blockNumber The block number to write to. 250 | * @param buffer A data area containing the data to be written. This array 251 | * must be allocated by the caller and have length of at least 252 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 253 | * bytes of the array will be sent to the disk. 254 | */ 255 | public synchronized void beginWrite(int blockNumber, byte[] buffer) { 256 | if ( 257 | blockNumber < 0 258 | || blockNumber >= DISK_SIZE 259 | || buffer == null 260 | || buffer.length < BLOCK_SIZE) 261 | { 262 | throw new DiskException("Illegal disk write request: " 263 | + " block number " + blockNumber 264 | + " buffer " + buffer); 265 | } 266 | 267 | if (busy) { 268 | throw new DiskException("Disk write attempted " 269 | + " while the disk was still busy."); 270 | } 271 | 272 | isWriting = true; 273 | this.buffer = buffer; 274 | targetBlock = blockNumber; 275 | requestQueued = true; 276 | 277 | notify(); 278 | } // beginWrite(int, byte[]) 279 | 280 | /** Waits for a call to beginRead or beginWrite. */ 281 | protected synchronized void waitForRequest() { 282 | while(!requestQueued) { 283 | try { 284 | wait(); 285 | } catch (Exception e) { 286 | e.printStackTrace(); 287 | } 288 | } 289 | requestQueued = false; 290 | busy = true; 291 | } // waitForRequest() 292 | 293 | /** Indicates to the CPU that the current operation has completed. */ 294 | protected void finishOperation() { 295 | synchronized (this) { 296 | busy = false; 297 | currentBlock = targetBlock; 298 | } 299 | // NOTE: The interrupt needs to be outside the critical section 300 | // to avoid a race condition: The interrupt handler in the kernel 301 | // may wish to call beginRead or beginWrite (perhaps indirectly), 302 | // which would deadlock if the interrupt handler were invoked with 303 | // the disk mutex locked. 304 | Kernel.interrupt(Kernel.INTERRUPT_DISK, 305 | 0,0,null,null,null); 306 | } // finishOperation() 307 | 308 | /** This method simulates the internal microprocessor of the disk 309 | * controler. It repeatedly waits for a start signal, does an I/O 310 | * operation, and sends an interrupt to the CPU. 311 | * This method should not be called directly. 312 | */ 313 | public void run() { 314 | for (;;) { 315 | waitForRequest(); 316 | 317 | // Pause to do the operation 318 | delay(targetBlock); 319 | 320 | // Move the data. 321 | if (isWriting) { 322 | arraycopy( 323 | buffer, 0, 324 | data, targetBlock * BLOCK_SIZE, 325 | BLOCK_SIZE); 326 | writeCount++; 327 | } else { 328 | arraycopy( 329 | data, targetBlock * BLOCK_SIZE, 330 | buffer, 0, 331 | BLOCK_SIZE); 332 | readCount++; 333 | } 334 | 335 | // Signal completion 336 | finishOperation(); 337 | } 338 | } // run() 339 | } // Disk 340 | -------------------------------------------------------------------------------- /src/minikernel/FastDisk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: FastDisk.java,v 1.15 2006/11/22 21:47:00 solomon Exp $ */ 9 | 10 | /** A new and improved Disk. 11 | * You may not change this class. 12 | *
13 | * This disk is so much faster than the previous model that read and write 14 | * operations appear to finish in no time. Because disk is so fast, beginRead 15 | * and beginWrite wait for the operation to finish rather than causing a CPU 16 | * interrupt when they complete. 17 | *
18 | * @see Disk 19 | * @see Kernel 20 | */ 21 | public class FastDisk extends Disk { 22 | /** Creates a new FastDisk. 23 | * @param size the total size of this disk, in blocks. 24 | */ 25 | public FastDisk(int size) { 26 | super(size); 27 | if (size < 0 || size >= (1<<15)) { 28 | throw new DiskException( 29 | String.format( 30 | "Cannot make a FastDisk with %d blocks. Max size is %d.", 31 | size, 1<<15)); 32 | } 33 | } // FastDisk 34 | 35 | /** Performs a read operation. 36 | * When this method returns, the operation is complete. 37 | * @param blockNumber The block number to read from. 38 | * @param buffer a data area to hold the data read. 39 | * @see Disk#beginRead(int, byte[]) 40 | */ 41 | public void read(int blockNumber, byte buffer[]) { 42 | System.arraycopy( 43 | data, blockNumber * BLOCK_SIZE, 44 | buffer, 0, 45 | BLOCK_SIZE); 46 | readCount++; 47 | } // read(int, byte[]) 48 | 49 | /** Performs a write operation. 50 | * When this method returns, the operation is complete. 51 | * @param blockNumber The block number to write to. 52 | * @param buffer a data area to hold the data to be written. 53 | * @see Disk#beginWrite(int, byte[]) 54 | */ 55 | public void write(int blockNumber, byte buffer[]) { 56 | System.arraycopy( 57 | buffer, 0, 58 | data, blockNumber * BLOCK_SIZE, 59 | BLOCK_SIZE); 60 | writeCount++; 61 | } // write(int, byte[]) 62 | 63 | //************Code added by Brett Duncan*********************// 64 | @Override 65 | public void format() { 66 | data = new byte[DISK_SIZE * BLOCK_SIZE]; 67 | for (int i = 1; i < DISK_SIZE; i++) { 68 | data[i] = '0'; 69 | } 70 | } 71 | 72 | public int getBlockSize() { 73 | return BLOCK_SIZE; 74 | } 75 | 76 | //************End code added by Brett Duncan*********************// 77 | 78 | /** Starts a new read operation. 79 | * @param blockNumber The block number to read from. 80 | * @param buffer A data area to hold the data read. This array must be 81 | * allocated by the caller and have length of at least 82 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 83 | * bytes of the array will be modified. 84 | * @deprecated Do not use this method. Use read instead. 85 | */ 86 | @Deprecated 87 | public synchronized void beginRead(int blockNumber, byte buffer[]) { 88 | throw new UnsupportedOperationException( 89 | "Don't use beginRead. Use read"); 90 | } // beginRead(int, byte[]) 91 | 92 | /** Starts a new write operation. 93 | * @param blockNumber The block number to write to. 94 | * @param buffer A data area containing the data to be written. This array 95 | * must be allocated by the caller and have length of at least 96 | * BLOCK_SIZE. If it is larger, only the first BLOCK_SIZE 97 | * bytes of the array will be sent to the disk. 98 | * @deprecated Do not use this method. Use read instead. 99 | */ 100 | @Deprecated 101 | public synchronized void beginWrite(int blockNumber, byte buffer[]) { 102 | throw new UnsupportedOperationException( 103 | "Don't use beginWrite. Use write"); 104 | } // beginWrite byte[]) 105 | } // FastDisk 106 | -------------------------------------------------------------------------------- /src/minikernel/FileSys.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | /* $Id: FileSys.java.src,v 1.4 2007/04/25 14:12:25 solomon Exp $ */ 9 | 10 | import java.util.*; 11 | import java.io.*; 12 | import static java.lang.System.*; 13 | 14 | /** A file system. */ 15 | 16 | public class FileSys { 17 | /** The disk holding this file system. */ 18 | private FastDisk disk; 19 | 20 | //************Code added by Brett Duncan*********************// 21 | private String currDir; 22 | private String fileTable[]; 23 | 24 | //************End code added by Brett Duncan*********************// 25 | 26 | /** Initializes a FileSys instance for managing a disk. 27 | * 28 | * @param disk the disk containing the persistent data. 29 | */ 30 | public FileSys(FastDisk disk) { 31 | this.disk = disk; 32 | 33 | //************Code added by Brett Duncan*********************// 34 | currDir = "/"; 35 | 36 | 37 | fileTable = new String[100]; 38 | int startBlock = 1; //The starting block where files begin to be stored. 39 | 40 | byte freeMap[] = new byte[disk.getBlockSize()]; 41 | 42 | disk.read(0, freeMap); 43 | 44 | byte tempBuffer[] = new byte[disk.getBlockSize()]; 45 | for (int i = startBlock; i < fileTable.length; i++) { 46 | if (freeMap[i] == '1') { 47 | disk.read(i, tempBuffer); 48 | 49 | String fileName = ""; 50 | for (int j = 0; j < 32; j++) { 51 | fileName += (char) tempBuffer[j]; 52 | } 53 | fileTable[i] = fileName; 54 | } 55 | } 56 | 57 | /* 58 | for (int i = startBlock; i < fileTable.length; i++) { 59 | byte fileNameBuffer[] = new byte[32]; 60 | disk.read(i, 32, fileNameBuffer); 61 | fileTable[i] = new String(fileNameBuffer); 62 | }*/ 63 | 64 | // For debugging purposes, print out the fileTable[] array. 65 | // for (int i = startBlock; i < fileTable.length; i++) { 66 | // System.out.printf("fileTable[%d] = %s\n", i, fileTable[i]); 67 | // } 68 | 69 | //************End code added by Brett Duncan*********************// 70 | 71 | } // FileSys(FastDisk) 72 | 73 | //************Code added by Brett Duncan*********************// 74 | 75 | public FastDisk getDisk() { 76 | return disk; 77 | } 78 | 79 | public int getBlockSizeOfDisk() { 80 | return disk.getBlockSize(); 81 | } 82 | 83 | public void setDisk(Disk disk) { 84 | this.disk = (FastDisk) disk; 85 | } 86 | 87 | public String[] getFileTable() { 88 | return fileTable; 89 | } 90 | 91 | public void updateFileTable(int targetBlock, String newFileName) { 92 | fileTable[targetBlock] = newFileName; 93 | } 94 | 95 | //************End code added by Brett Duncan*********************// 96 | 97 | } // FileSys 98 | -------------------------------------------------------------------------------- /src/minikernel/FileTester.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Code acquired from http://pages.cs.wisc.edu/~solomon/cs537/project5/. 3 | * Modified by Brett Duncan for the operating systems project. 4 | */ 5 | 6 | package minikernel; 7 | 8 | import java.io.*; 9 | import java.util.*; 10 | import static java.lang.System.*; 11 | 12 | /** Basic driver program to be used as a shell for the MiniKernel for project 5. 13 | * It can be run in two modes: 14 | *
20 | * The testfile consists of commands to the driver program (one per line) as 21 | * well as comments. Comments beginning with /* will be ignored completely by 22 | * the driver. Comments beginning with // will be echoed to the output. 23 | *
24 | * See the test files test*.data for examples.
25 | *
26 | * Revised, May 7, 2007.
27 | * $Id: FileTester.java,v 1.21 2007/05/07 15:04:02 solomon Exp $
28 | */
29 | public class FileTester {
30 | /** Synopsis of commands. */
31 | private static String[] helpInfo = {
32 | "help",
33 | "quit",
34 | "format",
35 | "cd pathname",
36 | "pwd",
37 | "create pathname",
38 | "read pathname",
39 | "write pathname data",
40 | "writeln pathname",
41 | "rm pathname",
42 | "mkdir pathname",
43 | "rmdir pathname",
44 | "ln oldpath newpath",
45 | "readlink pathname",
46 | "ls [ dirname ]",
47 | };
48 |
49 | /** Disk block size, as retrieved from the Disk (cheating!). */
50 | private static int blockSize;
51 |
52 | /** Main program.
53 | * @param args command-line arguments (there should be at most one:
54 | * the name of a test file from which to read commands).
55 | */
56 | public static void main(String [] args) {
57 |
58 | //************Code added by Brett Duncan*********************//
59 |
60 | //Power on the disk
61 | Kernel.interrupt(Kernel.INTERRUPT_POWER_ON, 10, 0, new FastDisk(100), null, null);
62 | //XXX: Script to test commands
63 | // args = new String[1];
64 | //// args[0] = "test1.script";
65 | // args[0] = "mytest.script";
66 |
67 | //************End code added by Brett Duncan*********************//
68 |
69 | // NB: This program is designed only to test the file system support
70 | // of the kernel, so it "cheats" in using non-kernel operations to
71 | // read commands and write diagnostics.
72 | if (args.length > 1) {
73 | System.err.println("usage: FileTester [ script-file ]");
74 | System.exit(0);
75 | }
76 |
77 | // blockSize = Disk.BLOCK_SIZE;
78 | // This is a bit of a cheat. We really should have a Kernel call
79 | // to get this information.
80 | blockSize = Library.getBlockSizeOfDisk();
81 | //XXX: Fixed it.
82 |
83 | // Is the input coming from a file?
84 | boolean fromFile = (args.length == 1);
85 |
86 | // Create a stream for input
87 | BufferedReader input = null;
88 |
89 | // Open our input stream
90 | if (fromFile) {
91 | try {
92 | input = new BufferedReader(new FileReader(args[0]));
93 | } catch (FileNotFoundException e) {
94 | System.err.println("Error: Script file "
95 | + args[0] + " not found.");
96 | System.exit(1);
97 | }
98 | } else {
99 | input = new BufferedReader(new InputStreamReader(System.in));
100 | }
101 |
102 | // Cycle through user or file input
103 | for (;;) {
104 | String cmd = null;
105 | try {
106 | // Print out the prompt for the user
107 | if (!fromFile) {
108 | out.printf("--> ");
109 | System.out.flush();
110 | }
111 |
112 | // Read in a line
113 | String line = input.readLine();
114 |
115 | // Check for EOF and empty lines
116 | if (line == null) {
117 | // End of file (Ctrl-D for interactive input)
118 | return;
119 | }
120 | line = line.trim();
121 | if (line.length() == 0) {
122 | continue;
123 | }
124 |
125 | // Handle comments and echoing
126 | if (line.startsWith("//")) {
127 | if (fromFile) {
128 | out.printf("%s\n", line);
129 | }
130 | continue;
131 | }
132 | if (line.startsWith("/*")) {
133 | continue;
134 | }
135 |
136 | // Echo the command line
137 | if (fromFile) {
138 | out.printf("--> %s\n", line);
139 | }
140 |
141 | // Parse the command line
142 | StringTokenizer st = new StringTokenizer(line);
143 | cmd = st.nextToken();
144 |
145 | // Call the function that corresponds to the command
146 | int result = 0;
147 | if (cmd.equalsIgnoreCase("quit")) {
148 | Library.shutdown();
149 | return;
150 | } else if (cmd.equalsIgnoreCase("help") || cmd.equals("?")) {
151 | help();
152 | continue;
153 | } else if (cmd.equalsIgnoreCase("format")) {
154 | result = Library.format();
155 | } else if (cmd.equalsIgnoreCase("cd")) {
156 | result = Library.chdir(st.nextToken());
157 | } else if (cmd.equalsIgnoreCase("pwd")) {
158 | result = pwd();
159 | } else if (cmd.equalsIgnoreCase("create")) {
160 | result = Library.create(st.nextToken());
161 | } else if (cmd.equalsIgnoreCase("read")) {
162 | result = readTest(st.nextToken(), false);
163 | } else if (cmd.equalsIgnoreCase("write")) {
164 | result = writeTest(st.nextToken(), line);
165 | } else if (cmd.equalsIgnoreCase("writeln")) {
166 | result = writeLines(st.nextToken(), input);
167 | } else if (cmd.equalsIgnoreCase("rm")) {
168 | result = Library.delete(st.nextToken());
169 | } else if (cmd.equalsIgnoreCase("mkdir")) {
170 | result = Library.mkdir(st.nextToken());
171 | } else if (cmd.equalsIgnoreCase("rmdir")) {
172 | result = Library.rmdir(st.nextToken());
173 | } else if (cmd.equalsIgnoreCase("ln")) {
174 | String oldName = st.nextToken();
175 | String newName = st.nextToken();
176 | result = Library.symlink(oldName, newName);
177 | } else if (cmd.equalsIgnoreCase("readlink")) {
178 | result = readTest(st.nextToken(), true);
179 | } else if (cmd.equalsIgnoreCase("ls")) {
180 | if (st.hasMoreTokens()) {
181 | result = dumpDir(st.nextToken());
182 | } else {
183 | result = dumpDir(".");
184 | }
185 | } else {
186 | out.printf("unknown command\n");
187 | continue;
188 | }
189 |
190 | // Print out the result of the function call
191 | if (result != 0) {
192 | if (result == -1) {
193 | out.printf("*** System call failed\n");
194 | } else {
195 | out.printf("*** Bad result %d from system call\n",
196 | result);
197 | }
198 | }
199 | } catch (NoSuchElementException e) {
200 | // Handler for nextToken()
201 | out.printf("Incorrect number of arguments\n");
202 | help(cmd);
203 | } catch (IOException e) {
204 | e.printStackTrace();
205 | return;
206 | }
207 | } // for (;;)
208 | } // main(String[])
209 |
210 | /** Prints a list of available commands. */
211 | private static void help() {
212 | out.printf("Commands are:\n");
213 | for (int i = 0; i < helpInfo.length; i++) {
214 | out.printf(" %s\n", helpInfo[i]);
215 | }
216 | } // help()
217 |
218 | /** Prints help for command "cmd".
219 | * @param cmd the name of the command.
220 | */
221 | private static void help(String cmd) {
222 | for (int i = 0; i < helpInfo.length; i++) {
223 | if (helpInfo[i].startsWith(cmd)) {
224 | out.printf("usage: %s\n", helpInfo[i]);
225 | return;
226 | }
227 | }
228 | out.printf("unknown command '%s'\n", cmd);
229 | } // help(String)
230 |
231 | /** Reads data from a (simulated) file or symlink using Library.read
232 | * or Library.readlink and displays the results.
233 | * @param fname the name of the file or symlink.
234 | * @param isLink true to read a symlink, false to read an ordinary file.
235 | * @return the result of the Library.read call.
236 | */
237 | private static int readTest(String fname, boolean isLink) {
238 | byte[] buf = new byte[blockSize];
239 | int n = isLink
240 | ? Library.readlink(fname, buf)
241 | : Library.read(fname, buf);
242 | boolean needNewline = false;
243 | if (n < 0) {
244 | return n;
245 | }
246 | for (int i = 0; i < buf.length; i++) {
247 | if (buf[i] != 0) {
248 | showChar(buf[i] & 0xff);
249 | needNewline = (buf[i] != '\n');
250 | }
251 | }
252 | if (needNewline) {
253 | out.printf("\n");
254 | }
255 | return n;
256 | } // readTest(String,boolean)
257 |
258 | /** Writes data to a (simulated) file using Library.write.
259 | * @param fname the name of the file.
260 | * @param info a source of data.
261 | * @return the result of the Library.write call.
262 | */
263 | private static int writeTest(String fname, String info) {
264 | // Info has the format 'write fname one two three ...
265 | int p;
266 | p = info.indexOf(' ');
267 | if (p >= 0) {
268 | p = info.indexOf(' ', p + 1);
269 | if (p < 0) {
270 | p = info.length();
271 | } else {
272 | p++;
273 | }
274 | } else {
275 | p = 0;
276 | }
277 | byte[] buf = new byte[Math.max(blockSize, info.length() - p)];
278 | int i = 0;
279 | while (p < info.length()) {
280 | buf[i++] = (byte) info.charAt(p++);
281 | }
282 | return Library.write(fname, buf);
283 | } // writeTest(String, byte[])
284 |
285 | /** Write data to a (simulated) file using Library.write.
286 | * Data comes from the following lines in the input stream.
287 | * @param fname the name of the file.
288 | * @param in the input stream.
289 | * @return the result of the Library.write call.
290 | */
291 | private static int writeLines(String fname, BufferedReader in) {
292 | try {
293 | byte[] buf = new byte[blockSize];
294 | int i = 0;
295 | for (;;) {
296 | String line = in.readLine();
297 | if (line == null || line.equals(".")) {
298 | break;
299 | }
300 | for (int j = 0; j < line.length(); j++) {
301 | if (i >= buf.length) {
302 | byte[] newBuf = new byte[buf.length * 2];
303 | System.arraycopy(buf, 0, newBuf, 0, buf.length);
304 | buf = newBuf;
305 | }
306 | buf[i++] = (byte) line.charAt(j);
307 | }
308 | if (i >= buf.length) {
309 | break;
310 | }
311 | buf[i++] = '\n';
312 | }
313 | return Library.write(fname, buf);
314 | } catch (IOException e) {
315 | e.printStackTrace();
316 | return -1;
317 | }
318 | } // writeLines(String, BufferedReader)
319 |
320 | /** Display a readable representation of a byte.
321 | * @param b the byte to display as a number in the range 0..255.
322 | */
323 | private static void showChar(int b) {
324 | if (b >= ' ' && b <= '~') {
325 | out.printf("%c", (char)b);
326 | return;
327 | }
328 | if (b == '\n') {
329 | out.printf("\\n\n");
330 | return;
331 | }
332 | if (b == '\\') {
333 | out.printf("\\\\");
334 | return;
335 | }
336 | out.printf("\\%03o", b);
337 | } // showChar(int)
338 |
339 | /** Displays the contents of a directory.
340 | * @param dirname the name of the directory.
341 | * @return the result of the readdir call.
342 | */
343 | private static int dumpDir(String dirname) {
344 | byte[] buf = new byte[blockSize];
345 | int n = Library.readdir(dirname, buf);
346 | if (n < 0) {
347 | return n;
348 | }
349 | for (int i = 0; i < buf.length; i += 16) {
350 | int block = ((buf[i] & 0xff) << 8) + (buf [i+1] & 0xff);
351 | if (block == 0) {
352 | continue;
353 | }
354 | StringBuffer sb = new StringBuffer();
355 | for (int j = 3; j < 16; j++) {
356 | if (buf[i + j] == 0) {
357 | break;
358 | }
359 | sb.append((char) buf[i + j]);
360 | }
361 | String fname = sb.toString();
362 | out.printf("%s %s", block, fname);
363 | switch (buf[i + 2]) {
364 | case 'O':
365 | break;
366 | case 'D':
367 | out.printf("/");
368 | break;
369 | case 'L':
370 | out.printf(" -> ");
371 | byte[] buf1 = new byte[blockSize];
372 | n = Library.readlink(dirname + "/" + fname, buf1);
373 | if (n < 0) {
374 | return n;
375 | }
376 | for (int j = 0; j < buf1.length; j++) {
377 | if (buf1[j] == 0) {
378 | break;
379 | }
380 | out.printf("%c", (char) buf1[j]);
381 | }
382 | break;
383 | default:
384 | out.printf("?type \\%03o?", buf[i + 2]);
385 | //out.printf("
18 | * There is only one public interface to this class: interrupt().
19 | * System calls, disk notification, and power on messages all arrive
20 | * by way of this function.
21 | *
22 | * See the list of SYSCALL_XXX constants to learn what
23 | * system calls are currently supported.
24 | *
25 | * @see Disk
26 | * @see Library
27 | */
28 | public class Kernel {
29 |
30 | /** No public instances. */
31 | private Kernel() {}
32 |
33 | //////////////// Values for the first parameter ("kind") to interrupt()
34 |
35 | /** An interrupt kind indicating that a user program caused the interrupt.
36 | * A user may call this function to perform a system call.
203 | * In that case, set kind to INTERRUPT_USER
204 | * and set i1 to the system call number. Other
205 | * parameters should be set as the system call requires.
206 | *
207 | * A disk may call this function to indicate the current operation
208 | * has completed. In that case, kind will be
209 | * INTERRUPT_DISK and all parameters will be zero or null.
210 | *
216 | * The boot code may call this function to indicate that the computer
217 | * has been turned on and it is time to start the first program
218 | * and use the disk. In that case, kind will be
219 | * INTERRUPT_POWER_ON, o1 will point to the Disk to be
220 | * used, o2 will be a String containing the name of the shell to use,
221 | * i1 will indicate the size of the buffer cache,
222 | * and all other parameters will be zero or null.
223 | *
224 | * Since different system calls require different parameters, this
225 | * method has a variety of arguments of various types. Any one
226 | * system call will use at most a few of them. The others should be
227 | * zero or null.
228 | *
229 | * @param kind the kind of system call, one of the
230 | * INTERRUPT_XXX codes.
231 | * @param i1 the first integer parameter. If kind ==
232 | * INTERRUPT_USER, i1 should be one of the
233 | * SYSTEM_XXX codes to indicate which system call is being
234 | * invoked.
235 | * @param i2 another integer parameter.
236 | * @param o1 a parameter of some object type.
237 | * @param o2 another parameter of some object type.
238 | * @param a a byte-array parameter (generally used for binary input/output).
239 | *
240 | * @return a negative number indicating an error code, or other
241 | * values depending on the system call.
242 | */
243 | public static int interrupt(int kind, int i1, int i2,
244 | Object o1, Object o2, byte a[])
245 | {
246 | try {
247 | switch (kind) {
248 | case INTERRUPT_USER:
249 | switch (i1) {
250 | case SYSCALL_OUTPUT:
251 | return doOutput((String)o1);
252 |
253 | case SYSCALL_INPUT:
254 | return doInput((StringBuffer)o1);
255 |
256 | case SYSCALL_EXEC:
257 | return doExec((String)o1,(String[])o2);
258 |
259 | case SYSCALL_JOIN:
260 | return doJoin(i2);
261 |
262 | //************Code added by Brett Duncan*********************//
263 | //XXX: SYSCALL cases
264 |
265 | case SYSCALL_FORMAT:
266 | return doFormat();
267 |
268 | case SYSCALL_CREATE:
269 | return doCreateFile(a);
270 |
271 | case SYSCALL_READ:
272 | return doRead(a);
273 |
274 | case SYSCALL_WRITE:
275 | return doWrite(((String) o1).getBytes(), a);
276 |
277 | case SYSCALL_DELETE:
278 | return doDelete(((String) o1).getBytes());
279 |
280 | case SYSCALL_READDIR:
281 | return doReadDir();
282 |
283 | case SYSCALL_SHUTDOWN:
284 | doShutdown();
285 | break;
286 |
287 | case SYSCALL_GET_BLOCK_SIZE:
288 | return doGetBlockSize();
289 |
290 | //************End code added by Brett Duncan*********************//
291 |
292 | default:
293 | return ERROR_BAD_ARGUMENT;
294 | }
295 |
296 | case INTERRUPT_DISK:
297 | break;
298 |
299 | case INTERRUPT_POWER_ON:
300 | doPowerOn(i1, o1, o2);
301 | // doShutdown();
302 | break;
303 |
304 | default:
305 | return ERROR_BAD_ARGUMENT;
306 | } // switch (kind)
307 | } catch (Exception e) {
308 | // Most likely, we arrived here due to a bad cast.
309 | e.printStackTrace();
310 | return ERROR_BAD_ARGUMENT;
311 | }
312 | return 0;
313 | } // interrupt(int, int, int, Object, Object, byte[])
314 |
315 | /** Performs the actions associated with a POWER_ON interrupt.
316 | * @param i1 the first int parameter to the interrupt (the disk cache size)
317 | * @param o1 the first Object parameter to the interrupt (the Disk).
318 | * @param o2 the second Object parameter to the interrupt (the shell
319 | * command-line).
320 | */
321 | private static void doPowerOn(int i1, Object o1, Object o2) {
322 | cacheSize = i1;
323 |
324 | //************Code added by Brett Duncan*********************//
325 | // disk = (Disk)o1;
326 | disk = (FastDisk)o1;
327 | //Create new file system object, which makes managing the file system
328 | //easier.
329 | filesys = new FileSys(disk);
330 | //************End code added by Brett Duncan*********************//
331 |
332 | String shellCommand = (String) o2;
333 |
334 | doOutput("Kernel: Disk is " + filesys.getBlockSizeOfDisk() + " blocks\n");
335 | doOutput("Kernel: Disk cache size is " + i1 + " blocks\n");
336 |
337 | //Commented this out because we're just gonna use the default shell
338 | //in FileTester.java.
339 | /*doOutput("Kernel: Loading initial program.\n");
340 |
341 | StringTokenizer st = new StringTokenizer(shellCommand);
342 | int n = st.countTokens();
343 | if (n < 1) {
344 | doOutput("Kernel: No shell specified\n");
345 | exit(1);
346 | }
347 |
348 | String shellName = st.nextToken();
349 | String[] args = new String[n - 1];
350 | for (int i = 1; i < n; i++) {
351 | args[i - 1] = st.nextToken();
352 | }
353 |
354 | if (doExecAndWait(shellName, args) < 0) {
355 | doOutput("Kernel: Unable to start " + shellCommand + "!\n");
356 | exit(1);
357 | } else {
358 | doOutput("Kernel: " + shellCommand + " has terminated.\n");
359 | }
360 | */
361 | Launcher.joinAll();
362 | } // doPowerOn(int, Object, Object)
363 |
364 | /** Does any "shutdown" activities required after all activities started by
365 | * a POWER_ON interrupt have completed.
366 | */
367 | private static void doShutdown() {
368 |
369 | // disk.flush();
370 | //************Code added by Brett Duncan*********************//
371 | //XXX: Shutdown method
372 | filesys.getDisk().flush();
373 | //************End code added by Brett Duncan*********************//
374 | } // doShutdown()
375 |
376 | /** Displays a message on the console.
377 | * @param msg the message to display
378 | */
379 | private static int doOutput(String msg) {
380 | out.print(msg);
381 | return 0;
382 | } // doOutput(String)
383 |
384 | private static BufferedReader br
385 | = new BufferedReader(new InputStreamReader(in));
386 |
387 | /** Reads a line from the console into a StringBuffer.
388 | * @param sb a place to put the line of input.
389 | */
390 | private static int doInput(StringBuffer sb) {
391 | try {
392 | String s = br.readLine();
393 | if (s==null) {
394 | return ERROR_END_OF_FILE;
395 | }
396 | sb.append(s);
397 | return 0;
398 | } catch (IOException t) {
399 | t.printStackTrace();
400 | return ERROR_IO;
401 | }
402 | } // doInput(StringBuffer)
403 |
404 | /** Loads a program and runs it.
405 | * Blocks the caller until the program has terminated.
406 | * @param command the program to run.
407 | * @param args command-line args to pass to the program.
408 | * @return the program's return code on success, ERROR_NO_CLASS,
409 | * ERROR_NO_MAIN, or ERROR_BAD_COMMAND if the command cannot be run, or
410 | * ERROR_IN_CHILD if the program throws an uncaught exception.
411 | */
412 | private static int doExecAndWait(String command, String args[]) {
413 | Launcher l;
414 | try {
415 | l = new Launcher(command, args);
416 | } catch (ClassNotFoundException e) {
417 | return ERROR_NO_CLASS;
418 | } catch (NoSuchMethodException e) {
419 | return ERROR_NO_MAIN;
420 | } catch (Exception e) {
421 | e.printStackTrace();
422 | return ERROR_BAD_COMMAND;
423 | }
424 | try {
425 | l.run();
426 | l.delete();
427 | return l.returnCode;
428 | } catch (Exception e) {
429 | e.printStackTrace();
430 | return ERROR_IN_CHILD;
431 | }
432 | } // doExecAndWait(String, String[])
433 |
434 | /** Loads a program and runs it in the background.
435 | * Does not wait for the program to terminate.
436 | * @param command the program to run.
437 | * @param args command-line args to pass to the program.
438 | * @return a process id on success or ERROR_NO_CLASS, ERROR_NO_MAIN, or
439 | * ERROR_BAD_COMMAND if the command cannot be run.
440 | */
441 | private static int doExec(String command, String args[]) {
442 | try {
443 | Launcher l = new Launcher(command, args);
444 | l.start();
445 | return l.pid;
446 | } catch (ClassNotFoundException e) {
447 | return ERROR_NO_CLASS;
448 | } catch (NoSuchMethodException e) {
449 | return ERROR_NO_MAIN;
450 | } catch (Exception e) {
451 | e.printStackTrace();
452 | return ERROR_BAD_COMMAND;
453 | }
454 | } // doExec(String, String[])
455 |
456 | /** Waits for a program previous started by doExec to terminate.
457 | * @param pid the process id of the program.
458 | * @return the return code returned by the program.
459 | */
460 | private static int doJoin(int pid) {
461 | return Launcher.joinOne(pid);
462 | } // doJoin(int)
463 |
464 | //************Code added by Brett Duncan*********************//
465 | //XXX: New doXXX methods
466 |
467 | /**
468 | * Libary function to get the block size of the file system's disk.
469 | * @return The block size, in bytes, of the disk.
470 | */
471 | private static int doGetBlockSize() {
472 | return filesys.getBlockSizeOfDisk();
473 | }
474 |
475 | /**
476 | * This method initializes the contents of the disk with any data structures
477 | * necessary to represent an "empty" file system. This method should create
478 | * an "empty" root directory "/".
479 | * @return 0 if successful, -1 if there was an error.
480 | */
481 | private static int doFormat() {
482 | //Create new file system object.
483 | // filesys = new FileSys(new FastDisk(100));
484 |
485 | filesys.getDisk().format();
486 |
487 | doOutput("Kernel: Disk formatted.\n");
488 |
489 | return 0;
490 | }
491 |
492 | /**
493 | * Creates a new file with the indicated filename. The initial contents of
494 | * the file are all null (zero) bytes.
495 | * @param pathName The bytes containing the file name.
496 | * @return 0 if successful, -1 if there was an error.
497 | */
498 | private static int doCreateFile(byte pathName[]) {
499 |
500 | //Check if file name length is > 32.
501 | if (pathName.length > 32) {
502 | doOutput("Kernel: User error: File name too long!\n");
503 | return -1;
504 | }
505 |
506 | //A 1 block byte array that will hold the free map
507 | byte freeMap[] = new byte[filesys.getBlockSizeOfDisk()];
508 |
509 | //Read block 0 into freeMap
510 | filesys.getDisk().read(0, freeMap);
511 |
512 | //Check if the file specified by pathName already exists.
513 | String pathNameString = new String(pathName);
514 | for (int i = 1; i < 100; i++) {
515 | if (freeMap[i] == '1'
516 | && pathNameString.equals(filesys.getFileTable()[i].trim())) {
517 |
518 | doOutput("Kernel: User error: File name already exists at"
519 | + "block " + i + "!\n");
520 | return -1;
521 | }
522 | }
523 |
524 | //The target block to create the file to.
525 | int targetBlock = 1;
526 | //Search freeMap for the next 0, which will be the target block.
527 | for (int i = 1; i < filesys.getDisk().DISK_SIZE; i++) {
528 | if (freeMap[i] == '0') {
529 | targetBlock = i;
530 | break;
531 | }
532 | }
533 |
534 | //Byte array that will hold the path name and file contents.
535 | byte pathAndContents[] = new byte[filesys.getBlockSizeOfDisk()];
536 | //Copy path name to pathAndContents
537 | arraycopy(pathName, 0, pathAndContents, 0, pathName.length);
538 |
539 | //Write path and contents to the target block.
540 | filesys.getDisk().write(targetBlock, pathAndContents);
541 |
542 | //Update the free map at targetBlock indicating that block is occupied.
543 | freeMap[targetBlock] = '1';
544 |
545 | //Write free map back to block 0 of the disk.
546 | filesys.getDisk().write(0, freeMap);
547 |
548 | //Set the file system's fileTable at targetBlock to the file name so
549 | //we can look it up and other methods can use it.
550 | filesys.getFileTable()[targetBlock] = new String(pathName);
551 |
552 | //Indicate that file was created successfully.
553 | doOutput("Kernel: Created file ");
554 | for (int i = 0; i < pathName.length; i++) {
555 | doOutput((char) pathName[i] + "");
556 | }
557 | doOutput(" at block " + targetBlock + ". \n");
558 |
559 | return 0;
560 | }
561 |
562 | /**
563 | * Reads and displays the contents of the file specified by pathName[].
564 | * @param pathName The file name to read the contents of.
565 | * @return 0 if successful, -1 if there was an error.
566 | */
567 | private static int doRead(byte pathName[]) {
568 |
569 | //Buffer holding the data read from a block.
570 | byte tempBuffer[] = new byte[FastDisk.BLOCK_SIZE];
571 |
572 | int targetBlock = findTargetBlock(pathName);
573 |
574 | if (targetBlock == -1) {
575 | doOutput("Kernel: User error: File not found.\n");
576 | return -1;
577 | }
578 |
579 | filesys.getDisk().read(targetBlock, tempBuffer);
580 |
581 | doOutput("Kernel: File name: ");
582 | for (int i = 0; i < 32; i++) {
583 | doOutput((char) tempBuffer[i] + "");
584 | }
585 | doOutput(" at block " + targetBlock + ". ");
586 | doOutput("Contents: \n");
587 | for (int i = 32; i < 512; i++) {
588 | doOutput((char) tempBuffer[i] + "");
589 | }
590 | doOutput("\n");
591 | return 0;
592 | }
593 |
594 | /**
595 | * Writes the contents of buffer[] into the file specified by pathName[].
596 | * @param pathName The file to write the buffer to.
597 | * @param buffer The contents to be written to the file.
598 | * @return 0 if successful, -1 if there was an error.
599 | */
600 | private static int doWrite(byte pathName[], byte buffer[]) {
601 |
602 | int targetBlock = findTargetBlock(pathName);
603 |
604 | if (targetBlock == -1) {
605 | doOutput("Kernel: User error: File not found.\n");
606 | return -1;
607 | }
608 |
609 | //The byte array that will hold the file name and buffer (contents to
610 | //write). Do this because in FileTester, it strips off the file name.
611 | byte fileNameAndBuffer[] = new byte[filesys.getBlockSizeOfDisk()];
612 |
613 | //Copy buffer (the contents to write) into fileNameAndBuffer, starting
614 | //at position 32 because in FileTester, it strips off the file name.
615 | arraycopy(buffer, 0,
616 | fileNameAndBuffer, 32,
617 | filesys.getBlockSizeOfDisk() - 32);
618 |
619 | //Add the file name to the beginning of fileNameAndBuffer.
620 | System.arraycopy(pathName, 0,
621 | fileNameAndBuffer, 0,
622 | pathName.length);
623 |
624 | //Now we can write, keeping the file name at the beginning instead of
625 | //it being overwritten by the contents.
626 | filesys.getDisk().write(targetBlock, fileNameAndBuffer);
627 |
628 | return 0;
629 | }
630 |
631 | /**
632 | * Deletes a file from the disk, making the block occupied by the file null.
633 | * @param pathName The file to be deleted.
634 | * @return 0 if successful, -1 if there was an error.
635 | */
636 | private static int doDelete(byte pathName[]) {
637 | //Find target block to delete.
638 | int targetBlock = findTargetBlock(pathName);
639 |
640 | if (targetBlock == -1) {
641 | doOutput("Kernel: User error: File not found.\n");
642 | return -1;
643 | }
644 |
645 | //Create a null byte array of size block size.
646 | byte nullByteArray[] = new byte[filesys.getBlockSizeOfDisk()];
647 | for (int i = 0; i < nullByteArray.length; i++) {
648 | nullByteArray[i] = '\0';
649 | }
650 |
651 | //Write the null byte array to the disk at the target block.
652 | filesys.getDisk().write(targetBlock, nullByteArray);
653 |
654 | //Set the file table at the targetBlock's index to 0, indicating the
655 | //file was deleted.
656 | filesys.getFileTable()[targetBlock] = null;
657 |
658 | //A 1 block byte array that will hold the free map
659 | byte freeMap[] = new byte[filesys.getBlockSizeOfDisk()];
660 |
661 | //Read block 0 into freeMap
662 | filesys.getDisk().read(0, freeMap);
663 |
664 | //Change the bit at the free map to 0, indicating the block is now free.
665 | freeMap[targetBlock] = '0';
666 |
667 | //Write the updated freeMap back to block 0.
668 | filesys.getDisk().write(0, freeMap);
669 |
670 | return 0;
671 |
672 | }
673 |
674 | /**
675 | * Displays the contents of the current directory.
676 | * @return 0 if successful, -1 if there was an error.
677 | */
678 | private static int doReadDir() {
679 |
680 | doOutput("Kernel: ");
681 | for (int i = 1; i < filesys.getFileTable().length; i++) {
682 |
683 | doOutput(
684 | filesys.getFileTable()[i] == null ?
685 | "" :
686 | /*"Block " + i + ": " +*/
687 | filesys.getFileTable()[i].trim() + " "
688 | );
689 |
690 | }
691 | doOutput("\n");
692 |
693 | return 0;
694 | }
695 |
696 | /**
697 | * Helper method for finding the block indicated by pathName[].
698 | * @param pathName The file name to find the block index of.
699 | * @return 0 if successful, -1 if the file was not found.
700 | */
701 | private static int findTargetBlock(byte pathName[]) {
702 |
703 | int targetBlock = -1;
704 |
705 | String pathNameString = new String(pathName);
706 | for (int i = 1; i < filesys.getDisk().DISK_SIZE; i++) {
707 |
708 | //Check if null so .trim() doesn't throw null pointer exception.
709 | String fileTableString =
710 | filesys.getFileTable()[i] == null ?
711 | "" :
712 | filesys.getFileTable()[i].trim();
713 |
714 | if (pathNameString.equals(fileTableString)) {
715 | targetBlock = i;
716 | break;
717 | }
718 | }
719 |
720 | return targetBlock;
721 |
722 | }
723 |
724 | //************End code added by Brett Duncan*********************//
725 |
726 | /** A Launcher instance represents one atomic command being run by the
727 | * Kernel. It has associated with it a process id (pid), a Java method
728 | * to run, and a list of arguments to the method.
729 | * Do not modify any part of this class.
730 | */
731 | static private class Launcher extends Thread {
732 | /** Mapping of process ids to Launcher instances. */
733 | static Map
37 | *
39 | * Other parameters depend on the call number.
40 | */
41 | public static final int INTERRUPT_USER = 0;
42 |
43 | /** An interrupt kind indicating that a disk caused the interrupt.
44 | * All other parameters will be null or zero.
45 | */
46 | public static final int INTERRUPT_DISK = 1;
47 |
48 | /** An interrupt kind indicating that the system just started.
49 | * The Kernel should set up any internal state and
50 | * begin executing the first program.
51 | *
52 | *
57 | */
58 | public static final int INTERRUPT_POWER_ON = 2;
59 |
60 | //////////////// Values for the second parameter ("i1") for USER interrupts
61 |
62 | /** System call to output text on the console.
63 | *
64 | *
67 | */
68 | public static final int SYSCALL_OUTPUT = 0;
69 |
70 | /** System call to read text from the console.
71 | * This function returns when the user presses [Enter].
72 | *
73 | *
77 | */
78 | public static final int SYSCALL_INPUT = 1;
79 |
80 | /** System call to execute a new program.
81 | * The new program will run in parallel to the current program.
82 | *
83 | *
88 | */
89 | public static final int SYSCALL_EXEC = 2;
90 |
91 | /** System call to wait for a process to terminate.
92 | * This call will not return until the indicated process has
93 | * run to completion.
94 | *
95 | *
98 | */
99 | public static final int SYSCALL_JOIN = 3;
100 |
101 | //************Code added by Brett Duncan*********************//
102 | //XXX: SYSCALL constants
103 |
104 | /**
105 | * System call to format the disk.
106 | */
107 | public static final int SYSCALL_FORMAT = 4;
108 |
109 | /** System call to create a new file.
110 | *
111 | */
112 | public static final int SYSCALL_CREATE = 5;
113 |
114 | /**
115 | * System call to read the contents of a file.
116 | */
117 | public static final int SYSCALL_READ = 6;
118 |
119 | /**
120 | * System call to write to the disk.
121 | */
122 | public static final int SYSCALL_WRITE = 7;
123 |
124 | /**
125 | * System call to delete a file from the disk.
126 | */
127 | public static final int SYSCALL_DELETE = 8;
128 |
129 | /**
130 | * System call to display contents of the current directory.
131 | */
132 | public static final int SYSCALL_READDIR = 9;
133 |
134 | /**
135 | * System call to perform safe shutdown of the disk.
136 | */
137 | public static final int SYSCALL_SHUTDOWN = 10;
138 |
139 | public static final int SYSCALL_GET_BLOCK_SIZE = 11;
140 |
141 | //************End code added by Brett Duncan*****************//
142 |
143 | //////////////// Error codes returned by interrupt()
144 |
145 | /** An error code indicating that one of the system call parameters made no
146 | * sense.
147 | */
148 | public static final int ERROR_BAD_ARGUMENT = -1;
149 |
150 | /** An error code indicating that the class name passed to SYSCALL_EXEC
151 | * could not be found.
152 | */
153 | public static final int ERROR_NO_CLASS = -2;
154 |
155 | /** An error code indicating that the class name passed to SYSCALL_EXEC
156 | * named a class with no appropriate main() method.
157 | */
158 | public static final int ERROR_NO_MAIN = -3;
159 |
160 | /** An error code indicating some unspecified problem running the class
161 | * passed SYSCALL_EXEC.
162 | */
163 | public static final int ERROR_BAD_COMMAND = -4;
164 |
165 | /** An error code indicating that one parameter was too big or too small. */
166 | public static final int ERROR_OUT_OF_RANGE = -5;
167 |
168 | /** An error code indicating that end of file was reached. */
169 | public static final int ERROR_END_OF_FILE = -6;
170 |
171 | /** An error code indicating that something went wrong during an I/O
172 | * operation.
173 | */
174 | public static final int ERROR_IO = -7;
175 |
176 | /** An error code indicating that a child program caused an exception and
177 | * crashed.
178 | */
179 | public static final int ERROR_IN_CHILD = -8;
180 |
181 | /** An error code indicating an attempt to join with a non-existant
182 | * process.
183 | */
184 | public static final int ERROR_NO_SUCH_PROCESS = -9;
185 |
186 | //////////////// Transient state of the kernel
187 |
188 | /** The disk to be used */
189 | // private static Disk disk;
190 | private static FastDisk disk;
191 |
192 |
193 | /** The file system. */
194 | private static FileSys filesys;
195 |
196 | /** The size of the disk cache */
197 | private static int cacheSize;
198 |
199 | //////////////// Methods
200 |
201 | /** This is the only entry into the kernel.
202 | *
211 | * Important: If the Disk calls interrupt(), the
212 | * Kernel should take care of business and return from the interrupt
213 | * as soon as possible. All Disk I/O is halted while the interrupt is
214 | * being processed.
215 | *
31 | * rc = Kernel.interrupt(Kernel.INTERRUPT_USER, ... )
32 | *
33 | * and rc is less than 0.
34 | */
35 | public static final String[] errorMessage = {
36 | "OK", // 0
37 | "Invalid argument", // ERROR_BAD_ARGUMENT = -1
38 | "No such class", // ERROR_NO_CLASS = -2
39 | "Class has no main method", // ERROR_NO_MAIN = -3
40 | "Command aborted", // ERROR_BAD_COMMAND = -4
41 | "Argument out of range", // ERROR_OUT_OF_RANGE = -5
42 | "End of file on console input", // ERROR_END_OF_FILE = -6
43 | "I/O error on console input", // ERROR_IO = -7
44 | "Exception in user program", // ERROR_IN_CHILD = -8
45 | "No such process" // ERROR_NO_SUCH_PROCESS = -9
46 | };
47 |
48 | /** Performs SYSCALL_OUTPUT.
49 | * Displays text on the console.
50 | * @param s a String to display
51 | * @return zero
52 | */
53 | public static int output(String s) {
54 | return Kernel.interrupt(Kernel.INTERRUPT_USER,
55 | Kernel.SYSCALL_OUTPUT, 0, s, null, null);
56 | } // output
57 |
58 | /** Performs SYSCALL_INPUT.
59 | * Waits for the user to type some text and hit [return].
60 | * The input line is returned in the supplied StringBuffer
61 | * @param result a place to put the result
62 | * @return zero on success, or one of the error codes Kernel.END_OF_FILE or
63 | * Kernel.ERROR_IO.
64 | */
65 | public static int input(StringBuffer result) {
66 | result.setLength(0);
67 | return Kernel.interrupt(Kernel.INTERRUPT_USER,
68 | Kernel.SYSCALL_INPUT, 0, result, null, null);
69 | } // input
70 |
71 | /** Performs SYSCALL_EXEC.
72 | * Launches the named program, and lets it run in parallel
73 | * to the current program.
74 | * @param command The name of a Java class to execute.
75 | * @param args The arguments to give the new program
76 | * @return a non-negative process id, or ERROR_BAD_COMMAND.
77 | */
78 | public static int exec(String command, String args[]) {
79 | return Kernel.interrupt(Kernel.INTERRUPT_USER,
80 | Kernel.SYSCALL_EXEC, 0, command, args, null);
81 | } // exec
82 |
83 | /** Performs SYSCALL_JOIN.
84 | * Waits for a process to terminate
85 | * @param pid a process id returned by a previous call to exec.
86 | * @return zero or ERROR_NO_SUCH_PROCESS
87 | */
88 | public static int join(int pid) {
89 | return Kernel.interrupt(Kernel.INTERRUPT_USER,
90 | Kernel.SYSCALL_JOIN, pid, null, null, null);
91 | } // join
92 |
93 | //************Code added by Brett Duncan*********************//
94 | /**
95 | * Gets block size of the disk.
96 | * @return The block size of the disk.
97 | */
98 | public static int getBlockSizeOfDisk() {
99 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_GET_BLOCK_SIZE, 0, null, null, null);
100 | }
101 | //************End code added by Brett Duncan*********************//
102 |
103 | //XXX: Implementing methods
104 |
105 | /** Formats the disk. If the disk is already formatted, this system call
106 | * will destroy all data on it.
107 | * @return 0 on success and -1 on failure.
108 | */
109 | public static int format() {
110 | // err.println("format system call not implemented yet");
111 | // return -1;
112 |
113 | //************Code added by Brett Duncan*********************//
114 |
115 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_FORMAT, 0, null, null, null);
116 |
117 | //************End code added by Brett Duncan*********************//
118 |
119 | } // format(int)
120 |
121 | /** Changes the current working directory.
122 | * @param pathname the name of the directory to go to. If it is a relative
123 | * pathname (does not start with '/'), it is relative to the current
124 | * working directory.
125 | * @return 0 on success and -1 on failure.
126 | */
127 | public static int chdir(String pathname) {
128 | err.println("chdir system call not implemented yet");
129 | return -1;
130 | } // chdir(String)
131 |
132 | /** Creates a new "ordinary" file.
133 | * @param pathname the name of the new file being created.
134 | * @return 0 on success and -1 on failure.
135 | */
136 | public static int create(String pathname) {
137 | // err.println("create system call not implemented yet");
138 | // return -1;
139 |
140 | //************Code added by Brett Duncan*********************//
141 |
142 | return Kernel.interrupt(
143 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_CREATE, 0, null, null, pathname.getBytes());
144 | //************End code added by Brett Duncan*********************//
145 |
146 | } // create(String)
147 |
148 | /** Reads from a file.
149 | * @param pathname the name of the file to read from.
150 | * @param buffer the destination for the data.
151 | * @return 0 on success and -1 on failure.
152 | */
153 | public static int read(String pathname, byte[] buffer) {
154 |
155 | // err.println("read system call not implemented yet");
156 | // return -1;
157 |
158 | //************Code added by Brett Duncan*********************//
159 | return Kernel.interrupt(
160 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_READ, 0, null, null, pathname.getBytes());
161 | //************End code added by Brett Duncan*********************//
162 |
163 | } // read(String, byte[])
164 |
165 | /** Writes to a file.
166 | * @param pathname the name of the file to write to.
167 | * @param buffer the source of the data.
168 | * @return 0 on success and -1 on failure.
169 | */
170 | public static int write(String pathname, byte[] buffer) {
171 | // err.println("write system call not implemented yet");
172 | //************Code added by Brett Duncan*********************//
173 | return Kernel.interrupt(
174 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_WRITE, 0, pathname, null, buffer);
175 | //************End code added by Brett Duncan*********************//
176 | // return -1;
177 | } // write(String, byte[])
178 |
179 | /** Deletes an "ordinary" file.
180 | * @param pathname the name of the file to delete.
181 | * @return 0 on success and -1 on failure.
182 | */
183 | public static int delete(String pathname) {
184 | return Kernel.interrupt(
185 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_DELETE, 0, pathname, null, null);
186 | // return -1;
187 | } // delete(String)
188 |
189 | /** Creates an empty directory.
190 | * @param pathname the name of the new directory being created
191 | * @return 0 on success and -1 on failure.
192 | */
193 | public static int mkdir(String pathname) {
194 | err.println("mkdir system call not implemented yet");
195 | return -1;
196 | } // mkdir(String)
197 |
198 | /** Removes a directory. The directory must be empty.
199 | * @param pathname the name of the directory to remove.
200 | * @return 0 on success and -1 on failure.
201 | */
202 | public static int rmdir(String pathname) {
203 | err.println("rmdir system call not implemented yet");
204 | return -1;
205 | } // rmdir(String)
206 |
207 | /** Creates a symbolic link.
208 | * @param oldName a pathname that will be target of the symlink. It need
209 | * not exist.
210 | * @param newName the name of the new symlink.
211 | * @return 0 on success and -1 on failure.
212 | */
213 | public static int symlink(String oldName, String newName) {
214 | err.println("symlink system call not implemented yet");
215 | return -1;
216 | } // symlink(String,String)
217 |
218 | /** Reads the contents of a symbolic link.
219 | * @param pathname the name of the symbolic link.
220 | * @param buffer the destination for its pathname.
221 | * @return 0 on success and -1 on failure.
222 | */
223 | public static int readlink(String pathname, byte[] buffer) {
224 | err.println("readlink system call not implemented yet");
225 | return -1;
226 | } // readlink(String, byte[])
227 |
228 | /** Reads the contents of a directory.
229 | * @param pathname the name of the directory.
230 | * @param buffer the destination for its contents.
231 | * @return 0 on success and -1 on failure.
232 | */
233 | public static int readdir(String pathname, byte[] buffer) {
234 | return Kernel.interrupt(Kernel.INTERRUPT_USER, Kernel.SYSCALL_READDIR,
235 | 0, null, null, null);
236 | // return -1;
237 | } // readdir(String, byte[])
238 |
239 | //************Code added by Brett Duncan*********************//
240 | public static void shutdown() {
241 | Kernel.interrupt(
242 | Kernel.INTERRUPT_USER, Kernel.SYSCALL_SHUTDOWN, 0, null, null, null);
243 | }
244 | //************End code added by Brett Duncan*********************//
245 |
246 | } // Library
247 |
--------------------------------------------------------------------------------
/src/minikernel/Makefile.sdx:
--------------------------------------------------------------------------------
1 | SRC = Boot.java Disk.java FastDisk.java FileTester.java Kernel.java Library.java FileSys.java
2 |
3 | all: compile run
4 |
5 | compile:
6 | javac -Xlint:all $(SRC)
7 |
8 | run:
9 | java -enableassertions Boot 10 FastDisk 100 FileTester test1.script
10 |
11 | clean:
12 | $(RM) *.class DISK
13 |
--------------------------------------------------------------------------------
/src/minikernel/MyShell.java:
--------------------------------------------------------------------------------
1 | //package minikernel;
2 | //
3 | //import java.util.Scanner;
4 | //import minikernel.Disk;
5 | //import minikernel.FastDisk;
6 | //
7 | //public class MyShell {
8 | //
9 | // public static FastDisk disk;
10 | //
11 | // public static void main(String[] args) {
12 | // // TODO Auto-generated method stub
13 | //// System.out.println("Hello, world!");
14 | //// disk = new FastDisk(100);
15 | //
16 | // //Outline of your main method:
17 | //
18 | // /* Loop through commands on command line */
19 | // try {
20 | // while (true) {
21 | // /* Display your Shell cursor */
22 | // System.out.print("> ");
23 | //
24 | // /* Get new command line input */
25 | // Scanner s = new Scanner(System.in);
26 | // String input = s.nextLine();
27 | //
28 | // /* Break up commands and store them in an array using '&' as delimeter */
29 | // String commands[] = input.split("&");
30 | //
31 | // /* Create a thread array for each command detected */
32 | // CommandExecutorThread commandExecutorThread[] = new CommandExecutorThread[commands.length];
33 | //
34 | // /* Loop to allow a new thread per command to be created */
35 | //// for (String command : commands) {
36 | // for (int i = 0; i < commands.length; i++) {
37 | // commandExecutorThread[i] = new CommandExecutorThread(i, commands[i].trim());
38 | // commandExecutorThread[i].start();
39 | // }
40 | //
41 | // /* If it is EXIT command then close your Shell by executing System.exit(0)*/
42 | //
43 | // /* Add new thread to the thread array and run it */
44 | // /* Join the threads */
45 | // for (int i = 0; i < commandExecutorThread.length; i++) {
46 | // commandExecutorThread[i].join();
47 | // }
48 | // } //while
49 | //
50 | // } /* Check for a keyboard interupt or other non-serious system problem */
51 | //
52 | // catch (Exception e) {
53 | // System.out.println("\n\nInterrupt was detected. my Shell is closing.");
54 | //// e.printStackTrace();
55 | // System.exit(0);
56 | // }
57 | //
58 | // }
59 | //
60 | //}
61 | //
62 | //class CommandExecutorThread extends Thread {
63 | //
64 | // private int myID = 0;
65 | // private String command;
66 | //
67 | // public CommandExecutorThread(int myID, String command) {
68 | // this.myID = myID;
69 | // this.command = command;
70 | // }
71 | //
72 | // public void run() {
73 | // System.out.println("myID = " + myID + ". Running command " + command);
74 | //
75 | // switch (command) {
76 | // case "format":
77 | // break;
78 | // }
79 | //
80 | // }
81 | //
82 | //}
83 |
--------------------------------------------------------------------------------
/test1.script:
--------------------------------------------------------------------------------
1 | //************** simple test: format, create, write, read, readdir
2 | format
3 | pwd
4 | create /junk
5 | ls /
6 | write /junk this is file junk
7 | read /junk
8 | ls .
9 | read junk
10 | writeln junk
11 | two
12 | lines
13 | .
14 | read junk
15 | //*************** mkdir, chdir, symlink, readlink
16 | mkdir dir1
17 | mkdir dir1/subdir
18 | ls /
19 | create dir1/subdir/foo
20 | cd dir1
21 | pwd
22 | ls subdir
23 | cd subdir
24 | pwd
25 | cd ..
26 | pwd
27 | writeln subdir/foo
28 | This is foo
29 | .
30 | read subdir/foo
31 | // Absolute symlink
32 | ln /dir1/subdir/foo link1
33 | readlink link1
34 | read link1
35 | readlink /dir1/link1
36 | read /dir1/link1
37 | // Relative symlink
38 | ln ../dir1 link2
39 | readlink link2
40 | ls ../dir1
41 | ls link2
42 | ls link2/subdir
43 | read link2/subdir/foo
44 | read /dir1/link2/subdir/foo
45 | //**************** rmdir delete
46 | ls .
47 | rmdir subdir
48 | rm subdir/foo
49 | rmdir subdir
50 | rm link1
51 | ls
52 | pwd
53 | //**************** Examples from the FAQ
54 |
55 | //Q9
56 | format
57 | mkdir /dir
58 | cd /dir
59 | pwd
60 | rmdir /dir
61 | cd ..
62 | rmdir /dir
63 |
64 | //Q10
65 | format
66 | mkdir /foo
67 | create /foo/bar
68 | write /foo/bar This is file /foo/bar
69 | read /foo/bar
70 | read foo/bar
71 | read foo/bar/
72 | read foo//bar
73 | read //foo///bar////
74 |
75 | //Q11
76 | format
77 | mkdir /tmp
78 | cd /tmp
79 | ln /tmp/bad bad
80 | cd bad
81 | rm bad
82 |
--------------------------------------------------------------------------------