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 }
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 }
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 }
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 }
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 }
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 }
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) }
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 }
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 }
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 }