func (file *server_File) loop() { alive := true for alive { req := <-file.in switch req := req.(type) { case req_File_Read: // Indicate we have another outstanding reader file.wg.Add(1) callback := make(chan resFile) file.out <- res_File_Async{callback} // Launch a new goroutine to perform the read, using the callback // channel to return the result. go func() { n, err := common.Read(file.rip, req.buf, req.pos) callback <- res_File_Read{n, err} file.wg.Done() // signal completion }() case req_File_Write: file.wg.Wait() // wait for any outstanding reads to complete before proceeding n, err := common.Write(file.rip, req.buf, req.pos) file.out <- res_File_Write{n, err} case req_File_Truncate: file.wg.Wait() // wait for any outstanding reads to complete before proceeding common.Truncate(file.rip, req.size, file.rip.Bcache) file.out <- res_File_Truncate{} case req_File_Fstat: // Code here case req_File_Sync: // Code here case req_File_Dup: file.count++ file.out <- res_File_Dup{} case req_File_Close: file.wg.Wait() // wait for any outstanding reads to complete before proeceding file.count-- // Let's push our changes to the inode cache file.rip.Icache.FlushInode(file.rip) file.rip.Icache.PutInode(file.rip) if file.count == 0 { alive = false } file.out <- res_File_Close{} } } }
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 (itable *server_InodeTbl) loop() { alive := true for alive { req := <-itable.in switch req := req.(type) { case req_InodeTbl_MountDevice: itable.devices[req.devnum] = req.info itable.out <- res_InodeTbl_MountDevice{} case req_InodeTbl_UnmountDevice: itable.devices[req.devnum] = nil itable.out <- res_InodeTbl_UnmountDevice{} case req_InodeTbl_GetInode: // Create an channel on which the inode result will be delivered callback := make(chan resInodeTbl) // Find the cache slot for an inode, or a candidate slot in which // the inode can be loaded.` slotIndex := itable.findSlot(req.devnum, req.inum) if slotIndex == common.NO_INODE || slotIndex >= len(itable.slots) { // Inode table is completely full! itable.out <- res_InodeTbl_Async{callback} callback <- res_InodeTbl_GetInode{nil, common.ENFILE} } else { xp := itable.slots[slotIndex].inode if xp.Count > 0 { // We found the inode, so return it slot := itable.slots[slotIndex] xp.Count++ itable.out <- res_InodeTbl_Async{callback} slot.ReturnOrQueue(callback) } else { // Need to load the inode asynchronously, so make sure the // cache slot isn't claimed by someone else in the meantime slot := itable.slots[slotIndex] xp.Devinfo = itable.devices[req.devnum] xp.Inum = req.inum xp.Count++ slot.Queue(callback) go func() { // Load the inode into the Inode itable.loadInode(xp) // Notify anyone waiting for this slot slot.FinishedLoading(xp) }() itable.out <- res_InodeTbl_Async{callback} } } case req_InodeTbl_DupInode: // Given an inode, duplicate it by incrementing its count rip := req.inode rip.Count++ itable.out <- res_InodeTbl_DupInode{rip} case req_InodeTbl_PutInode: rip := req.inode if rip == nil { itable.out <- res_InodeTbl_PutInode{} continue } rip.Count-- if rip.Count == 0 { // means no one is using it now if rip.Nlinks == 0 { // free the inode common.Truncate(rip, 0, itable.bcache) // return all the disk blocks rip.Mode = common.I_NOT_ALLOC rip.Dirty = true rip.Devinfo.AllocTbl.FreeInode(rip.Inum) } else { // TODO: Handle the pipe case here // if rip.pipe == true { // truncate(rip) // } } // rip.pipe = false if rip.Dirty { // Write this inode out to disk // TODO: Should this be performed asynchronously? itable.writeInode(rip) } } itable.out <- res_InodeTbl_PutInode{} case req_InodeTbl_FlushInode: rip := req.inode if rip == nil { itable.out <- res_InodeTbl_FlushInode{} } else { itable.writeInode(rip) } itable.out <- res_InodeTbl_FlushInode{} case req_InodeTbl_IsDeviceBusy: count := 0 for i := 0; i < len(itable.slots); i++ { rip := itable.slots[i].inode if rip.Count > 0 && rip.Devinfo.Devnum == req.devnum { count += rip.Count } } itable.out <- res_InodeTbl_IsDeviceBusy{count > 1} case req_InodeTbl_Shutdown: for i := 0; i < len(itable.devices); i++ { if itable.devices[i] != nil { itable.out <- res_InodeTbl_Shutdown{common.EBUSY} continue } } itable.out <- res_InodeTbl_Shutdown{nil} alive = false } } }