Beispiel #1
0
func Unlink(rip *common.Inode, name string) error {
	if !rip.IsDirectory() {
		return common.ENOTDIR
	}

	dirp := rip

	inum := 0
	err := search_dir(dirp, name, &inum, DELETE)
	return err
}
Beispiel #2
0
func Link(rip *common.Inode, name string, inum int) error {
	if !rip.IsDirectory() {
		return common.ENOTDIR
	}

	dirp := rip

	// Add the entry to the directory
	err := search_dir(dirp, name, &inum, ENTER)
	return err
}
Beispiel #3
0
func IsEmpty(rip *common.Inode) bool {
	if !rip.IsDirectory() {
		return false
	}

	dirp := rip

	zeroinode := 0
	if err := search_dir(dirp, "", &zeroinode, IS_EMPTY); err != nil {
		return false
	}
	return true
}
Beispiel #4
0
func Lookup(rip *common.Inode, name string) (bool, int, int) {
	if !rip.IsDirectory() {
		return false, common.NO_DEV, common.NO_INODE
	}

	dirp := rip

	inum := 0
	err := search_dir(dirp, name, &inum, LOOKUP)
	if err != nil {
		return false, common.NO_DEV, common.NO_INODE
	}

	return true, dirp.Devinfo.Devnum, inum
}
Beispiel #5
0
func (fs *FileSystem) lastDir(proc *Process, path string) (*common.Inode, string, error) {
	var rip *common.Inode
	if filepath.IsAbs(path) {
		rip = proc.rootdir
	} else {
		rip = proc.workdir
	}

	// If directory has been removed or path is empty, return ENOENT
	if rip.Nlinks == 0 || len(path) == 0 {
		return nil, "", common.ENOENT
	}

	// We're going to use this inode, so make a copy of it
	rip = fs.itable.DupInode(rip)

	pathlist := strings.Split(path, string(filepath.Separator))
	if filepath.IsAbs(path) {
		pathlist = pathlist[1:]
	}

	// Scan the path component by component
	for i := 0; i < len(pathlist)-1; i++ {
		// Fetch the next component in the path
		newrip, err := fs.advance(proc, rip, pathlist[i])

		// Current inode obsolete or irrelevant
		fs.itable.PutInode(rip)
		if newrip == nil || err != nil {
			return nil, "", common.ENOENT
		}
		// Continue to the next component
		rip = newrip
	}

	if rip.Type() != common.I_DIRECTORY {
		// The penultimate path entry was not a directory, so return nil
		fs.itable.PutInode(rip)
		return nil, "", common.ENOTDIR
	}

	return rip, pathlist[len(pathlist)-1], nil
}
Beispiel #6
0
func (c *server_InodeTbl) loadInode(xp *common.Inode) {
	// The count at this point is guaranteed to be > 0, so the device cannot
	// be unmounted until the load has completed and the inode has been 'put'

	inum := xp.Inum - 1

	info := xp.Devinfo

	inodes_per_block := info.Blocksize / common.V2_INODE_SIZE
	ioffset := inum % inodes_per_block
	blocknum := info.MapOffset + (inum / inodes_per_block)

	// Load the inode from the disk and create an in-memory version of it
	bp := c.bcache.GetBlock(info.Devnum, blocknum, common.INODE_BLOCK, common.NORMAL)
	inodeb := bp.Block.(common.InodeBlock)

	// We have the full block, now get the correct inode entry
	inode_d := &inodeb[ioffset]
	xp.Disk_Inode = inode_d
	xp.Dirty = false
	xp.Mounted = nil
}
Beispiel #7
0
func (c *server_InodeTbl) writeInode(xp *common.Inode) {
	// Calculate the block number we need
	inum := xp.Inum - 1
	info := xp.Devinfo
	inodes_per_block := info.Blocksize / common.V2_INODE_SIZE
	ioffset := inum % inodes_per_block
	block_num := info.MapOffset + (inum / inodes_per_block)

	// Load the inode from the disk
	bp := c.bcache.GetBlock(info.Devnum, block_num, common.INODE_BLOCK, common.NORMAL)
	inodeb := bp.Block.(common.InodeBlock)

	// TODO: Update times, handle read-only filesystems
	bp.Dirty = true

	// Copy the disk_inode from rip into the inode block
	inodeb[ioffset] = *xp.Disk_Inode
	xp.Dirty = false
	c.bcache.PutBlock(bp, common.INODE_BLOCK)
}
Beispiel #8
0
func (fs *FileSystem) do_open(proc *Process, path string, oflags int, omode uint16) (common.Fd, error) {
	// Remap the bottom two bits of oflags
	bits := mode_map[oflags&common.O_ACCMODE]

	var err error
	var rip *common.Inode
	var exist bool = false

	// If O_CREATE is set, try to make the file
	if oflags&common.O_CREAT > 0 {
		// Create a new node by calling new_node()
		omode := common.I_REGULAR | (omode & common.ALL_MODES & proc.umask)
		dirp, newrip, _, err := fs.new_node(proc, path, omode, common.NO_ZONE)
		if err == nil {
			exist = false
		} else if err != common.EEXIST {
			return nil, err
		} else {
			exist = (oflags&common.O_EXCL == 0)
		}

		// we don't need the parent directory
		fs.itable.PutInode(dirp)
		rip = newrip
	} else {
		// grab the inode at the given path
		rip, err = fs.eatPath(proc, path)
		if err != nil {
			return nil, err
		}
	}

	// Find an available filp entry for the file descriptor
	fdindex := -1
	for i := 0; i < len(proc.files); i++ {
		if proc.files[i] == nil {
			fdindex = i
			break
		}
	}

	if fdindex == -1 {
		return nil, common.EMFILE
	}

	err = nil // we'll use this to set error codes

	if exist { // if the file existed already
		// TODO: Check permissions here
		switch rip.Type() {
		case common.I_REGULAR:
			if oflags&common.O_TRUNC > 0 {
				common.Truncate(rip, 0, fs.bcache)
				// Flush the inode so it gets written on next block cache
				fs.itable.FlushInode(rip)
			}
		case common.I_DIRECTORY:
			// Directories cannot be opened in this system
			err = common.EISDIR
		default:
			panic("NYI: Process.Open with non regular/directory")
		}
	}

	if err != nil {
		// Something went wrong, so release the inode
		fs.itable.PutInode(rip)
		return nil, err
	}

	// Make sure there is a 'File' server running
	if rip.File == nil {
		// Spawn a file process to handle reading/writing
		rip.File = file.NewFile(rip)
	}

	// Create a new 'filp' object to expose to the user
	filp := &filp{1, 0, rip.File, rip, bits, new(sync.Mutex)}
	proc.files[fdindex] = filp

	return filp, nil
}
Beispiel #9
0
func (fs *FileSystem) do_mount(proc *Process, dev common.BlockDevice, path string) error {
	if dev == nil {
		return common.EINVAL
	}

	// scan bitmap block table to see if 'dev' is already mounted
	found := false
	freeIndex := -1
	for i := 0; i < common.NR_DEVICES; i++ {
		if fs.devices[i] == dev {
			found = true
		} else if fs.devices[i] == nil {
			freeIndex = i
		}
	}

	if found {
		return common.EBUSY // already mounted
	}

	if freeIndex == -1 {
		return common.ENFILE // no device slot available
	}

	// Invalidate the cache for this index to be sure
	fs.bcache.Invalidate(freeIndex)

	// Fill in the device info
	devinfo, err := common.GetDeviceInfo(dev)

	// If it a recognized Minix filesystem
	if err != nil {
		return err
	}

	// Create a new allocation table for this device
	alloc := alloctbl.NewAllocTbl(devinfo, fs.bcache, freeIndex)

	// Update the device number/alloc table
	devinfo.Devnum = freeIndex
	devinfo.AllocTbl = alloc

	// Add the device to the block cache/inode table
	fs.bcache.MountDevice(freeIndex, dev, devinfo)
	fs.itable.MountDevice(freeIndex, devinfo)
	fs.devices[freeIndex] = dev
	fs.devinfo[freeIndex] = devinfo

	// Get the inode of the file to be mounted on
	rip, err := fs.eatPath(fs.procs[common.ROOT_PROCESS], path)

	if err != nil {
		// Perform lots of cleanup
		fs.devices[freeIndex] = nil
		fs.devinfo[freeIndex] = nil
		fs.bcache.UnmountDevice(freeIndex)
		fs.itable.UnmountDevice(freeIndex)
		return err
	}

	var r error = nil

	// It may not be busy
	if rip.Count > 1 {
		r = common.EBUSY
	}

	// It may not be spacial
	bits := rip.Type()
	if bits == common.I_BLOCK_SPECIAL || bits == common.I_CHAR_SPECIAL {
		r = common.ENOTDIR
	}

	// Get the root inode of the mounted file system
	var root_ip *common.Inode
	if r == nil {
		root_ip, err = fs.itable.GetInode(freeIndex, common.ROOT_INODE)
		if err != nil {
			r = err
		}
	}

	if root_ip != nil && root_ip.Mode == 0 {
		r = common.EINVAL
	}

	// File types of 'rip' and 'root_ip' may not conflict
	if r == nil {
		if !rip.IsDirectory() && root_ip.IsDirectory() {
			r = common.EISDIR
		}
	}

	// If error, return the bitmap and both inodes; release the maps
	if r != nil {
		// TODO: Refactor this error handling code?
		// Perform lots of cleanup
		fs.devices[freeIndex] = nil
		fs.devinfo[freeIndex] = nil
		fs.bcache.UnmountDevice(freeIndex)
		fs.itable.UnmountDevice(freeIndex)
		return r
	}

	// Nothing else can go wrong, so perform the mount
	minfo := &common.MountInfo{
		MountPoint:  rip,
		MountTarget: root_ip,
	}
	rip.Mounted = minfo     // so we can find the root inode during lookup
	root_ip.Mounted = minfo // so we can easily resolve from a mount target to the mount point

	// Store the mountinfo in the device info table for easy mapping
	devinfo.MountInfo = minfo
	return nil
}
Beispiel #10
0
func search_dir(dirp *common.Inode, path string, inum *int, op dirop) error {
	// TODO: Check permissions (see minix source)
	devinfo := dirp.Devinfo
	blocksize := devinfo.Blocksize

	// step through the directory on block at a time
	var bp *common.CacheBlock
	var dp *common.Disk_dirent
	old_slots := int(dirp.Size / common.DIR_ENTRY_SIZE)
	new_slots := 0
	e_hit := false
	match := false
	extended := false

	for pos := 0; pos < int(dirp.Size); pos += blocksize {
		b := common.ReadMap(dirp, pos, dirp.Bcache) // get block number
		if dirp.Bcache == nil {
			panic(fmt.Sprintf("No block cache: %q", dirp))
		}
		bp = dirp.Bcache.GetBlock(devinfo.Devnum, b, common.DIRECTORY_BLOCK, common.NORMAL)
		if bp == nil {
			panic("get_block returned NO_BLOCK")
		}

		// Search the directory block
		dirarr := bp.Block.(common.DirectoryBlock)
		for i := 0; i < len(dirarr); i++ {
			dp = &dirarr[i]
			new_slots++
			if new_slots > old_slots { // not found, but room left
				if op == ENTER {
					e_hit = true
				}
				break
			}

			// Match occurs if string found
			if op != ENTER && dp.Inum != 0 {
				if op == IS_EMPTY {
					// If this succeeds, dir is not empty
					if !dp.HasName(".") && !dp.HasName("..") {
						log.Printf("Found entry: %s (%d)", dp.String(), dp.Inum)
						match = true
					}
				} else {
					if dp.HasName(path) {
						match = true
					}
				}
			}

			if match {
				var r error = nil
				// LOOK_UP or DELETE found what it wanted
				if op == IS_EMPTY {
					r = common.ENOTEMPTY
				} else if op == DELETE {
					// TODO: Save inode for recovery
					dp.Inum = 0 // erase entry
					bp.Dirty = true
					dirp.Dirty = true
				} else {
					*inum = int(dp.Inum)
				}
				dirp.Bcache.PutBlock(bp, common.DIRECTORY_BLOCK)
				return r
			}

			// Check for free slot for the benefit of ENTER
			if op == ENTER && dp.Inum == 0 {
				e_hit = true // we found a free slot
				break
			}
		}

		// The whole block has been searched or ENTER has a free slot
		if e_hit { // e_hit set if ENTER can be performed now
			break
		}
		dirp.Bcache.PutBlock(bp, common.DIRECTORY_BLOCK) // otherwise continue searching dir
	}

	// The whole directory has now been searched
	if op != ENTER {
		if op == IS_EMPTY {
			return nil
		} else {
			return common.ENOENT
		}
	}

	// This call is for ENTER. If no free slot has been found so far, try to
	// extend directory.
	if !e_hit { // directory is full and no room left in last block
		new_slots++ // increase directory size by 1 entry
		// TODO: Does this rely on overflow? Does it work?
		if new_slots == 0 { // dir size limited by slot count (overflow?)
			return common.EFBIG
		}
		var err error
		bp, err = common.NewBlock(dirp, int(dirp.Size), common.DIRECTORY_BLOCK, dirp.Bcache)
		if err != nil {
			return err
		}
		dirarr := bp.Block.(common.DirectoryBlock)
		dp = &dirarr[0]
		extended = true
	}

	// 'bp' now points to a directory block with space. 'dp' points to a slot
	// in that block.

	// Set the name of this directory entry
	pathb := []byte(path)
	if len(pathb) < common.NAME_MAX {
		dp.Name[len(pathb)] = 0
	}
	for i := 0; i < common.NAME_MAX && i < len(pathb); i++ {
		dp.Name[i] = pathb[i]
	}
	dp.Inum = uint32(*inum)
	bp.Dirty = true

	dirp.Bcache.PutBlock(bp, common.DIRECTORY_BLOCK)
	// TODO: update times
	dirp.Dirty = true
	if new_slots > old_slots {
		dirp.Size = (int32(new_slots * common.DIR_ENTRY_SIZE))
		// Send the change to disk if the directory is extended
		if extended {
			// TODO: Write this inode out to the block cache
			dirp.Icache.FlushInode(dirp)
		}
	}
	return nil
}