// Change the working directory of the process, and verify that we can open a // file using the relative path. func TestChdir(test *testing.T) { fs, proc := OpenMinixImage(test) // Change into /var and then try to open run/syslogd.pid if err := proc.Chdir("/var"); err != nil { testutils.FatalHere(test, "Failed to change directory: %s", err) } if proc.workdir.Inum != 543 { testutils.FatalHere(test, "Got wrong inode expected %d, got %d", 543, proc.workdir.Inum) } // Fetch something with a relative path rip, err := fs.eatPath(proc, "run/syslogd.pid") if err != nil { testutils.FatalHere(test, "Could not open relative file: %s", err) } if rip.Inum != 481 { testutils.FatalHere(test, "Got wrong inode expected %d, got %d", 481, rip.Inum) } fs.itable.PutInode(rip) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Ensure that an open process prevents clean shutdown func TestSpawnNoExit(test *testing.T) { fs, proc := OpenMinixImage(test) _, err := proc.Fork() if err != nil { testutils.FatalHere(test, "Failed when spawning new process: %s", err) } err = fs.Shutdown() if err != common.EBUSY { testutils.FatalHere(test, "Expected EBUSY error, got: %v", err) } }
// Create a new file on the file system, ensure that it is given the // appropriate inode number and created successfully. Then unlink the file // so the file system remains in the same state it began in. func TestCreateThenUnlink(test *testing.T) { fs, proc := OpenMinixImage(test) alloc := proc.rootdir.Devinfo.AllocTbl // Check the state of the bitmap before creating this file inum, err := alloc.AllocInode() if err != nil { testutils.FatalHere(test, "Error pre-allocating an inode: %s", err) } alloc.FreeInode(inum) // Get block 13 and print it before we make any changes //bp := fs.bcache.GetBlock(ROOT_DEVICE, 13, INODE_BLOCK, NORMAL) //debug.PrintBlock(bp, fs.devinfo[ROOT_DEVICE]) // Create a new file and check allocation tables, etc. file, err := fs.Open(proc, "/tmp/created_file", common.O_CREAT, 0666) if err != nil { testutils.FatalHere(test, "Failed when creating new file: %s", err) } filp := file.(*filp) if filp.inode.Inum != inum { testutils.ErrorHere(test, "Inum mismatch expected %d, got %d", inum, filp.inode.Inum) } // Close and unlink the new file err = fs.Close(proc, file) if err != nil { testutils.ErrorHere(test, "Failed when closing new file: %s", err) } err = fs.Unlink(proc, "/tmp/created_file") if err != nil { testutils.ErrorHere(test, "Failed when unlinking new file: %s", err) } // The bit we just freed should be our next inum2, err := alloc.AllocInode() if err != nil { testutils.FatalHere(test, "Failed when checking inode allocation: %s", err) } if inum != inum2 { testutils.FatalHere(test, "Inode mismatch expected %d, got %d", inum, inum2) } alloc.FreeInode(inum2) fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Ensure cleanup happens properly with fork/exit func TestForkWithExit(test *testing.T) { fs, proc := OpenMinixImage(test) child, err := proc.Fork() if err != nil { testutils.FatalHere(test, "Failed when spawning new process: %s", err) } child.Exit() err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
func TestShutdownNoRootProcExit(test *testing.T) { fs, _ := OpenMinixImage(test) err := fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed shutting down: %s", err) } }
func OpenEuroparl(test *testing.T) *os.File { filename := getExtraFilename("europarl-en.txt") file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { testutils.FatalHere(test, "Could not open Europarl reference: %s", err) } return file }
func OpenMinixImage(test *testing.T) (*FileSystem, *Process) { imageFilename := getExtraFilename("minix3root.img") fs, proc, err := OpenFileSystemFile(imageFilename) if err != nil { testutils.FatalHere(test, "Failed opening file system: %s", err) } return fs, proc }
// Test read functionality by reading the same file from the guest/host // operating systems and comparing the results of sucessive read calls. The // number of bytes read per call is set to (4/3) of the block size of the file // system to ensure that we hit all codepaths. func TestRead(test *testing.T) { fs, proc := OpenMinixImage(test) file, err := fs.Open(proc, "/sample/europarl-en.txt", common.O_RDONLY, 0666) if err != nil { testutils.FatalHere(test, "Failed when opening file: %s", err) } ofile := OpenEuroparl(test) // Read and compare the two files blocksize := fs.devinfo[common.ROOT_DEVICE].Blocksize numbytes := blocksize + (blocksize / 3) data := make([]byte, numbytes) odata := make([]byte, numbytes) offset := 0 for { n, err := file.Read(data) od, oerr := ofile.Read(odata) if n != od { testutils.FatalHere(test, "Bytes read mismatch at offset %d: expected %d, got %d", offset, od, n) } if err != oerr { testutils.FatalHere(test, "Error mismatch at offset %d: expected '%s', got '%s'", offset, oerr, err) } if bytes.Compare(data, odata) != 0 { testutils.FatalHere(test, "Data mismatch at offset %d\n==Expected\n%s\n==Got\n%s", offset, odata, data) } if err == io.EOF && oerr == io.EOF { break } offset += n } fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Test that we can open and close a file func TestOpenClose(test *testing.T) { fs, proc := OpenMinixImage(test) file, err := proc.Open("/sample/europarl-en.txt", common.O_RDONLY, 0666) if err != nil { testutils.FatalHere(test, "Failed opening file: %s", err) } found, count := checkFileAndCount(proc, file) if !found { testutils.FatalHere(test, "Did not find open file in proc.files") } if count != 1 { testutils.FatalHere(test, "Open file count incorrect got %d, expected %d", count, 1) } // Now close the file and make sure things are cleaned up err = proc.Close(file) found, count = checkFileAndCount(proc, file) if found { testutils.FatalHere(test, "Found file in process table, should not have") } if count != 0 { testutils.FatalHere(test, "Open file count mismatch got %d, expected %d", count, 0) } // How many goroutines are open right now? numgoros := runtime.NumGoroutine() stacknow := make([]byte, 4096) runtime.Stack(stacknow, true) fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } // We expect shutdown to have killed the following goroutines // * device // * block cache // * inode cache // * allocation table // * file server // This test is fragile, so be careful with it! expected := numgoros - 5 if runtime.NumGoroutine() != expected { test.Logf("Original stack:\n%s\n", stacknow) newstack := make([]byte, 4096) runtime.Stack(newstack, true) test.Logf("Current stack:\n%s\n", newstack) testutils.FatalHere(test, "Goroutine count mismatch got %d, expected %d", expected, runtime.NumGoroutine()) } }
// Test changin the position within an open file using the same technique as // TestRead, by comparing to the POSIX API provided by the Go standard // libraries corresponding calls. func TestSeek(test *testing.T) { fs, proc := OpenMinixImage(test) file, err := fs.Open(proc, "/sample/europarl-en.txt", common.O_RDONLY, 0666) if err != nil { testutils.FatalHere(test, "Failed when opening file: %s", err) } ofile := OpenEuroparl(test) type seekData struct { whence int pos int } seekOps := []seekData{ {0, 0}, {0, 31337}, {1, 3333}, } // Read and compare several blocks of the file, seeking between each read. // This ensures that our seek behaviour is the same as POSIX. blocksize := fs.devinfo[common.ROOT_DEVICE].Blocksize numbytes := blocksize + (blocksize / 3) data := make([]byte, numbytes) odata := make([]byte, numbytes) for idx, testData := range seekOps { pos, err := file.Seek(testData.pos, testData.whence) opos, err := ofile.Seek(int64(testData.pos), testData.whence) if int64(pos) != opos { testutils.FatalHere(test, "Seek position mismatch in test %d: exected %d, got %d", idx, opos, pos) } n, err := file.Read(data) od, oerr := ofile.Read(odata) if n != od { testutils.FatalHere(test, "Bytes read mismatch at offset %d: expected %d, got %d", idx, od, n) } if err != oerr { testutils.FatalHere(test, "Error mismatch at offset %d: expected '%s', got '%s'", idx, oerr, err) } if bytes.Compare(data, odata) != 0 { testutils.FatalHere(test, "Data mismatch at offset %d\n==Expected\n%s\n==Got\n%s", idx, odata, data) } } fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Make a new directory on the file system, ensure that it is given the // appropriate number/contents, then rmdir the file and check that the file // system is returned to its initial state. func TestMkdir(test *testing.T) { fs, proc := OpenMinixImage(test) bitmap := proc.rootdir.Devinfo.AllocTbl // Check the state of the bitmaps before creating this file inum, err := bitmap.AllocInode() if err != nil { testutils.FatalHere(test, "Error pre-allocating an inode: %s", err) } bitmap.FreeInode(inum) znum, err := bitmap.AllocZone(common.NO_ZONE) if err != nil { testutils.FatalHere(test, "Error pre-allocating a zone: %s", err) } bitmap.FreeZone(znum) // Create a new file and check allocation tables, etc. err = fs.Mkdir(proc, "/tmp/new_directory", 0666) if err != nil { testutils.FatalHere(test, "Failed when creating new directory: %s", err) } dirp, err := fs.eatPath(proc, "/tmp/new_directory") if err != nil { testutils.FatalHere(test, "Failed when looking up new directory: %s", err) } if dirp.Inum != inum { testutils.ErrorHere(test, "Inum mismatch expected %d, got %d", inum, dirp.Inum) } ok, devnum, inum := Lookup(dirp, ".") if !ok { testutils.ErrorHere(test, "Current directory . lookup failed") } if devnum != dirp.Devinfo.Devnum { testutils.ErrorHere(test, "Current directory . devnum mismatch expected %d, got %d", dirp.Devinfo.Devnum, devnum) } if inum != dirp.Inum { testutils.ErrorHere(test, "Current directory . inum mismatch expected %d, got %d", dirp.Inum, inum) } if !dirp.IsDirectory() { testutils.ErrorHere(test, "New directory is not a directory") } if dirp.Nlinks != 2 { testutils.ErrorHere(test, "Links mismatch expected %d, got %d", 2, dirp.Nlinks) } if dirp.Size != 128 { testutils.ErrorHere(test, "Directory size mismatch expected %d, got %d", 128, dirp.Size) } fs.itable.PutInode(dirp) // Remove the new directory err = fs.Rmdir(proc, "/tmp/new_directory") if err != nil { testutils.ErrorHere(test, "Failed when unlinking new directory: %s", err) } // The bit we just freed should be our next inum2, err := bitmap.AllocInode() if err != nil { testutils.ErrorHere(test, "Failed when checking inode allocation: %s", err) } if inum != inum2 { testutils.ErrorHere(test, "Inode mismatch expected %d, got %d", inum, inum2) } bitmap.FreeInode(inum2) fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Test write functionality by taking the data from the host system and // writing it to the guest system along with the host system (at the same // time) and comparing the returns from these functions. Then compare the // written data to the original data to make sure it was written correctly. func TestWrite(test *testing.T) { fs, proc := OpenMinixImage(test) ofile := OpenEuroparl(test) // Read the data for the entire file filesize := 4489799 // known filedata, err := ioutil.ReadAll(ofile) if err != nil { testutils.FatalHere(test, "Failed when reading from original file: %s", err) } if filesize != len(filedata) { testutils.FatalHere(test, "File content sizes differ: %v != %v", len(filedata), filesize) } if ofile.Close() != nil { testutils.FatalHere(test, "Failed when closing original file: %s", err) } // Open the two files that will be written to gfile, err := fs.Open(proc, "/tmp/europarl-en.txt", common.O_CREAT|common.O_TRUNC|common.O_RDWR, 0666) if err != nil || gfile == nil { testutils.FatalHere(test, "Could not open file on guest: %s", err) } hfile, err := os.OpenFile("/tmp/europarl-en.txt", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) if err != nil || hfile == nil { testutils.FatalHere(test, "Could not open file on host: %s", err) } // Write the data to a file on the host/guest operating systems in sync blocksize := fs.devinfo[common.ROOT_DEVICE].Blocksize numbytes := blocksize + (blocksize / 3) pos := 0 for pos < filesize { // Write the next numbytes bytes to the file endpos := pos + numbytes if endpos > filesize { endpos = filesize } data := filedata[pos:endpos] gn, gerr := gfile.Write(data) hn, herr := hfile.Write(data) if gn != hn { testutils.ErrorHere(test, "Bytes read mismatch at offset %d: expected %d, got %d", pos, hn, gn) } if gerr != herr { testutils.ErrorHere(test, "Error mismatch at offset %d: expected '%s', got '%s'", pos, herr, gerr) } rip, err := fs.eatPath(proc, "/tmp/europarl-en.txt") if err != nil { testutils.FatalHere(test, "After write at position %d: could not locate newly created file: %s", pos, err) } else { fs.itable.PutInode(rip) } pos += gn } if hfile.Close() != nil { testutils.ErrorHere(test, "Failed when closing host file") } // Seek to beginning of file gfile.Seek(0, 0) written := make([]byte, filesize) n, err := gfile.Read(written) if n != filesize { testutils.ErrorHere(test, "Verify count mismatch expected %d, got %d", filesize, n) } if err != nil { testutils.ErrorHere(test, "Error when reading to verify: %s", err) } compare := bytes.Compare(filedata, written) if compare != 0 { testutils.ErrorHere(test, "Error comparing written data expected %d, got %d", 0, compare) } if fs.Close(proc, gfile) != nil { testutils.ErrorHere(test, "Error when closing out the written file: %s", err) } err = fs.Unlink(proc, "/tmp/europarl-en.txt") if err != nil { testutils.ErrorHere(test, "Failed when unlinking written file: %s", err) } fs.Exit(proc) err = fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }
// Test that path lookups function properly func TestEatPath(test *testing.T) { fs, proc := OpenMinixImage(test) // Fetch some additional inodes to ensure path lookup is functioning // properly. // // inode permission link size name // 1 drwxr-xr-x 14 1088 / // 2 drwxr-xr-x 2 128 /usr // 541 drwxr-xr-x 2 192 /sample // 542 -rw-r--r-- 1 4489799 /sample/europarl-en.txt // 35 -rw------- 2 2705920 /boot/image/3.1.8 // 540 -rw-r--r-- 1 395 /root/.ssh/known_hosts // 481 -rw------- 1 5 /var/run/syslogd.pid type inodeTest struct { path string inum int links int size int zones []int } inodeTests := []inodeTest{ {"/", 1, 14, 1088, nil}, {"/usr", 2, 2, 128, nil}, {"/sample", 541, 2, 192, nil}, {"/sample/europarl-en.txt", 542, 1, 4489799, nil}, {"/boot/image/3.1.8", 35, 2, 2705920, nil}, {"/root/.ssh/known_hosts", 540, 1, 395, nil}, {"/var/run/syslogd.pid", 481, 1, 5, nil}, } for _, itest := range inodeTests { rip, err := fs.eatPath(proc, itest.path) if err != nil { testutils.FatalHere(test, "Failed when fetching inode for %s: %s", itest.path, err) } if itest.inum != -1 && rip.Inum != itest.inum { testutils.ErrorHere(test, "[%s] mismatch for inum got %d, expected %d", itest.path, rip.Inum, itest.inum) } if itest.links != -1 && rip.Nlinks != uint16(itest.links) { testutils.ErrorHere(test, "[%s] mismatch for links got %d, expected %d", itest.path, rip.Nlinks, itest.links) } if itest.size != -1 && rip.Size != int32(itest.size) { testutils.ErrorHere(test, "[%s] mismatch for size got %d, expected %d", itest.path, rip.Size, itest.size) } for i := 0; i < 10; i++ { if i < len(itest.zones) && rip.Zone[i] != uint32(itest.zones[i]) { testutils.ErrorHere(test, "[%s] mismatch for zone[%d] got %d, expected %d", i, itest.path, rip.Zone[i], itest.zones[i]) } } // Convert the test to use a relative path and then compare the inodes if len(itest.path) > 1 { relpath := itest.path[1:] relrip, err := fs.eatPath(proc, relpath) if err != nil { testutils.ErrorHere(test, "Failed fetching relative path %s", relpath) } if relrip != rip { testutils.ErrorHere(test, "Relative inode does not match absolute inode for path %s", itest.path) } fs.itable.PutInode(relrip) } fs.itable.PutInode(rip) } fs.Exit(proc) err := fs.Shutdown() if err != nil { testutils.FatalHere(test, "Failed when shutting down filesystem: %s", err) } }