func (s *apiServer) Create(ctx context.Context, r *pb.CreateRequest) (*pb.CreateResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } ts := time.Now().Unix() inode := s.fl.GetID() attr := &pb.Attr{ Inode: inode, Atime: ts, Mtime: ts, Ctime: ts, Crtime: ts, Mode: r.Attr.Mode, Uid: r.Attr.Uid, Gid: r.Attr.Gid, } rname, rattr, err := s.fs.Create(ctx, formic.GetID(fsid.Bytes(), r.Parent, 0), formic.GetID(fsid.Bytes(), inode, 0), inode, r.Name, attr, false) if err != nil { return nil, err } return &pb.CreateResponse{Name: rname, Attr: rattr}, err }
func (s *apiServer) Write(ctx context.Context, r *pb.WriteRequest) (*pb.WriteResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } log.Printf("WRITE: Inode %d Offset: %d Size: %d", r.Inode, r.Offset, len(r.Payload)) block := uint64(r.Offset / s.blocksize) firstOffset := int64(0) if r.Offset%s.blocksize != 0 { // Handle non-aligned offset firstOffset = r.Offset - int64(block)*s.blocksize } cur := int64(0) for cur < int64(len(r.Payload)) { sendSize := min(s.blocksize, int64(len(r.Payload))-cur) if sendSize+firstOffset > s.blocksize { sendSize = s.blocksize - firstOffset } payload := r.Payload[cur : cur+sendSize] id := formic.GetID(fsid.Bytes(), r.Inode, block+1) // 0 block is for inode data if firstOffset > 0 || sendSize < s.blocksize { // need to get the block and update chunk := make([]byte, firstOffset+int64(len(payload))) data, err := s.fs.GetChunk(ctx, id) if firstOffset > 0 && err != nil { // TODO: How do we differentiate a block that hasn't been created yet, and a block that is truely missing? log.Printf("WARN: couldn't get block id %d", id) } else { if len(data) > len(chunk) { chunk = data } else { copy(chunk, data) } } copy(chunk[firstOffset:], payload) payload = chunk firstOffset = 0 } err := s.fs.WriteChunk(ctx, id, payload) // TODO: Need better error handling for failing with multiple chunks if err != nil { return &pb.WriteResponse{Status: 1}, err } s.updateChan <- &UpdateItem{ id: formic.GetID(fsid.Bytes(), r.Inode, 0), block: block, blocksize: uint64(s.blocksize), size: uint64(len(payload)), mtime: time.Now().Unix(), } cur += sendSize block += 1 } return &pb.WriteResponse{Status: 0}, nil }
func TestGetID(t *testing.T) { id1 := formic.GetID([]byte("1"), uint64(1), uint64(1)) id2 := formic.GetID([]byte("1"), uint64(1), uint64(1)) if !bytes.Equal(id1, id2) { t.Errorf("Generated IDs not equal") } id3 := formic.GetID([]byte("1"), uint64(1), uint64(2)) if bytes.Equal(id1, id3) { t.Errorf("Generated IDs were equal") } }
func (s *apiServer) Rename(ctx context.Context, r *pb.RenameRequest) (*pb.RenameResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } return s.fs.Rename(ctx, formic.GetID(fsid.Bytes(), r.OldParent, 0), formic.GetID(fsid.Bytes(), r.NewParent, 0), r.OldName, r.NewName) }
func (d *Deletinator) run() { // TODO: Parallelize this thing? for { todelete := <-d.in log.Println("Deleting: ", todelete) // TODO: Need better context ctx := context.Background() // Get the dir entry info dirent, err := d.fs.GetDirent(ctx, todelete.parent, todelete.name) if store.IsNotFound(err) { // NOTE: If it isn't found then it is likely deleted. // Do we need to do more to ensure this? // Skip for now continue } if err != nil { // TODO Better error handling? // re-q the id, to try again later log.Print("Delete error getting dirent: ", err) d.in <- todelete continue } ts := dirent.Tombstone deleted := uint64(0) for b := uint64(0); b < ts.Blocks; b++ { // Delete each block id := formic.GetID(ts.FsId, ts.Inode, b+1) err := d.fs.DeleteChunk(ctx, id, ts.Dtime) if err != nil && !store.IsNotFound(err) && err != ErrStoreHasNewerValue { continue } deleted++ } if deleted == ts.Blocks { // Everything is deleted so delete the entry err := d.fs.DeleteChunk(ctx, formic.GetID(ts.FsId, ts.Inode, 0), ts.Dtime) if err != nil && !store.IsNotFound(err) && err != ErrStoreHasNewerValue { // Couldn't delete the inode entry so try again later d.in <- todelete continue } err = d.fs.DeleteListing(ctx, todelete.parent, todelete.name, ts.Dtime) if err != nil && !store.IsNotFound(err) && err != ErrStoreHasNewerValue { log.Println(" Err: ", err) // TODO: Better error handling // Ignore for now to be picked up later? } } else { // If all artifacts are not deleted requeue for later d.in <- todelete } } }
func (o *OortFS) InitFs(ctx context.Context, fsid []byte) error { id := formic.GetID(fsid, 1, 0) n, _ := o.GetChunk(ctx, id) if len(n) == 0 { log.Println("Creating new root at ", id) // Need to create the root node r := &pb.InodeEntry{ Version: InodeEntryVersion, Inode: 1, IsDir: true, FsId: fsid, } ts := time.Now().Unix() r.Attr = &pb.Attr{ Inode: 1, Atime: ts, Mtime: ts, Ctime: ts, Crtime: ts, Mode: uint32(os.ModeDir | 0775), Uid: 1001, // TODO: need to config default user/group id Gid: 1001, } b, err := proto.Marshal(r) if err != nil { return err } err = o.WriteChunk(ctx, id, b) if err != nil { return err } } return nil }
func (s *apiServer) Removexattr(ctx context.Context, r *pb.RemovexattrRequest) (*pb.RemovexattrResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } return s.fs.Removexattr(ctx, formic.GetID(fsid.Bytes(), r.Inode, 0), r.Name) }
func (s *apiServer) ReadDirAll(ctx context.Context, n *pb.ReadDirAllRequest) (*pb.ReadDirAllResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } return s.fs.ReadDirAll(ctx, formic.GetID(fsid.Bytes(), n.Inode, 0)) }
func (s *apiServer) Remove(ctx context.Context, r *pb.RemoveRequest) (*pb.RemoveResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } status, err := s.fs.Remove(ctx, formic.GetID(fsid.Bytes(), r.Parent, 0), r.Name) return &pb.RemoveResponse{Status: status}, err }
func (s *apiServer) Lookup(ctx context.Context, r *pb.LookupRequest) (*pb.LookupResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } name, attr, err := s.fs.Lookup(ctx, formic.GetID(fsid.Bytes(), r.Parent, 0), r.Name) return &pb.LookupResponse{Name: name, Attr: attr}, err }
func (s *apiServer) SetAttr(ctx context.Context, r *pb.SetAttrRequest) (*pb.SetAttrResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } attr, err := s.fs.SetAttr(ctx, formic.GetID(fsid.Bytes(), r.Attr.Inode, 0), r.Attr, r.Valid) return &pb.SetAttrResponse{Attr: attr}, err }
func (s *apiServer) Symlink(ctx context.Context, r *pb.SymlinkRequest) (*pb.SymlinkResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } ts := time.Now().Unix() inode := s.fl.GetID() attr := &pb.Attr{ Inode: inode, Atime: ts, Mtime: ts, Ctime: ts, Crtime: ts, Mode: uint32(os.ModeSymlink | 0755), Size: uint64(len(r.Target)), Uid: r.Uid, Gid: r.Gid, } return s.fs.Symlink(ctx, formic.GetID(fsid.Bytes(), r.Parent, 0), formic.GetID(fsid.Bytes(), inode, 0), r.Name, r.Target, attr, inode) }
func (s *apiServer) Read(ctx context.Context, r *pb.ReadRequest) (*pb.ReadResponse, error) { err := s.validateIP(ctx) if err != nil { return nil, err } fsid, err := GetFsId(ctx) if err != nil { return nil, err } log.Printf("READ: Inode: %d Offset: %d Size: %d", r.Inode, r.Offset, r.Size) block := uint64(r.Offset / s.blocksize) data := make([]byte, r.Size) firstOffset := int64(0) if r.Offset%s.blocksize != 0 { // Handle non-aligned offset firstOffset = r.Offset - int64(block)*s.blocksize } cur := int64(0) for cur < r.Size { id := formic.GetID(fsid.Bytes(), r.Inode, block+1) // block 0 is for inode data chunk, err := s.fs.GetChunk(ctx, id) if err != nil { log.Print("Err: Failed to read block: ", err) // NOTE: This returns basically 0's to the client.for this block in this case // It is totally valid for a fs to request an invalid block // TODO: Do we need to differentiate between real errors and bad requests? return &pb.ReadResponse{}, nil } if len(chunk) == 0 { break } count := copy(data[cur:], chunk[firstOffset:]) firstOffset = 0 block += 1 cur += int64(count) if int64(len(chunk)) < s.blocksize { break } } f := &pb.ReadResponse{Inode: r.Inode, Payload: data} return f, nil }