Skip to content

Commit

Permalink
allow setting favorites, mtime and a temporary etag
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 Jan 8, 2021
1 parent 36d7490 commit 733d40a
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 50 deletions.
142 changes: 124 additions & 18 deletions pkg/storage/fs/ocis/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,37 @@ package ocis

import (
"context"
"fmt"
"path/filepath"
"strconv"
"strings"
"time"

provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/user"
"github.com/pkg/errors"
"github.com/pkg/xattr"
)

func parseMTime(v string) (t time.Time, err error) {
p := strings.SplitN(v, ".", 2)
var sec, nsec int64
if sec, err = strconv.ParseInt(p[0], 10, 64); err == nil {
if len(p) > 1 {
nsec, err = strconv.ParseInt(p[1], 10, 64)
}
}
return time.Unix(sec, nsec), err
}

func (fs *ocisfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) (err error) {
n, err := fs.lu.NodeFromResource(ctx, ref)
if err != nil {
return errors.Wrap(err, "ocisfs: error resolving ref")
}
sublog := appctx.GetLogger(ctx).With().Interface("node", n).Logger()

if !n.Exists {
err = errtypes.NotFound(filepath.Join(n.ParentID, n.Name))
Expand All @@ -52,29 +69,83 @@ func (fs *ocisfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Refere
}

nodePath := n.lu.toInternalPath(n.ID)

errs := []error{}
// TODO should we really continue updating when an error occurs?
if md.Metadata != nil {
if val, ok := md.Metadata["mtime"]; ok {
delete(md.Metadata, "mtime")
err := n.SetMtime(ctx, val)
if err != nil {
errs = append(errs, errors.Wrap(err, "could not set mtime"))
}
}
// TODO(jfd) special handling for atime?
// TODO(jfd) allow setting birth time (btime)?
// TODO(jfd) any other metadata that is interesting? fileid?
// TODO unset when file is updated
// TODO unset when folder is updated or add timestamp to etag?
if val, ok := md.Metadata["etag"]; ok {
delete(md.Metadata, "etag")
err := n.SetEtag(ctx, val)
if err != nil {
errs = append(errs, errors.Wrap(err, "could not set etag"))
}
}
if val, ok := md.Metadata[_favoriteKey]; ok {
delete(md.Metadata, _favoriteKey)
if u, ok := user.ContextGetUser(ctx); ok {
if uid := u.GetId(); uid != nil {
if err := n.SetFavorite(uid, val); err != nil {
sublog.Error().Err(err).
Interface("user", u).
Msg("could not set favorite flag")
errs = append(errs, errors.Wrap(err, "could not set favorite flag"))
}
} else {
sublog.Error().Interface("user", u).Msg("user has no id")
errs = append(errs, errors.Wrap(errtypes.UserRequired("userrequired"), "user has no id"))
}
} else {
sublog.Error().Interface("user", u).Msg("error getting user from ctx")
errs = append(errs, errors.Wrap(errtypes.UserRequired("userrequired"), "error getting user from ctx"))
}
}
}
for k, v := range md.Metadata {
// TODO set etag as temporary etag tmpEtagAttr
attrName := metadataPrefix + k
if err = xattr.Set(nodePath, attrName, []byte(v)); err != nil {
return errors.Wrap(err, "ocisfs: could not set metadata attribute "+attrName+" to "+k)
errs = append(errs, errors.Wrap(err, "ocisfs: could not set metadata attribute "+attrName+" to "+k))
}
}
return

switch len(errs) {
case 0:
return fs.tp.Propagate(ctx, n)
case 1:
// TODO Propagate if anything changed
return errs[0]
default:
// TODO Propagate if anything changed
// TODO how to return multiple errors?
return errors.New("multiple errors occurred, see log for details")
}
}

func (fs *ocisfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) (err error) {
n, err := fs.lu.NodeFromResource(ctx, ref)
if err != nil {
return errors.Wrap(err, "ocisfs: error resolving ref")
}
sublog := appctx.GetLogger(ctx).With().Interface("node", n).Logger()

if !n.Exists {
err = errtypes.NotFound(filepath.Join(n.ParentID, n.Name))
return err
}

ok, err := fs.p.HasPermission(ctx, n, func(rp *provider.ResourcePermissions) bool {
// TODO use SetArbitraryMetadata grant to CS3 api, tracked in https://github.com/cs3org/cs3apis/issues/91
// TODO use SetArbitraryMetadata grant to CS3 api, tracked in https://github.com/cs3org/cs3apis/issues/91
return rp.InitiateFileUpload
})
switch {
Expand All @@ -85,22 +156,57 @@ func (fs *ocisfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refe
}

nodePath := n.lu.toInternalPath(n.ID)
for i := range keys {
attrName := metadataPrefix + keys[i]
if err = xattr.Remove(nodePath, attrName); err != nil {
// a non-existing attribute will return an error, which we can ignore
// (using string compare because the error type is syscall.Errno and not wrapped/recognizable)
if e, ok := err.(*xattr.Error); !ok || !(e.Err.Error() == "no data available" ||
// darwin
e.Err.Error() == "attribute not found") {
appctx.GetLogger(ctx).Error().Err(err).
Interface("node", n).
Str("key", keys[i]).
Msg("could not unset metadata")
errs := []error{}
for _, k := range keys {
switch k {
case _favoriteKey:
if u, ok := user.ContextGetUser(ctx); ok {
// the favorite flag is specific to the user, so we need to incorporate the userid
if uid := u.GetId(); uid != nil {
fa := fmt.Sprintf("%s%s@%s", favPrefix, uid.GetOpaqueId(), uid.GetIdp())
if err := xattr.Remove(nodePath, fa); err != nil {
sublog.Error().Err(err).
Interface("user", u).
Str("key", fa).
Msg("could not unset favorite flag")
errs = append(errs, errors.Wrap(err, "could not unset favorite flag"))
}
} else {
sublog.Error().
Interface("user", u).
Msg("user has no id")
errs = append(errs, errors.Wrap(errtypes.UserRequired("userrequired"), "user has no id"))
}
} else {
err = nil
sublog.Error().
Interface("user", u).
Msg("error getting user from ctx")
errs = append(errs, errors.Wrap(errtypes.UserRequired("userrequired"), "error getting user from ctx"))
}
default:
if err = xattr.Remove(nodePath, metadataPrefix+k); err != nil {
// a non-existing attribute will return an error, which we can ignore
// (using string compare because the error type is syscall.Errno and not wrapped/recognizable)
if e, ok := err.(*xattr.Error); !ok || !(e.Err.Error() == "no data available" ||
// darwin
e.Err.Error() == "attribute not found") {
sublog.Error().Err(err).
Str("key", k).
Msg("could not unset metadata")
errs = append(errs, errors.Wrap(err, "could not unset metadata"))
}
}
}
}
return
switch len(errs) {
case 0:
return fs.tp.Propagate(ctx, n)
case 1:
// TODO Propagate if anything changed
return errs[0]
default:
// TODO Propagate if anything changed
// TODO how to return multiple errors?
return errors.New("multiple errors occurred, see log for details")
}
}
Loading

0 comments on commit 733d40a

Please sign in to comment.