Skip to content

Commit

Permalink
fix list trash
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
  • Loading branch information
butonic committed Feb 16, 2022
1 parent e4c1191 commit 15db9ad
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 61 deletions.
12 changes: 12 additions & 0 deletions pkg/storage/utils/decomposedfs/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ func (n *Node) SetMetadata(key string, val string) (err error) {
return nil
}

// RemoveMetadata removes a given key
func (n *Node) RemoveMetadata(key string) (err error) {
if err = xattr.Remove(n.InternalPath(), key); err != nil {
if e, ok := err.(*xattr.Error); ok && (e.Err.Error() == "no data available" ||
// darwin
e.Err.Error() == "attribute not found") {
return nil
}
}
return err
}

// GetMetadata reads the metadata for the given key
func (n *Node) GetMetadata(key string) (val string, err error) {
nodePath := n.InternalPath()
Expand Down
52 changes: 27 additions & 25 deletions pkg/storage/utils/decomposedfs/recycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,64 +166,66 @@ func (fs *Decomposedfs) createTrashItem(ctx context.Context, spaceID, parentNode
return item, nil
}

// readTrashLink returns nodeID and timestamp
func readTrashLink(path string) (string, string, error) {
link, err := os.Readlink(path)
if err != nil {
return "", "", err
}
// ../../../../../nodes/e5/6c/75/a8/-d235-4cbb-8b4e-48b6fd0f2094.T.2022-02-16T14:38:11.769917408Z
// TODO use filepath.Separator to support windows
link = strings.ReplaceAll(link, "/", "")
// ..........nodese56c75a8-d235-4cbb-8b4e-48b6fd0f2094.T.2022-02-16T14:38:11.769917408Z
if link[0:15] != "..........nodes" || link[51:54] != ".T." {
return "", "", errtypes.InternalError("malformed trash link")
}
return link[15:51], link[54:], nil
}

func (fs *Decomposedfs) listTrashRoot(ctx context.Context, spaceID string) ([]*provider.RecycleItem, error) {
log := appctx.GetLogger(ctx)
items := make([]*provider.RecycleItem, 0)

trashRoot := fs.getRecycleRoot(ctx, spaceID)
f, err := os.Open(trashRoot)
if err != nil {
if os.IsNotExist(err) {
return items, nil
}
return nil, errors.Wrap(err, "tree: error listing "+trashRoot)
}
defer f.Close()

names, err := f.Readdirnames(0)
matches, err := filepath.Glob(trashRoot + "/*/*/*/*/*")
if err != nil {
return nil, err
}

for i := range names {
trashnode, err := os.Readlink(filepath.Join(trashRoot, names[i]))
for _, itemPath := range matches {
nodeID, timeSuffix, err := readTrashLink(itemPath)
if err != nil {
log.Error().Err(err).Str("trashRoot", trashRoot).Str("name", names[i]).Msg("error reading trash link, skipping")
continue
}
parts := strings.SplitN(filepath.Base(trashnode), node.TrashIDDelimiter, 2)
if len(parts) != 2 {
log.Error().Err(err).Str("trashRoot", trashRoot).Str("name", names[i]).Str("trashnode", trashnode).Interface("parts", parts).Msg("malformed trash link, skipping")
log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Msg("error reading trash link, skipping")
continue
}

nodePath := fs.lu.InternalPath(spaceID, filepath.Base(trashnode))
nodePath := fs.lu.InternalPath(spaceID, nodeID) + node.TrashIDDelimiter + timeSuffix
md, err := os.Stat(nodePath)
if err != nil {
log.Error().Err(err).Str("trashRoot", trashRoot).Str("name", names[i]).Str("trashnode", trashnode). /*.Interface("parts", parts)*/ Msg("could not stat trash item, skipping")
log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node_path", nodePath).Msg("could not stat trash item, skipping")
continue
}

item := &provider.RecycleItem{
Type: getResourceType(md.IsDir()),
Size: uint64(md.Size()),
Key: parts[0],
Key: nodeID,
}
if deletionTime, err := time.Parse(time.RFC3339Nano, parts[1]); err == nil {
if deletionTime, err := time.Parse(time.RFC3339Nano, timeSuffix); err == nil {
item.DeletionTime = &types.Timestamp{
Seconds: uint64(deletionTime.Unix()),
// TODO nanos
}
} else {
log.Error().Err(err).Str("trashRoot", trashRoot).Str("name", names[i]).Str("link", trashnode).Interface("parts", parts).Msg("could parse time format, ignoring")
log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not parse time format, ignoring")
}

// lookup origin path in extended attributes
var attrBytes []byte
if attrBytes, err = xattr.Get(nodePath, xattrs.TrashOriginAttr); err == nil {
item.Ref = &provider.Reference{Path: string(attrBytes)}
} else {
log.Error().Err(err).Str("trashRoot", trashRoot).Str("name", names[i]).Str("link", trashnode).Msg("could not read origin path, skipping")
log.Error().Err(err).Str("trashRoot", trashRoot).Str("item", itemPath).Str("node", nodeID).Str("dtime", timeSuffix).Msg("could not read origin path, skipping")
continue
}
// TODO filter results by permission ... on the original parent? or the trashed node?
Expand Down Expand Up @@ -326,5 +328,5 @@ func getResourceType(isDir bool) provider.ResourceType {
}

func (fs *Decomposedfs) getRecycleRoot(ctx context.Context, spaceID string) string {
return filepath.Join(fs.o.Root, "trash", spaceID)
return filepath.Join(fs.o.Root, "spaces", Pathify(spaceID, 1, 2), "trash")
}
31 changes: 6 additions & 25 deletions pkg/storage/utils/decomposedfs/spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ func readSpaceAndNodeFromSpaceTypeLink(path string) (string, string, error) {
if err != nil {
return "", "", err
}
// ../../spaces/4c/510ada-c86b-4815-8820-42cdf82c3d51/nodes/4c/51/0a/da/-c86b-4815-8820-42cdf82c3d51
// TODO use filepath.Separator to support windows
link = strings.ReplaceAll(link, "/", "")
// ../../spaces/4c/510ada-c86b-4815-8820-42cdf82c3d51/nodes/4c/51/0a/da/-c86b-4815-8820-42cdf82c3d51
// ....spaces4c510ada-c86b-4815-8820-42cdf82c3d51nodes4c510ada-c86b-4815-8820-42cdf82c3d51
if link[0:10] != "....spaces" || link[46:51] != "nodes" {
return "", "", errtypes.InternalError("malformed link")
}
Expand Down Expand Up @@ -363,10 +364,10 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up
}

space := req.StorageSpace
_, spaceID, _ := utils.SplitStorageSpaceID(space.Id.OpaqueId)
spaceID, nodeID, _ := utils.SplitStorageSpaceID(space.Id.OpaqueId)

if restore {
matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spacetypes", spaceTypeAny, spaceID))
matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spacetypes", spaceTypeAny, nodeID))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -402,28 +403,7 @@ func (fs *Decomposedfs) UpdateStorageSpace(ctx context.Context, req *provider.Up
}
}

matches, err := filepath.Glob(filepath.Join(fs.o.Root, "spacetypes", spaceTypeAny, spaceID))
if err != nil {
return nil, err
}

if len(matches) != 1 {
return &provider.UpdateStorageSpaceResponse{
Status: &v1beta11.Status{
Code: v1beta11.Code_CODE_NOT_FOUND,
Message: fmt.Sprintf("update space failed: found %d matching spaces", len(matches)),
},
}, nil
}

target, err := os.Readlink(matches[0])
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("match", matches[0]).Msg("could not read link, skipping")
}
nodeID := strings.TrimLeft(target, "/.")
nodeID = strings.ReplaceAll(nodeID, "/", "")
node, err := node.ReadNode(ctx, fs.lu, spaceID, nodeID)

node, err := node.ReadNode(ctx, fs.lu, spaceID, spaceID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -581,6 +561,7 @@ func (fs *Decomposedfs) createStorageSpace(ctx context.Context, spaceType string
}

// we can reuse the node id as the space id
// TODO pathify spaceid
err := os.Symlink("../../spaces/"+Pathify(spaceID, 1, 2)+"/nodes/"+Pathify(spaceID, 4, 2), filepath.Join(fs.o.Root, "spacetypes", spaceType, spaceID))
if err != nil {
if isAlreadyExists(err) {
Expand Down
30 changes: 19 additions & 11 deletions pkg/storage/utils/decomposedfs/tree/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,6 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) {
src := filepath.Join(n.ParentInternalPath(), n.Name)
return os.Remove(src)
}
// Prepare the trash
err = os.MkdirAll(filepath.Join(t.root, "trash", n.SpaceRoot.ID), 0700)
if err != nil {
return
}

// get the original path
origin, err := t.lookup.Path(ctx, n)
Expand All @@ -444,13 +439,24 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) {

deletionTime := time.Now().UTC().Format(time.RFC3339Nano)

// Prepare the trash
trashLink := filepath.Join(t.root, "spaces", Pathify(n.SpaceRoot.ID, 1, 2), "trash", Pathify(n.ID, 4, 2))
if err := os.MkdirAll(filepath.Dir(trashLink), 0700); err != nil {
// Roll back changes
n.RemoveMetadata(xattrs.TrashOriginAttr)
return err
}

// FIXME can we just move the node into the trash dir? instead of adding another symlink and appending a trash timestamp?
// can we just use the mtime as the trash time?
// TODO store a trashed by userid

// first make node appear in the space trash
// parent id and name are stored as extended attributes in the node itself
trashLink := filepath.Join(t.root, "trash", n.SpaceRoot.ID, n.ID)
err = os.Symlink("../../nodes/"+n.SpaceRoot.ID+n.ID+node.TrashIDDelimiter+deletionTime, trashLink)
err = os.Symlink("../../../../../nodes/"+Pathify(n.ID, 4, 2)+node.TrashIDDelimiter+deletionTime, trashLink)
if err != nil {
// To roll back changes
// TODO unset trashOriginAttr
// Roll back changes
n.RemoveMetadata(xattrs.TrashOriginAttr)
return
}

Expand All @@ -462,7 +468,8 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) {
if err != nil {
// To roll back changes
// TODO remove symlink
// TODO unset trashOriginAttr
// Roll back changes
n.RemoveMetadata(xattrs.TrashOriginAttr)
return
}

Expand All @@ -476,7 +483,8 @@ func (t *Tree) Delete(ctx context.Context, n *node.Node) (err error) {
// To roll back changes
// TODO revert the rename
// TODO remove symlink
// TODO unset trashOriginAttr
// Roll back changes
n.RemoveMetadata(xattrs.TrashOriginAttr)
return
}

Expand Down

0 comments on commit 15db9ad

Please sign in to comment.