func (o *OortFS) Create(ctx context.Context, parent, id []byte, inode uint64, name string, attr *pb.Attr, isdir bool) (string, *pb.Attr, error) { // Check to see if the name already exists b, err := o.comms.ReadGroupItem(ctx, parent, []byte(name)) if err != nil && !store.IsNotFound(err) { // TODO: Needs beter error handling return "", &pb.Attr{}, err } if len(b) > 0 { p := &pb.DirEntry{} err = formic.Unmarshal(b, p) if err != nil { return "", &pb.Attr{}, err } // Return an error if entry already exists and is not a tombstone if p.Tombstone == nil { return "", &pb.Attr{}, nil } } var direntType fuse.DirentType if isdir { direntType = fuse.DT_Dir } else { direntType = fuse.DT_File } // Add the name to the group d := &pb.DirEntry{ Version: DirEntryVersion, Name: name, Id: id, Type: uint32(direntType), } b, err = formic.Marshal(d) if err != nil { return "", &pb.Attr{}, err } err = o.comms.WriteGroup(ctx, parent, []byte(name), b) if err != nil { return "", &pb.Attr{}, err } // Add the inode entry n := &pb.InodeEntry{ Version: InodeEntryVersion, Inode: inode, IsDir: isdir, Attr: attr, Blocks: 0, } b, err = formic.Marshal(n) if err != nil { return "", &pb.Attr{}, err } err = o.WriteChunk(ctx, id, b) if err != nil { return "", &pb.Attr{}, err } return name, attr, nil }
func (o *OortFS) Rename(ctx context.Context, oldParent, newParent []byte, oldName, newName string) (*pb.RenameResponse, error) { // Get the ID from the group list b, err := o.comms.ReadGroupItem(ctx, oldParent, []byte(oldName)) if store.IsNotFound(err) { return &pb.RenameResponse{}, nil } if err != nil { return &pb.RenameResponse{}, err } d := &pb.DirEntry{} err = formic.Unmarshal(b, d) if err != nil { return &pb.RenameResponse{}, err } // TODO: Handle orphaned data from overwrites // Create new entry d.Name = newName b, err = formic.Marshal(d) err = o.comms.WriteGroup(ctx, newParent, []byte(newName), b) if err != nil { return &pb.RenameResponse{}, err } // Delete old entry err = o.comms.DeleteGroupItem(ctx, oldParent, []byte(oldName)) if err != nil { // TODO: Handle errors // If we fail here then we will have two entries return &pb.RenameResponse{}, err } return &pb.RenameResponse{}, nil }
func (o *OortFS) Update(ctx context.Context, id []byte, block, blocksize, size uint64, mtime int64) error { b, err := o.GetChunk(ctx, id) if err != nil { return err } n := &pb.InodeEntry{} err = formic.Unmarshal(b, n) if err != nil { return err } blocks := n.Blocks if block >= blocks { n.Blocks = block + 1 n.LastBlock = size n.BlockSize = blocksize n.Attr.Size = blocksize*block + size } else if block == (blocks - 1) { n.LastBlock = size n.Attr.Size = blocksize*block + size } if mtime > n.Attr.Mtime { n.Attr.Mtime = mtime } b, err = formic.Marshal(n) if err != nil { return err } err = o.WriteChunk(ctx, id, b) if err != nil { return err } return nil }
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 := formic.Marshal(r) if err != nil { return err } err = o.WriteChunk(ctx, id, b) if err != nil { return err } } return nil }
func (o *OortFS) Symlink(ctx context.Context, parent, id []byte, name string, target string, attr *pb.Attr, inode uint64) (*pb.SymlinkResponse, error) { // Check to see if the name exists val, err := o.comms.ReadGroupItem(ctx, parent, []byte(name)) if err != nil && !store.IsNotFound(err) { // TODO: Needs beter error handling return &pb.SymlinkResponse{}, err } if len(val) > 1 { // Exists already return &pb.SymlinkResponse{}, nil } n := &pb.InodeEntry{ Version: InodeEntryVersion, Inode: inode, IsDir: false, IsLink: true, Target: target, Attr: attr, } b, err := formic.Marshal(n) if err != nil { return &pb.SymlinkResponse{}, err } err = o.WriteChunk(ctx, id, b) if err != nil { return &pb.SymlinkResponse{}, err } // Add the name to the group d := &pb.DirEntry{ Version: DirEntryVersion, Name: name, Id: id, Type: uint32(fuse.DT_File), } b, err = formic.Marshal(d) if err != nil { return &pb.SymlinkResponse{}, err } err = o.comms.WriteGroup(ctx, parent, []byte(name), b) if err != nil { return &pb.SymlinkResponse{}, err } return &pb.SymlinkResponse{Name: name, Attr: attr}, nil }
func (o *OortFS) WriteChunk(ctx context.Context, id, data []byte) error { crc := o.hasher() crc.Write(data) fb := &pb.FileBlock{ Version: FileBlockVersion, Data: data, Checksum: crc.Sum32(), } b, err := formic.Marshal(fb) if err != nil { return err } return o.comms.WriteValue(ctx, id, b) }
func (o *OortFS) SetAttr(ctx context.Context, id []byte, attr *pb.Attr, v uint32) (*pb.Attr, error) { valid := fuse.SetattrValid(v) b, err := o.GetChunk(ctx, id) if err != nil { return &pb.Attr{}, err } n := &pb.InodeEntry{} err = formic.Unmarshal(b, n) if err != nil { return &pb.Attr{}, err } if valid.Mode() { n.Attr.Mode = attr.Mode } if valid.Size() { if n.Attr.Size == 0 { n.Blocks = 0 n.LastBlock = 0 } n.Attr.Size = attr.Size } if valid.Mtime() { n.Attr.Mtime = attr.Mtime } if valid.Atime() { n.Attr.Atime = attr.Atime } if valid.Uid() { n.Attr.Uid = attr.Uid } if valid.Gid() { n.Attr.Gid = attr.Gid } b, err = formic.Marshal(n) if err != nil { return &pb.Attr{}, err } err = o.WriteChunk(ctx, id, b) if err != nil { return &pb.Attr{}, err } return n.Attr, nil }
func (o *OortFS) Remove(ctx context.Context, parent []byte, name string) (int32, error) { // Get the ID from the group list b, err := o.comms.ReadGroupItem(ctx, parent, []byte(name)) if store.IsNotFound(err) { return 1, nil } else if err != nil { return 1, err } d := &pb.DirEntry{} err = formic.Unmarshal(b, d) if err != nil { return 1, err } // TODO: More error handling needed // TODO: Handle possible race conditions where user writes and deletes the same file over and over // Mark the item deleted in the group t := &pb.Tombstone{} tsm := brimtime.TimeToUnixMicro(time.Now()) t.Dtime = tsm t.Qtime = tsm t.FsId = []byte("1") // TODO: Make sure this gets set when we are tracking fsids inode, err := o.GetInode(ctx, d.Id) if err != nil { return 1, err } t.Blocks = inode.Blocks t.Inode = inode.Inode d.Tombstone = t b, err = formic.Marshal(d) if err != nil { return 1, err } // NOTE: The tsm-1 is kind of a hack because the timestamp needs to be updated on this write, but if we choose tsm, once the actual delete comes through, it will not work because it is going to try to delete with a timestamp of tsm. err = o.comms.WriteGroupTS(ctx, parent, []byte(name), b, tsm-1) if err != nil { return 1, err // Not really sure what should be done here to try to recover from err } o.deleteChan <- &DeleteItem{ parent: parent, name: name, } return 0, nil }
func (o *OortFS) Removexattr(ctx context.Context, id []byte, name string) (*pb.RemovexattrResponse, error) { b, err := o.GetChunk(ctx, id) if err != nil { return &pb.RemovexattrResponse{}, err } n := &pb.InodeEntry{} err = formic.Unmarshal(b, n) if err != nil { return &pb.RemovexattrResponse{}, err } delete(n.Xattr, name) b, err = formic.Marshal(n) if err != nil { return &pb.RemovexattrResponse{}, err } err = o.WriteChunk(ctx, id, b) if err != nil { return &pb.RemovexattrResponse{}, err } return &pb.RemovexattrResponse{}, nil }
func (o *OortFS) Setxattr(ctx context.Context, id []byte, name string, value []byte) (*pb.SetxattrResponse, error) { b, err := o.GetChunk(ctx, id) if err != nil { return &pb.SetxattrResponse{}, err } n := &pb.InodeEntry{} err = formic.Unmarshal(b, n) if err != nil { return &pb.SetxattrResponse{}, err } if n.Xattr == nil { n.Xattr = make(map[string][]byte) } n.Xattr[name] = value b, err = formic.Marshal(n) if err != nil { return &pb.SetxattrResponse{}, err } err = o.WriteChunk(ctx, id, b) if err != nil { return &pb.SetxattrResponse{}, err } return &pb.SetxattrResponse{}, nil }