Example #1
0
// 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)
	}
}
Example #2
0
// Test to ensure that blocks are re-used in last-recently-used order, i.e.
// in the reverse order they are 'put' back into the cache.
func TestLRUOrder(test *testing.T) {
	dev, cache := openTestCache(test)

	// get 10 blocks
	blocks := make([]*common.CacheBlock, 10)
	for i := 0; i < 10; i++ {
		blocks[i] = cache.GetBlock(0, i, common.FULL_DATA_BLOCK, common.NORMAL)
	}

	// put them back
	for i := 0; i < 10; i++ {
		cache.PutBlock(blocks[i], common.FULL_DATA_BLOCK)
	}

	// now fetch 10 more different blocks
	blocks2 := make([]*common.CacheBlock, 10)
	for i := 0; i < 10; i++ {
		blocks2[i] = cache.GetBlock(0, i+10, common.FULL_DATA_BLOCK, common.NORMAL)
		if blocks2[i] != blocks[0+i] {
			testutils.ErrorHere(test, "cache block mismatch, expected %p, got %p", blocks[9-i], blocks2[i])
		}
	}

	closeTestCache(test, dev, cache)
}
Example #3
0
func openTestCache(test *testing.T) (common.BlockDevice, *LRUCache) {
	dev := testutils.NewTestDevice(test, 64, 100)
	cache := NewLRUCache(4, 10, 16)

	err := cache.MountDevice(0, dev, getDevInfo(64))
	if err != nil {
		testutils.ErrorHere(test, "Failed when mounting ramdisk device into cache: %s", err)
	}
	return dev, cache.(*LRUCache)
}
Example #4
0
func closeTestCache(test *testing.T, dev common.BlockDevice, cache *LRUCache) {
	cache.Flush(0)
	err := cache.UnmountDevice(0)
	if err != nil {
		testutils.ErrorHere(test, "Failed when unmounting ramdisk device: %s", err)
	}

	//	if err = cache.Close(); err != nil {
	//		ErrorHere(test, "Failed when closing cache: %s", err)
	//	}

	//	if err = dev.Close(); err != nil {
	//		ErrorHere(test, "Failed when closing device: %s", err)
	//	}
}
Example #5
0
func TestGetConcurrency(test *testing.T) {
	dev, cache := openTestCache(test)
	bdev := testutils.NewBlockingDevice(testutils.NewTestDevice(test, 64, 100))
	cache.MountDevice(1, bdev, getDevInfo(64))

	// Test that reads from a normal device are not blocked by reads from a
	// broken device.
	wg := new(sync.WaitGroup)
	wg.Add(2)
	go func() {
		// Do the read on the broken device
		cb := cache.GetBlock(1, 0, common.FULL_DATA_BLOCK, common.NORMAL)
		cache.PutBlock(cb, common.FULL_DATA_BLOCK)
		wg.Done()
	}()

	go func() {
		// Wait for the device to be blocked
		<-bdev.HasBlocked
		cb := cache.GetBlock(0, 0, common.FULL_DATA_BLOCK, common.NORMAL)
		// Now unblock that device so we can shut down
		bdev.Unblock <- true
		cache.PutBlock(cb, common.FULL_DATA_BLOCK)

		wg.Done()
	}()

	wg.Wait()
	if err := cache.UnmountDevice(1); err != nil {
		testutils.ErrorHere(test, "Failed when unmounting device: %s", err)
	}
	if err := bdev.Close(); err != nil {
		testutils.ErrorHere(test, "Failed when closing device: %s", err)
	}
	closeTestCache(test, dev, cache)
}
Example #6
0
func TestCacheFullPanic(test *testing.T) {
	dev, cache := openTestCache(test)

	for i := 0; i < 10; i++ {
		_ = cache.GetBlock(0, i, common.FULL_DATA_BLOCK, common.NORMAL)
	}

	done := make(chan bool)
	go func() {
		defer func() {
			if x := recover(); x == nil {
				testutils.ErrorHere(test, "Expected all buffers in use panic")
			}
			done <- true
		}()
		_ = cache.GetBlock(0, 11, common.FULL_DATA_BLOCK, common.NORMAL)
	}()

	<-done
	closeTestCache(test, dev, cache)
}
Example #7
0
// Test that blocks are cached. This test will deadlock if more than one block
// read is attempted from the underlying device.
func TestDoesCache(test *testing.T) {
	// Open an always-broken device
	dev := testutils.NewBlockingDevice(testutils.NewTestDevice(test, 64, 100))
	cache := NewLRUCache(4, 10, 16)
	err := cache.MountDevice(0, dev, getDevInfo(64))
	if err != nil {
		testutils.ErrorHere(test, "Failed when mounting ramdisk device into cache: %s", err)
	}

	wg := new(sync.WaitGroup)
	wg.Add(2)

	go func() {
		// Allow a single block to be read
		<-dev.HasBlocked
		dev.Unblock <- true
		wg.Done()
	}()

	go func() {
		cb1 := cache.GetBlock(0, 5, common.FULL_DATA_BLOCK, common.NORMAL)
		data, ok := cb1.Block.(common.FullDataBlock)
		if !ok {
			testutils.ErrorHere(test, "Did not get a FullDataBlock")
		}
		if data[0] != 5 {
			testutils.ErrorHere(test, "Data in block did not match, expected %x, got %x", 5, data[0])
		}

		// this should be pulled from the cache, not from the device
		cb2 := cache.GetBlock(0, 5, common.FULL_DATA_BLOCK, common.NORMAL)
		if cb1 != cb2 {
			testutils.ErrorHere(test, "Cache block mismatch, expected %p, got %p", cb1, cb2)
		}
		data, ok = cb2.Block.(common.FullDataBlock)
		if !ok {
			testutils.ErrorHere(test, "Did not get a FullDataBlock")
		}
		if data[0] != 5 {
			testutils.ErrorHere(test, "Data in block did not match, expected %x, got %x", 5, data[0])
		}

		cache.PutBlock(cb1, common.FULL_DATA_BLOCK)
		cache.PutBlock(cb2, common.FULL_DATA_BLOCK)

		wg.Done()
	}()

	wg.Wait()

	err = cache.UnmountDevice(0)
	if err != nil {
		testutils.ErrorHere(test, "Failed when unmounting ramdisk device: %s", err)
	}

	// if err = cache.Close(); err != nil {
	// 	ErrorHere(test, "Failed when closing cache: %s", err)
	// }

	// if err = dev.Close(); err != nil {
	// 	ErrorHere(test, "Failed when closing device: %s", err)
	// }
}
Example #8
0
// 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)
	}
}
Example #9
0
// 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)
	}
}
Example #10
0
// 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)
	}
}