github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/node/node.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package node
    20  
    21  import (
    22  	"context"
    23  	"crypto/md5"
    24  	"crypto/sha1"
    25  	"encoding/hex"
    26  	"encoding/json"
    27  	"fmt"
    28  	"hash"
    29  	"hash/adler32"
    30  	"io"
    31  	"os"
    32  	"path/filepath"
    33  	"strconv"
    34  	"strings"
    35  	"time"
    36  
    37  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    38  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    39  	types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    40  	"github.com/cs3org/reva/v2/internal/grpc/services/storageprovider"
    41  	"github.com/cs3org/reva/v2/pkg/appctx"
    42  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    43  	"github.com/cs3org/reva/v2/pkg/errtypes"
    44  	"github.com/cs3org/reva/v2/pkg/mime"
    45  	"github.com/cs3org/reva/v2/pkg/rhttp/datatx/metrics"
    46  	"github.com/cs3org/reva/v2/pkg/storage/utils/ace"
    47  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
    48  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes"
    49  	"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
    50  	"github.com/cs3org/reva/v2/pkg/utils"
    51  	"github.com/google/uuid"
    52  	"github.com/pkg/errors"
    53  	"github.com/rogpeppe/go-internal/lockedfile"
    54  	"go.opentelemetry.io/otel"
    55  	"go.opentelemetry.io/otel/trace"
    56  )
    57  
    58  var tracer trace.Tracer
    59  
    60  func init() {
    61  	tracer = otel.Tracer("github.com/cs3org/reva/pkg/storage/utils/decomposedfs/node")
    62  }
    63  
    64  // Define keys and values used in the node metadata
    65  const (
    66  	LockdiscoveryKey = "lockdiscovery"
    67  	FavoriteKey      = "http://owncloud.org/ns/favorite"
    68  	ShareTypesKey    = "http://owncloud.org/ns/share-types"
    69  	ChecksumsKey     = "http://owncloud.org/ns/checksums"
    70  	UserShareType    = "0"
    71  	QuotaKey         = "quota"
    72  
    73  	QuotaUnlimited    = "0"
    74  	QuotaUncalculated = "-1"
    75  	QuotaUnknown      = "-2"
    76  
    77  	// TrashIDDelimiter represents the characters used to separate the nodeid and the deletion time.
    78  	TrashIDDelimiter    = ".T."
    79  	RevisionIDDelimiter = ".REV."
    80  
    81  	// RootID defines the root node's ID
    82  	RootID = "root"
    83  
    84  	// ProcessingStatus is the name of the status when processing a file
    85  	ProcessingStatus = "processing:"
    86  )
    87  
    88  type TimeManager interface {
    89  	// OverrideMTime overrides the mtime of the node, either on the node itself or in the given attributes, depending on the implementation
    90  	OverrideMtime(ctx context.Context, n *Node, attrs *Attributes, mtime time.Time) error
    91  
    92  	// MTime returns the mtime of the node
    93  	MTime(ctx context.Context, n *Node) (time.Time, error)
    94  	// SetMTime sets the mtime of the node
    95  	SetMTime(ctx context.Context, n *Node, mtime *time.Time) error
    96  
    97  	// TMTime returns the tmtime of the node
    98  	TMTime(ctx context.Context, n *Node) (time.Time, error)
    99  	// SetTMTime sets the tmtime of the node
   100  	SetTMTime(ctx context.Context, n *Node, tmtime *time.Time) error
   101  
   102  	// CTime returns the ctime of the node
   103  	CTime(ctx context.Context, n *Node) (time.Time, error)
   104  
   105  	// DTime returns the deletion time of the node
   106  	DTime(ctx context.Context, n *Node) (time.Time, error)
   107  	// SetDTime sets the deletion time of the node
   108  	SetDTime(ctx context.Context, n *Node, mtime *time.Time) error
   109  }
   110  
   111  // Tree is used to manage a tree hierarchy
   112  type Tree interface {
   113  	Setup() error
   114  
   115  	GetMD(ctx context.Context, node *Node) (os.FileInfo, error)
   116  	ListFolder(ctx context.Context, node *Node) ([]*Node, error)
   117  	// CreateHome(owner *userpb.UserId) (n *Node, err error)
   118  	CreateDir(ctx context.Context, node *Node) (err error)
   119  	TouchFile(ctx context.Context, node *Node, markprocessing bool, mtime string) error
   120  	// CreateReference(ctx context.Context, node *Node, targetURI *url.URL) error
   121  	Move(ctx context.Context, oldNode *Node, newNode *Node) (err error)
   122  	Delete(ctx context.Context, node *Node) (err error)
   123  	RestoreRecycleItemFunc(ctx context.Context, spaceid, key, trashPath string, target *Node) (*Node, *Node, func() error, error)
   124  	PurgeRecycleItemFunc(ctx context.Context, spaceid, key, purgePath string) (*Node, func() error, error)
   125  
   126  	InitNewNode(ctx context.Context, n *Node, fsize uint64) (metadata.UnlockFunc, error)
   127  
   128  	WriteBlob(node *Node, source string) error
   129  	ReadBlob(node *Node) (io.ReadCloser, error)
   130  	DeleteBlob(node *Node) error
   131  
   132  	BuildSpaceIDIndexEntry(spaceID, nodeID string) string
   133  	ResolveSpaceIDIndexEntry(spaceID, entry string) (string, string, error)
   134  
   135  	Propagate(ctx context.Context, node *Node, sizeDiff int64) (err error)
   136  }
   137  
   138  // PathLookup defines the interface for the lookup component
   139  type PathLookup interface {
   140  	NodeFromSpaceID(ctx context.Context, spaceID string) (n *Node, err error)
   141  	NodeFromResource(ctx context.Context, ref *provider.Reference) (*Node, error)
   142  	NodeFromID(ctx context.Context, id *provider.ResourceId) (n *Node, err error)
   143  
   144  	NodeIDFromParentAndName(ctx context.Context, n *Node, name string) (string, error)
   145  
   146  	GenerateSpaceID(spaceType string, owner *userpb.User) (string, error)
   147  
   148  	InternalRoot() string
   149  	InternalPath(spaceID, nodeID string) string
   150  	Path(ctx context.Context, n *Node, hasPermission PermissionFunc) (path string, err error)
   151  	MetadataBackend() metadata.Backend
   152  	TimeManager() TimeManager
   153  	ReadBlobIDAndSizeAttr(ctx context.Context, path string, attrs Attributes) (string, int64, error)
   154  	TypeFromPath(ctx context.Context, path string) provider.ResourceType
   155  	CopyMetadataWithSourceLock(ctx context.Context, sourcePath, targetPath string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), lockedSource *lockedfile.File, acquireTargetLock bool) (err error)
   156  	CopyMetadata(ctx context.Context, src, target string, filter func(attributeName string, value []byte) (newValue []byte, copy bool), acquireTargetLock bool) (err error)
   157  }
   158  
   159  type IDCacher interface {
   160  	CacheID(ctx context.Context, spaceID, nodeID, val string) error
   161  	GetCachedID(ctx context.Context, spaceID, nodeID string) (string, bool)
   162  }
   163  
   164  // Node represents a node in the tree and provides methods to get a Parent or Child instance
   165  type Node struct {
   166  	SpaceID   string
   167  	ParentID  string
   168  	ID        string
   169  	Name      string
   170  	Blobsize  int64
   171  	BlobID    string
   172  	owner     *userpb.UserId
   173  	Exists    bool
   174  	SpaceRoot *Node
   175  
   176  	lu          PathLookup
   177  	xattrsCache map[string][]byte
   178  	nodeType    *provider.ResourceType
   179  }
   180  
   181  // New returns a new instance of Node
   182  func New(spaceID, id, parentID, name string, blobsize int64, blobID string, t provider.ResourceType, owner *userpb.UserId, lu PathLookup) *Node {
   183  	if blobID == "" {
   184  		blobID = uuid.New().String()
   185  	}
   186  	return &Node{
   187  		SpaceID:  spaceID,
   188  		ID:       id,
   189  		ParentID: parentID,
   190  		Name:     name,
   191  		Blobsize: blobsize,
   192  		owner:    owner,
   193  		lu:       lu,
   194  		BlobID:   blobID,
   195  		nodeType: &t,
   196  	}
   197  }
   198  
   199  func (n *Node) MarshalJSON() ([]byte, error) {
   200  	return json.Marshal(&struct {
   201  		Name     string `json:"name"`
   202  		ID       string `json:"id"`
   203  		SpaceID  string `json:"spaceID"`
   204  		ParentID string `json:"parentID"`
   205  		BlobID   string `json:"blobID"`
   206  		BlobSize int64  `json:"blobSize"`
   207  		Exists   bool   `json:"exists"`
   208  	}{
   209  		Name:     n.Name,
   210  		ID:       n.ID,
   211  		SpaceID:  n.SpaceID,
   212  		ParentID: n.ParentID,
   213  		BlobID:   n.BlobID,
   214  		BlobSize: n.Blobsize,
   215  		Exists:   n.Exists,
   216  	})
   217  }
   218  
   219  // Type returns the node's resource type
   220  func (n *Node) Type(ctx context.Context) provider.ResourceType {
   221  	_, span := tracer.Start(ctx, "Type")
   222  	defer span.End()
   223  	if n.nodeType != nil {
   224  		return *n.nodeType
   225  	}
   226  
   227  	t := provider.ResourceType_RESOURCE_TYPE_INVALID
   228  
   229  	// Try to read from xattrs
   230  	typeAttr, err := n.XattrInt32(ctx, prefixes.TypeAttr)
   231  	if err == nil {
   232  		t = provider.ResourceType(typeAttr)
   233  		n.nodeType = &t
   234  		return t
   235  	}
   236  
   237  	// Fall back to checking on disk
   238  	fi, err := os.Lstat(n.InternalPath())
   239  	if err != nil {
   240  		return t
   241  	}
   242  
   243  	switch {
   244  	case fi.IsDir():
   245  		if _, err = n.Xattr(ctx, prefixes.ReferenceAttr); err == nil {
   246  			t = provider.ResourceType_RESOURCE_TYPE_REFERENCE
   247  		} else {
   248  			t = provider.ResourceType_RESOURCE_TYPE_CONTAINER
   249  		}
   250  	case fi.Mode().IsRegular():
   251  		t = provider.ResourceType_RESOURCE_TYPE_FILE
   252  	case fi.Mode()&os.ModeSymlink != 0:
   253  		t = provider.ResourceType_RESOURCE_TYPE_SYMLINK
   254  		// TODO reference using ext attr on a symlink
   255  		// nodeType = provider.ResourceType_RESOURCE_TYPE_REFERENCE
   256  	}
   257  	n.nodeType = &t
   258  	return t
   259  }
   260  
   261  // SetType sets the type of the node.
   262  func (n *Node) SetType(t provider.ResourceType) {
   263  	n.nodeType = &t
   264  }
   265  
   266  // NodeMetadata writes the Node metadata to disk and allows passing additional attributes
   267  func (n *Node) NodeMetadata(ctx context.Context) Attributes {
   268  	attribs := Attributes{}
   269  	attribs.SetInt64(prefixes.TypeAttr, int64(n.Type(ctx)))
   270  	attribs.SetString(prefixes.ParentidAttr, n.ParentID)
   271  	attribs.SetString(prefixes.NameAttr, n.Name)
   272  	if n.Type(ctx) == provider.ResourceType_RESOURCE_TYPE_FILE {
   273  		attribs.SetString(prefixes.BlobIDAttr, n.BlobID)
   274  		attribs.SetInt64(prefixes.BlobsizeAttr, n.Blobsize)
   275  	}
   276  	return attribs
   277  }
   278  
   279  // SetOwner sets the space owner on the node
   280  func (n *Node) SetOwner(owner *userpb.UserId) {
   281  	n.SpaceRoot.owner = owner
   282  }
   283  
   284  // SpaceOwnerOrManager returns the space owner of the space. If no owner is set
   285  // one of the space managers is returned instead.
   286  func (n *Node) SpaceOwnerOrManager(ctx context.Context) *userpb.UserId {
   287  	owner := n.Owner()
   288  	if owner != nil && owner.Type != userpb.UserType_USER_TYPE_SPACE_OWNER {
   289  		return owner
   290  	}
   291  
   292  	// We don't have an owner set. Find a manager instead.
   293  	grants, err := n.SpaceRoot.ListGrants(ctx)
   294  	if err != nil {
   295  		return nil
   296  	}
   297  	for _, grant := range grants {
   298  		if grant.Permissions.Stat && grant.Permissions.ListContainer && grant.Permissions.InitiateFileDownload {
   299  			return grant.GetGrantee().GetUserId()
   300  		}
   301  	}
   302  
   303  	return nil
   304  }
   305  
   306  // ReadNode creates a new instance from an id and checks if it exists
   307  func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID string, canListDisabledSpace bool, spaceRoot *Node, skipParentCheck bool) (*Node, error) {
   308  	ctx, span := tracer.Start(ctx, "ReadNode")
   309  	defer span.End()
   310  	var err error
   311  
   312  	if spaceRoot == nil {
   313  		// read space root
   314  		spaceRoot = &Node{
   315  			SpaceID: spaceID,
   316  			lu:      lu,
   317  			ID:      spaceID,
   318  		}
   319  		spaceRoot.SpaceRoot = spaceRoot
   320  		spaceRoot.owner, err = spaceRoot.readOwner(ctx)
   321  		switch {
   322  		case metadata.IsNotExist(err):
   323  			return spaceRoot, nil // swallow not found, the node defaults to exists = false
   324  		case err != nil:
   325  			return nil, err
   326  		}
   327  		spaceRoot.Exists = true
   328  
   329  		// lookup name in extended attributes
   330  		spaceRoot.Name, err = spaceRoot.XattrString(ctx, prefixes.NameAttr)
   331  		if err != nil {
   332  			return nil, err
   333  		}
   334  	}
   335  
   336  	// TODO ReadNode should not check permissions
   337  	if !canListDisabledSpace && spaceRoot.IsDisabled(ctx) {
   338  		// no permission = not found
   339  		return nil, errtypes.NotFound(spaceID)
   340  	}
   341  
   342  	// if current user cannot stat the root return not found?
   343  	// no for shares the root might be a different resource
   344  
   345  	// check if this is a space root
   346  	if spaceID == nodeID {
   347  		return spaceRoot, nil
   348  	}
   349  
   350  	// are we reading a revision?
   351  	revisionSuffix := ""
   352  	if strings.Contains(nodeID, RevisionIDDelimiter) {
   353  		// verify revision key format
   354  		kp := strings.SplitN(nodeID, RevisionIDDelimiter, 2)
   355  		if len(kp) == 2 {
   356  			// use the actual node for the metadata lookup
   357  			nodeID = kp[0]
   358  			// remember revision for blob metadata
   359  			revisionSuffix = RevisionIDDelimiter + kp[1]
   360  		}
   361  	}
   362  
   363  	// read node
   364  	n := &Node{
   365  		SpaceID:   spaceID,
   366  		lu:        lu,
   367  		ID:        nodeID,
   368  		SpaceRoot: spaceRoot,
   369  	}
   370  	nodePath := n.InternalPath()
   371  
   372  	// append back revision to nodeid, even when returning a not existing node
   373  	defer func() {
   374  		// when returning errors n is nil
   375  		if n != nil {
   376  			n.ID += revisionSuffix
   377  		}
   378  	}()
   379  
   380  	attrs, err := n.Xattrs(ctx)
   381  	switch {
   382  	case metadata.IsNotExist(err):
   383  		return n, nil // swallow not found, the node defaults to exists = false
   384  	case err != nil:
   385  		return nil, err
   386  	}
   387  	n.Exists = true
   388  
   389  	n.Name = attrs.String(prefixes.NameAttr)
   390  	n.ParentID = attrs.String(prefixes.ParentidAttr)
   391  	if n.ParentID == "" {
   392  		d, _ := os.ReadFile(lu.MetadataBackend().MetadataPath(n.InternalPath()))
   393  		if _, ok := lu.MetadataBackend().(metadata.MessagePackBackend); ok {
   394  			appctx.GetLogger(ctx).Error().Str("path", n.InternalPath()).Str("nodeid", n.ID).Interface("attrs", attrs).Bytes("messagepack", d).Msg("missing parent id")
   395  		}
   396  		return nil, errtypes.InternalError("Missing parent ID on node")
   397  	}
   398  
   399  	if revisionSuffix == "" {
   400  		n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, nodePath, attrs)
   401  		if err != nil {
   402  			return nil, err
   403  		}
   404  	} else {
   405  		n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, nodePath+revisionSuffix, nil)
   406  		if err != nil {
   407  			return nil, err
   408  		}
   409  	}
   410  
   411  	return n, nil
   412  }
   413  
   414  // Child returns the child node with the given name
   415  func (n *Node) Child(ctx context.Context, name string) (*Node, error) {
   416  	ctx, span := tracer.Start(ctx, "Child")
   417  	defer span.End()
   418  
   419  	spaceID := n.SpaceID
   420  	if spaceID == "" && n.ParentID == "root" {
   421  		spaceID = n.ID
   422  	} else if n.SpaceRoot != nil {
   423  		spaceID = n.SpaceRoot.ID
   424  	}
   425  	c := &Node{
   426  		SpaceID:   spaceID,
   427  		lu:        n.lu,
   428  		ParentID:  n.ID,
   429  		Name:      name,
   430  		SpaceRoot: n.SpaceRoot,
   431  	}
   432  
   433  	nodeID, err := n.lu.NodeIDFromParentAndName(ctx, n, name)
   434  	switch {
   435  	case metadata.IsNotExist(err) || metadata.IsNotDir(err):
   436  		return c, nil // if the file does not exist we return a node that has Exists = false
   437  	case err != nil:
   438  		return nil, err
   439  	}
   440  
   441  	c, err = ReadNode(ctx, n.lu, spaceID, nodeID, false, n.SpaceRoot, true)
   442  	if err != nil {
   443  		return nil, errors.Wrap(err, "could not read child node")
   444  	}
   445  
   446  	return c, nil
   447  }
   448  
   449  // ParentWithReader returns the parent node
   450  func (n *Node) ParentWithReader(ctx context.Context, r io.Reader) (*Node, error) {
   451  	_, span := tracer.Start(ctx, "ParentWithReader")
   452  	defer span.End()
   453  	if n.ParentID == "" {
   454  		return nil, fmt.Errorf("decomposedfs: root has no parent")
   455  	}
   456  	p := &Node{
   457  		SpaceID:   n.SpaceID,
   458  		lu:        n.lu,
   459  		ID:        n.ParentID,
   460  		SpaceRoot: n.SpaceRoot,
   461  	}
   462  
   463  	// fill metadata cache using the reader
   464  	attrs, err := p.XattrsWithReader(ctx, r)
   465  	switch {
   466  	case metadata.IsNotExist(err):
   467  		return p, nil // swallow not found, the node defaults to exists = false
   468  	case err != nil:
   469  		return nil, err
   470  	}
   471  	p.Exists = true
   472  
   473  	p.Name = attrs.String(prefixes.NameAttr)
   474  	p.ParentID = attrs.String(prefixes.ParentidAttr)
   475  
   476  	return p, err
   477  }
   478  
   479  // Parent returns the parent node
   480  func (n *Node) Parent(ctx context.Context) (p *Node, err error) {
   481  	return n.ParentWithReader(ctx, nil)
   482  }
   483  
   484  // Owner returns the space owner
   485  func (n *Node) Owner() *userpb.UserId {
   486  	return n.SpaceRoot.owner
   487  }
   488  
   489  // readOwner reads the owner from the extended attributes of the space root
   490  // in case either owner id or owner idp are unset we return an error and an empty owner object
   491  func (n *Node) readOwner(ctx context.Context) (*userpb.UserId, error) {
   492  	owner := &userpb.UserId{}
   493  
   494  	// lookup parent id in extended attributes
   495  	var attr string
   496  	var err error
   497  	// lookup ID in extended attributes
   498  	attr, err = n.SpaceRoot.XattrString(ctx, prefixes.OwnerIDAttr)
   499  	switch {
   500  	case err == nil:
   501  		owner.OpaqueId = attr
   502  	case metadata.IsAttrUnset(err):
   503  		// ignore
   504  	default:
   505  		return nil, err
   506  	}
   507  
   508  	// lookup IDP in extended attributes
   509  	attr, err = n.SpaceRoot.XattrString(ctx, prefixes.OwnerIDPAttr)
   510  	switch {
   511  	case err == nil:
   512  		owner.Idp = attr
   513  	case metadata.IsAttrUnset(err):
   514  		// ignore
   515  	default:
   516  		return nil, err
   517  	}
   518  
   519  	// lookup type in extended attributes
   520  	attr, err = n.SpaceRoot.XattrString(ctx, prefixes.OwnerTypeAttr)
   521  	switch {
   522  	case err == nil:
   523  		owner.Type = utils.UserTypeMap(attr)
   524  	case metadata.IsAttrUnset(err):
   525  		// ignore
   526  	default:
   527  		return nil, err
   528  	}
   529  
   530  	// owner is an optional property
   531  	if owner.Idp == "" && owner.OpaqueId == "" {
   532  		return nil, nil
   533  	}
   534  	return owner, nil
   535  }
   536  
   537  // PermissionSet returns the permission set and an accessDenied flag
   538  // for the current user
   539  // the parent nodes are not taken into account
   540  // accessDenied is separate from the resource permissions
   541  // because we only support full denials
   542  func (n *Node) PermissionSet(ctx context.Context) (*provider.ResourcePermissions, bool) {
   543  	u, ok := ctxpkg.ContextGetUser(ctx)
   544  	if !ok {
   545  		appctx.GetLogger(ctx).Debug().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Msg("no user in context, returning default permissions")
   546  		return NoPermissions(), false
   547  	}
   548  	if utils.UserEqual(u.Id, n.SpaceRoot.Owner()) {
   549  		return OwnerPermissions(), false
   550  	}
   551  	// read the permissions for the current user from the acls of the current node
   552  	if np, accessDenied, err := n.ReadUserPermissions(ctx, u); err == nil {
   553  		return np, accessDenied
   554  	}
   555  	// be defensive, we could have access via another grant
   556  	return NoPermissions(), true
   557  }
   558  
   559  // InternalPath returns the internal path of the Node
   560  func (n *Node) InternalPath() string {
   561  	return n.lu.InternalPath(n.SpaceID, n.ID)
   562  }
   563  
   564  // ParentPath returns the internal path of the parent of the current node
   565  func (n *Node) ParentPath() string {
   566  	return n.lu.InternalPath(n.SpaceID, n.ParentID)
   567  }
   568  
   569  // LockFilePath returns the internal path of the lock file of the node
   570  func (n *Node) LockFilePath() string {
   571  	return n.InternalPath() + ".lock"
   572  }
   573  
   574  // CalculateEtag returns a hash of fileid + tmtime (or mtime)
   575  func CalculateEtag(id string, tmTime time.Time) (string, error) {
   576  	h := md5.New()
   577  	if _, err := io.WriteString(h, id); err != nil {
   578  		return "", err
   579  	}
   580  	/* TODO we could strengthen the etag by adding the blobid, but then all etags would change. we would need a legacy etag check as well
   581  	if _, err := io.WriteString(h, n.BlobID); err != nil {
   582  		return "", err
   583  	}
   584  	*/
   585  	if tb, err := tmTime.UTC().MarshalBinary(); err == nil {
   586  		if _, err := h.Write(tb); err != nil {
   587  			return "", err
   588  		}
   589  	} else {
   590  		return "", err
   591  	}
   592  	return fmt.Sprintf(`"%x"`, h.Sum(nil)), nil
   593  }
   594  
   595  // SetMtimeString sets the mtime and atime of a node to the unixtime parsed from the given string
   596  func (n *Node) SetMtimeString(ctx context.Context, mtime string) error {
   597  	mt, err := utils.MTimeToTime(mtime)
   598  	if err != nil {
   599  		return err
   600  	}
   601  	return n.SetMtime(ctx, &mt)
   602  }
   603  
   604  // SetMTime writes the UTC mtime to the extended attributes or removes the attribute if nil is passed
   605  func (n *Node) SetMtime(ctx context.Context, t *time.Time) (err error) {
   606  	if t == nil {
   607  		return n.RemoveXattr(ctx, prefixes.MTimeAttr, true)
   608  	}
   609  	return n.SetXattrString(ctx, prefixes.MTimeAttr, t.UTC().Format(time.RFC3339Nano))
   610  }
   611  
   612  // SetEtag sets the temporary etag of a node if it differs from the current etag
   613  func (n *Node) SetEtag(ctx context.Context, val string) (err error) {
   614  	sublog := appctx.GetLogger(ctx).With().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Logger()
   615  	var tmTime time.Time
   616  	if tmTime, err = n.GetTMTime(ctx); err != nil {
   617  		return
   618  	}
   619  	var etag string
   620  	if etag, err = CalculateEtag(n.ID, tmTime); err != nil {
   621  		return
   622  	}
   623  
   624  	// sanitize etag
   625  	val = fmt.Sprintf("\"%s\"", strings.Trim(val, "\""))
   626  	if etag == val {
   627  		sublog.Debug().
   628  			Str("etag", val).
   629  			Msg("ignoring request to update identical etag")
   630  		return nil
   631  	}
   632  	// etag is only valid until the calculated etag changes, is part of propagation
   633  	return n.SetXattrString(ctx, prefixes.TmpEtagAttr, val)
   634  }
   635  
   636  // SetFavorite sets the favorite for the current user
   637  // TODO we should not mess with the user here ... the favorites is now a user specific property for a file
   638  // that cannot be mapped to extended attributes without leaking who has marked a file as a favorite
   639  // it is a specific case of a tag, which is user individual as well
   640  // TODO there are different types of tags
   641  // 1. public that are managed by everyone
   642  // 2. private tags that are only visible to the user
   643  // 3. system tags that are only visible to the system
   644  // 4. group tags that are only visible to a group ...
   645  // urgh ... well this can be solved using different namespaces
   646  // 1. public = p:
   647  // 2. private = u:<uid>: for user specific
   648  // 3. system = s: for system
   649  // 4. group = g:<gid>:
   650  // 5. app? = a:<aid>: for apps?
   651  // obviously this only is secure when the u/s/g/a namespaces are not accessible by users in the filesystem
   652  // public tags can be mapped to extended attributes
   653  func (n *Node) SetFavorite(ctx context.Context, uid *userpb.UserId, val string) error {
   654  	// the favorite flag is specific to the user, so we need to incorporate the userid
   655  	fa := fmt.Sprintf("%s:%s:%s@%s", prefixes.FavPrefix, utils.UserTypeToString(uid.GetType()), uid.GetOpaqueId(), uid.GetIdp())
   656  	return n.SetXattrString(ctx, fa, val)
   657  }
   658  
   659  // IsDir returns true if the node is a directory
   660  func (n *Node) IsDir(ctx context.Context) bool {
   661  	attr, _ := n.XattrInt32(ctx, prefixes.TypeAttr)
   662  	return attr == int32(provider.ResourceType_RESOURCE_TYPE_CONTAINER)
   663  }
   664  
   665  // AsResourceInfo return the node as CS3 ResourceInfo
   666  func (n *Node) AsResourceInfo(ctx context.Context, rp *provider.ResourcePermissions, mdKeys, fieldMask []string, returnBasename bool) (ri *provider.ResourceInfo, err error) {
   667  	sublog := appctx.GetLogger(ctx).With().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Logger()
   668  
   669  	var fn string
   670  	nodeType := n.Type(ctx)
   671  
   672  	var target string
   673  	if nodeType == provider.ResourceType_RESOURCE_TYPE_REFERENCE {
   674  		target, _ = n.XattrString(ctx, prefixes.ReferenceAttr)
   675  	}
   676  
   677  	id := &provider.ResourceId{SpaceId: n.SpaceID, OpaqueId: n.ID}
   678  
   679  	switch {
   680  	case n.IsSpaceRoot(ctx):
   681  		fn = "." // space roots do not have a path as they are referencing themselves
   682  	case returnBasename:
   683  		fn = n.Name
   684  	default:
   685  		fn, err = n.lu.Path(ctx, n, NoCheck)
   686  		if err != nil {
   687  			return nil, err
   688  		}
   689  	}
   690  
   691  	ri = &provider.ResourceInfo{
   692  		Id:            id,
   693  		Path:          fn,
   694  		Type:          nodeType,
   695  		MimeType:      mime.Detect(nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER, fn),
   696  		Size:          uint64(n.Blobsize),
   697  		Target:        target,
   698  		PermissionSet: rp,
   699  		Owner:         n.Owner(),
   700  		ParentId: &provider.ResourceId{
   701  			SpaceId:  n.SpaceID,
   702  			OpaqueId: n.ParentID,
   703  		},
   704  		Name: n.Name,
   705  	}
   706  
   707  	if n.IsProcessing(ctx) {
   708  		ri.Opaque = utils.AppendPlainToOpaque(ri.Opaque, "status", "processing")
   709  	}
   710  
   711  	if nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
   712  		ts, err := n.GetTreeSize(ctx)
   713  		if err == nil {
   714  			ri.Size = ts
   715  		} else {
   716  			ri.Size = 0 // make dirs always return 0 if it is unknown
   717  			sublog.Debug().Err(err).Msg("could not read treesize")
   718  		}
   719  	}
   720  
   721  	// TODO make etag of files use fileid and checksum
   722  
   723  	var tmTime time.Time
   724  	if tmTime, err = n.GetTMTime(ctx); err != nil {
   725  		sublog.Debug().Err(err).Msg("could not get tmtime")
   726  	}
   727  
   728  	// use temporary etag if it is set
   729  	if b, err := n.XattrString(ctx, prefixes.TmpEtagAttr); err == nil && b != "" {
   730  		ri.Etag = fmt.Sprintf(`"%x"`, b)
   731  	} else if ri.Etag, err = CalculateEtag(n.ID, tmTime); err != nil {
   732  		sublog.Debug().Err(err).Msg("could not calculate etag")
   733  	}
   734  
   735  	// mtime uses tmtime if present
   736  	// TODO expose mtime and tmtime separately?
   737  	un := tmTime.UnixNano()
   738  	ri.Mtime = &types.Timestamp{
   739  		Seconds: uint64(un / 1000000000),
   740  		Nanos:   uint32(un % 1000000000),
   741  	}
   742  
   743  	mdKeysMap := make(map[string]struct{})
   744  	for _, k := range mdKeys {
   745  		mdKeysMap[k] = struct{}{}
   746  	}
   747  
   748  	var returnAllMetadata bool
   749  	if _, ok := mdKeysMap["*"]; len(mdKeys) == 0 || ok {
   750  		returnAllMetadata = true
   751  	}
   752  
   753  	metadata := map[string]string{}
   754  
   755  	fieldMaskKeysMap := make(map[string]struct{})
   756  	for _, k := range fieldMask {
   757  		fieldMaskKeysMap[k] = struct{}{}
   758  	}
   759  
   760  	var returnAllFields bool
   761  	if _, ok := fieldMaskKeysMap["*"]; len(fieldMask) == 0 || ok {
   762  		returnAllFields = true
   763  	}
   764  
   765  	// read favorite flag for the current user
   766  	if _, ok := mdKeysMap[FavoriteKey]; returnAllMetadata || ok {
   767  		favorite := ""
   768  		if u, ok := ctxpkg.ContextGetUser(ctx); ok {
   769  			// the favorite flag is specific to the user, so we need to incorporate the userid
   770  			if uid := u.GetId(); uid != nil {
   771  				fa := fmt.Sprintf("%s:%s:%s@%s", prefixes.FavPrefix, utils.UserTypeToString(uid.GetType()), uid.GetOpaqueId(), uid.GetIdp())
   772  				if val, err := n.XattrString(ctx, fa); err == nil {
   773  					sublog.Debug().
   774  						Str("favorite", fa).
   775  						Msg("found favorite flag")
   776  					favorite = val
   777  				}
   778  			} else {
   779  				sublog.Error().Err(errtypes.UserRequired("userrequired")).Msg("user has no id")
   780  			}
   781  		} else {
   782  			sublog.Error().Err(errtypes.UserRequired("userrequired")).Msg("error getting user from ctx")
   783  		}
   784  		metadata[FavoriteKey] = favorite
   785  	}
   786  	// read locks
   787  	// FIXME move to fieldmask
   788  	if _, ok := mdKeysMap[LockdiscoveryKey]; returnAllMetadata || ok {
   789  		if n.hasLocks(ctx) {
   790  			err = readLocksIntoOpaque(ctx, n, ri)
   791  			if err != nil {
   792  				sublog.Debug().Err(errtypes.InternalError("lockfail"))
   793  			}
   794  		}
   795  	}
   796  
   797  	// share indicator
   798  	if _, ok := fieldMaskKeysMap["share-types"]; returnAllFields || ok {
   799  		granteeTypes := n.getGranteeTypes(ctx)
   800  		if len(granteeTypes) > 0 {
   801  			// TODO add optional property to CS3 ResourceInfo to transport grants?
   802  			var s strings.Builder
   803  			first := true
   804  			for _, t := range granteeTypes {
   805  				if !first {
   806  					s.WriteString(",")
   807  				} else {
   808  					first = false
   809  				}
   810  				s.WriteString(strconv.Itoa(int(t)))
   811  			}
   812  			ri.Opaque = utils.AppendPlainToOpaque(ri.Opaque, "share-types", s.String())
   813  		}
   814  	}
   815  
   816  	// checksums
   817  	// FIXME move to fieldmask
   818  	if _, ok := mdKeysMap[ChecksumsKey]; (nodeType == provider.ResourceType_RESOURCE_TYPE_FILE) && (returnAllMetadata || ok) {
   819  		// TODO which checksum was requested? sha1 adler32 or md5? for now hardcode sha1?
   820  		// TODO make ResourceInfo carry multiple checksums
   821  		n.readChecksumIntoResourceChecksum(ctx, storageprovider.XSSHA1, ri)
   822  		n.readChecksumIntoOpaque(ctx, storageprovider.XSMD5, ri)
   823  		n.readChecksumIntoOpaque(ctx, storageprovider.XSAdler32, ri)
   824  	}
   825  	// quota
   826  	// FIXME move to fieldmask
   827  	if _, ok := mdKeysMap[QuotaKey]; (nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER) && returnAllMetadata || ok {
   828  		if n.SpaceRoot != nil && n.SpaceRoot.InternalPath() != "" {
   829  			n.SpaceRoot.readQuotaIntoOpaque(ctx, ri)
   830  		}
   831  	}
   832  
   833  	// only read the requested metadata attributes
   834  	attrs, err := n.Xattrs(ctx)
   835  	if err != nil {
   836  		sublog.Error().Err(err).Msg("error getting list of extended attributes")
   837  	} else {
   838  		for key, value := range attrs {
   839  			// filter out non-custom properties
   840  			if !strings.HasPrefix(key, prefixes.MetadataPrefix) {
   841  				continue
   842  			}
   843  			// only read when key was requested
   844  			k := key[len(prefixes.MetadataPrefix):]
   845  			if _, ok := mdKeysMap[k]; returnAllMetadata || ok {
   846  				metadata[k] = string(value)
   847  			}
   848  
   849  		}
   850  	}
   851  	ri.ArbitraryMetadata = &provider.ArbitraryMetadata{
   852  		Metadata: metadata,
   853  	}
   854  
   855  	// add virusscan information
   856  	if scanned, _, date := n.ScanData(ctx); scanned {
   857  		ri.Opaque = utils.AppendPlainToOpaque(ri.Opaque, "scantime", date.Format(time.RFC3339Nano))
   858  	}
   859  
   860  	sublog.Debug().
   861  		Interface("ri", ri).
   862  		Msg("AsResourceInfo")
   863  
   864  	return ri, nil
   865  }
   866  
   867  func (n *Node) readChecksumIntoResourceChecksum(ctx context.Context, algo string, ri *provider.ResourceInfo) {
   868  	v, err := n.Xattr(ctx, prefixes.ChecksumPrefix+algo)
   869  	switch {
   870  	case err == nil:
   871  		ri.Checksum = &provider.ResourceChecksum{
   872  			Type: storageprovider.PKG2GRPCXS(algo),
   873  			Sum:  hex.EncodeToString(v),
   874  		}
   875  	case metadata.IsAttrUnset(err):
   876  		appctx.GetLogger(ctx).Debug().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Str("algorithm", algo).Msg("checksum not set")
   877  	default:
   878  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Str("algorithm", algo).Msg("could not read checksum")
   879  	}
   880  }
   881  
   882  func (n *Node) readChecksumIntoOpaque(ctx context.Context, algo string, ri *provider.ResourceInfo) {
   883  	v, err := n.Xattr(ctx, prefixes.ChecksumPrefix+algo)
   884  	switch {
   885  	case err == nil:
   886  		if ri.Opaque == nil {
   887  			ri.Opaque = &types.Opaque{
   888  				Map: map[string]*types.OpaqueEntry{},
   889  			}
   890  		}
   891  		ri.Opaque.Map[algo] = &types.OpaqueEntry{
   892  			Decoder: "plain",
   893  			Value:   []byte(hex.EncodeToString(v)),
   894  		}
   895  	case metadata.IsAttrUnset(err):
   896  		appctx.GetLogger(ctx).Debug().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Str("algorithm", algo).Msg("checksum not set")
   897  	default:
   898  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Str("algorithm", algo).Msg("could not read checksum")
   899  	}
   900  }
   901  
   902  // quota is always stored on the root node
   903  func (n *Node) readQuotaIntoOpaque(ctx context.Context, ri *provider.ResourceInfo) {
   904  	v, err := n.XattrString(ctx, prefixes.QuotaAttr)
   905  	switch {
   906  	case err == nil:
   907  		// make sure we have a proper signed int
   908  		// we use the same magic numbers to indicate:
   909  		// -1 = uncalculated
   910  		// -2 = unknown
   911  		// -3 = unlimited
   912  		if _, err := strconv.ParseInt(v, 10, 64); err == nil {
   913  			if ri.Opaque == nil {
   914  				ri.Opaque = &types.Opaque{
   915  					Map: map[string]*types.OpaqueEntry{},
   916  				}
   917  			}
   918  			ri.Opaque.Map[QuotaKey] = &types.OpaqueEntry{
   919  				Decoder: "plain",
   920  				Value:   []byte(v),
   921  			}
   922  		} else {
   923  			appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Str("quota", v).Msg("malformed quota")
   924  		}
   925  	case metadata.IsAttrUnset(err):
   926  		appctx.GetLogger(ctx).Debug().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Msg("quota not set")
   927  	default:
   928  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("nodepath", n.InternalPath()).Msg("could not read quota")
   929  	}
   930  }
   931  
   932  // HasPropagation checks if the propagation attribute exists and is set to "1"
   933  func (n *Node) HasPropagation(ctx context.Context) (propagation bool) {
   934  	if b, err := n.XattrString(ctx, prefixes.PropagationAttr); err == nil {
   935  		return b == "1"
   936  	}
   937  	return false
   938  }
   939  
   940  // IsDisabled returns true when the node has a dmtime attribute set
   941  // only used to check if a space is disabled
   942  // FIXME confusing with the trash logic
   943  func (n *Node) IsDisabled(ctx context.Context) bool {
   944  	if _, err := n.GetDTime(ctx); err == nil {
   945  		return true
   946  	}
   947  	return false
   948  }
   949  
   950  // GetTreeSize reads the treesize from the extended attributes
   951  func (n *Node) GetTreeSize(ctx context.Context) (treesize uint64, err error) {
   952  	ctx, span := tracer.Start(ctx, "GetTreeSize")
   953  	defer span.End()
   954  	s, err := n.XattrUint64(ctx, prefixes.TreesizeAttr)
   955  	if err != nil {
   956  		return 0, err
   957  	}
   958  	return s, nil
   959  }
   960  
   961  // SetTreeSize writes the treesize to the extended attributes
   962  func (n *Node) SetTreeSize(ctx context.Context, ts uint64) (err error) {
   963  	return n.SetXattrString(ctx, prefixes.TreesizeAttr, strconv.FormatUint(ts, 10))
   964  }
   965  
   966  // GetBlobSize reads the blobsize from the extended attributes
   967  func (n *Node) GetBlobSize(ctx context.Context) (treesize uint64, err error) {
   968  	s, err := n.XattrInt64(ctx, prefixes.BlobsizeAttr)
   969  	if err != nil {
   970  		return 0, err
   971  	}
   972  	return uint64(s), nil
   973  }
   974  
   975  // SetChecksum writes the checksum with the given checksum type to the extended attributes
   976  func (n *Node) SetChecksum(ctx context.Context, csType string, h hash.Hash) (err error) {
   977  	return n.SetXattr(ctx, prefixes.ChecksumPrefix+csType, h.Sum(nil))
   978  }
   979  
   980  // UnsetTempEtag removes the temporary etag attribute
   981  func (n *Node) UnsetTempEtag(ctx context.Context) (err error) {
   982  	return n.RemoveXattr(ctx, prefixes.TmpEtagAttr, true)
   983  }
   984  
   985  func isGrantExpired(g *provider.Grant) bool {
   986  	if g.Expiration == nil {
   987  		return false
   988  	}
   989  	return time.Now().After(time.Unix(int64(g.Expiration.Seconds), int64(g.Expiration.Nanos)))
   990  }
   991  
   992  // ReadUserPermissions will assemble the permissions for the current user on the given node without parent nodes
   993  // we indicate if the access was denied by setting a grant with no permissions
   994  func (n *Node) ReadUserPermissions(ctx context.Context, u *userpb.User) (ap *provider.ResourcePermissions, accessDenied bool, err error) {
   995  	// check if the current user is the owner
   996  	if utils.UserEqual(u.Id, n.Owner()) {
   997  		appctx.GetLogger(ctx).Debug().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Msg("user is owner, returning owner permissions")
   998  		return OwnerPermissions(), false, nil
   999  	}
  1000  
  1001  	ap = &provider.ResourcePermissions{}
  1002  
  1003  	// for an efficient group lookup convert the list of groups to a map
  1004  	// groups are just strings ... groupnames ... or group ids ??? AAARGH !!!
  1005  	groupsMap := make(map[string]bool, len(u.Groups))
  1006  	for i := range u.Groups {
  1007  		groupsMap[u.Groups[i]] = true
  1008  	}
  1009  
  1010  	var g *provider.Grant
  1011  
  1012  	// we read all grantees from the node
  1013  	var grantees []string
  1014  	if grantees, err = n.ListGrantees(ctx); err != nil {
  1015  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Msg("error listing grantees")
  1016  		return NoPermissions(), true, err
  1017  	}
  1018  
  1019  	// instead of making n getxattr syscalls we are going to list the acls and filter them here
  1020  	// we have two options here:
  1021  	// 1. we can start iterating over the acls / grants on the node or
  1022  	// 2. we can iterate over the number of groups
  1023  	// The current implementation tries to be defensive for cases where users have hundreds or thousands of groups, so we iterate over the existing acls.
  1024  	userace := prefixes.GrantPrefix + ace.UserAce(u.Id)
  1025  	userFound := false
  1026  	for i := range grantees {
  1027  		switch {
  1028  		// we only need to find the user once
  1029  		case !userFound && grantees[i] == userace:
  1030  			g, err = n.ReadGrant(ctx, grantees[i])
  1031  		case strings.HasPrefix(grantees[i], prefixes.GrantGroupAcePrefix): // only check group grantees
  1032  			gr := strings.TrimPrefix(grantees[i], prefixes.GrantGroupAcePrefix)
  1033  			if groupsMap[gr] {
  1034  				g, err = n.ReadGrant(ctx, grantees[i])
  1035  			} else {
  1036  				// no need to check attribute
  1037  				continue
  1038  			}
  1039  		default:
  1040  			// no need to check attribute
  1041  			continue
  1042  		}
  1043  
  1044  		if isGrantExpired(g) {
  1045  			continue
  1046  		}
  1047  
  1048  		switch {
  1049  		case err == nil:
  1050  			// If all permissions are set to false we have a deny grant
  1051  			if grants.PermissionsEqual(g.Permissions, &provider.ResourcePermissions{}) {
  1052  				return NoPermissions(), true, nil
  1053  			}
  1054  			AddPermissions(ap, g.GetPermissions())
  1055  		case metadata.IsAttrUnset(err):
  1056  			appctx.GetLogger(ctx).Error().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("grant", grantees[i]).Interface("grantees", grantees).Msg("grant vanished from node after listing")
  1057  			// continue with next segment
  1058  		default:
  1059  			appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("grant", grantees[i]).Msg("error reading permissions")
  1060  			// continue with next segment
  1061  		}
  1062  	}
  1063  
  1064  	appctx.GetLogger(ctx).Debug().Interface("permissions", ap).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Interface("user", u).Msg("returning aggregated permissions")
  1065  	return ap, false, nil
  1066  }
  1067  
  1068  // IsDenied checks if the node was denied to that user
  1069  func (n *Node) IsDenied(ctx context.Context) bool {
  1070  	gs, err := n.ListGrants(ctx)
  1071  	if err != nil {
  1072  		// be paranoid, resource is denied
  1073  		return true
  1074  	}
  1075  
  1076  	u := ctxpkg.ContextMustGetUser(ctx)
  1077  	isExecutant := func(g *provider.Grantee) bool {
  1078  		switch g.GetType() {
  1079  		case provider.GranteeType_GRANTEE_TYPE_USER:
  1080  			return g.GetUserId().GetOpaqueId() == u.GetId().GetOpaqueId()
  1081  		case provider.GranteeType_GRANTEE_TYPE_GROUP:
  1082  			// check gid
  1083  			gid := g.GetGroupId().GetOpaqueId()
  1084  			for _, group := range u.Groups {
  1085  				if gid == group {
  1086  					return true
  1087  				}
  1088  
  1089  			}
  1090  			return false
  1091  		default:
  1092  			return false
  1093  		}
  1094  
  1095  	}
  1096  
  1097  	for _, g := range gs {
  1098  		if !isExecutant(g.Grantee) {
  1099  			continue
  1100  		}
  1101  
  1102  		if grants.PermissionsEqual(g.Permissions, &provider.ResourcePermissions{}) {
  1103  			// resource is denied
  1104  			return true
  1105  		}
  1106  	}
  1107  
  1108  	// no deny grants
  1109  	return false
  1110  }
  1111  
  1112  // ListGrantees lists the grantees of the current node
  1113  // We don't want to wast time and memory by creating grantee objects.
  1114  // The function will return a list of opaque strings that can be used to make a ReadGrant call
  1115  func (n *Node) ListGrantees(ctx context.Context) (grantees []string, err error) {
  1116  	attrs, err := n.Xattrs(ctx)
  1117  	if err != nil {
  1118  		appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Msg("error listing attributes")
  1119  		return nil, err
  1120  	}
  1121  	for name := range attrs {
  1122  		if strings.HasPrefix(name, prefixes.GrantPrefix) {
  1123  			grantees = append(grantees, name)
  1124  		}
  1125  	}
  1126  	return
  1127  }
  1128  
  1129  // ReadGrant reads a CS3 grant
  1130  func (n *Node) ReadGrant(ctx context.Context, grantee string) (g *provider.Grant, err error) {
  1131  	xattr, err := n.Xattr(ctx, grantee)
  1132  	if err != nil {
  1133  		return nil, err
  1134  	}
  1135  	var e *ace.ACE
  1136  	if e, err = ace.Unmarshal(strings.TrimPrefix(grantee, prefixes.GrantPrefix), xattr); err != nil {
  1137  		return nil, err
  1138  	}
  1139  	return e.Grant(), nil
  1140  }
  1141  
  1142  // ReadGrant reads a CS3 grant
  1143  func (n *Node) DeleteGrant(ctx context.Context, g *provider.Grant, acquireLock bool) (err error) {
  1144  
  1145  	var attr string
  1146  	if g.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP {
  1147  		attr = prefixes.GrantGroupAcePrefix + g.Grantee.GetGroupId().OpaqueId
  1148  	} else {
  1149  		attr = prefixes.GrantUserAcePrefix + g.Grantee.GetUserId().OpaqueId
  1150  	}
  1151  
  1152  	if err = n.RemoveXattr(ctx, attr, acquireLock); err != nil {
  1153  		return err
  1154  	}
  1155  
  1156  	return nil
  1157  }
  1158  
  1159  // Purge removes a node from disk. It does not move it to the trash
  1160  func (n *Node) Purge(ctx context.Context) error {
  1161  	// remove node
  1162  	if err := utils.RemoveItem(n.InternalPath()); err != nil {
  1163  		return err
  1164  	}
  1165  
  1166  	// remove child entry in parent
  1167  	src := filepath.Join(n.ParentPath(), n.Name)
  1168  	return os.Remove(src)
  1169  }
  1170  
  1171  // ListGrants lists all grants of the current node.
  1172  func (n *Node) ListGrants(ctx context.Context) ([]*provider.Grant, error) {
  1173  	grantees, err := n.ListGrantees(ctx)
  1174  	if err != nil {
  1175  		return nil, err
  1176  	}
  1177  
  1178  	grants := make([]*provider.Grant, 0, len(grantees))
  1179  	for _, g := range grantees {
  1180  		grant, err := n.ReadGrant(ctx, g)
  1181  		if err != nil {
  1182  			appctx.GetLogger(ctx).
  1183  				Error().
  1184  				Err(err).
  1185  				Str("spaceid", n.SpaceID).
  1186  				Str("nodeid", n.ID).
  1187  				Str("grantee", g).
  1188  				Msg("error reading grant")
  1189  			continue
  1190  		}
  1191  		grants = append(grants, grant)
  1192  	}
  1193  	return grants, nil
  1194  }
  1195  
  1196  func (n *Node) getGranteeTypes(ctx context.Context) []provider.GranteeType {
  1197  	types := []provider.GranteeType{}
  1198  	if g, err := n.ListGrantees(ctx); err == nil {
  1199  		hasUserShares, hasGroupShares := false, false
  1200  		for i := range g {
  1201  			switch {
  1202  			case !hasUserShares && strings.HasPrefix(g[i], prefixes.GrantUserAcePrefix):
  1203  				hasUserShares = true
  1204  			case !hasGroupShares && strings.HasPrefix(g[i], prefixes.GrantGroupAcePrefix):
  1205  				hasGroupShares = true
  1206  			case hasUserShares && hasGroupShares:
  1207  				break
  1208  			}
  1209  		}
  1210  		if hasUserShares {
  1211  			types = append(types, provider.GranteeType_GRANTEE_TYPE_USER)
  1212  		}
  1213  		if hasGroupShares {
  1214  			types = append(types, provider.GranteeType_GRANTEE_TYPE_GROUP)
  1215  		}
  1216  	}
  1217  	return types
  1218  }
  1219  
  1220  // FindStorageSpaceRoot calls n.Parent() and climbs the tree
  1221  // until it finds the space root node and adds it to the node
  1222  func (n *Node) FindStorageSpaceRoot(ctx context.Context) error {
  1223  	if n.SpaceRoot != nil {
  1224  		return nil
  1225  	}
  1226  	var err error
  1227  	// remember the node we ask for and use parent to climb the tree
  1228  	parent := n
  1229  	for {
  1230  		if parent.IsSpaceRoot(ctx) {
  1231  			n.SpaceRoot = parent
  1232  			break
  1233  		}
  1234  		if parent, err = parent.Parent(ctx); err != nil {
  1235  			return err
  1236  		}
  1237  	}
  1238  	return nil
  1239  }
  1240  
  1241  // UnmarkProcessing removes the processing flag from the node
  1242  func (n *Node) UnmarkProcessing(ctx context.Context, uploadID string) error {
  1243  	// we currently have to decrease the counter for every processing run to match the incrases
  1244  	metrics.UploadProcessing.Sub(1)
  1245  
  1246  	v, _ := n.XattrString(ctx, prefixes.StatusPrefix)
  1247  	if v != ProcessingStatus+uploadID {
  1248  		// file started another postprocessing later - do not remove
  1249  		return nil
  1250  	}
  1251  	return n.RemoveXattr(ctx, prefixes.StatusPrefix, true)
  1252  }
  1253  
  1254  // IsProcessing returns true if the node is currently being processed
  1255  func (n *Node) IsProcessing(ctx context.Context) bool {
  1256  	v, err := n.XattrString(ctx, prefixes.StatusPrefix)
  1257  	return err == nil && strings.HasPrefix(v, ProcessingStatus)
  1258  }
  1259  
  1260  // ProcessingID returns the latest upload session id
  1261  func (n *Node) ProcessingID(ctx context.Context) (string, error) {
  1262  	v, err := n.XattrString(ctx, prefixes.StatusPrefix)
  1263  	return strings.TrimPrefix(v, ProcessingStatus), err
  1264  }
  1265  
  1266  // IsSpaceRoot checks if the node is a space root
  1267  func (n *Node) IsSpaceRoot(ctx context.Context) bool {
  1268  	return n.ID == n.SpaceID
  1269  }
  1270  
  1271  // SetScanData sets the virus scan info to the node
  1272  func (n *Node) SetScanData(ctx context.Context, info string, date time.Time) error {
  1273  	attribs := Attributes{}
  1274  	attribs.SetString(prefixes.ScanStatusPrefix, info)
  1275  	attribs.SetString(prefixes.ScanDatePrefix, date.Format(time.RFC3339Nano))
  1276  	return n.SetXattrsWithContext(ctx, attribs, true)
  1277  }
  1278  
  1279  // ScanData returns scanning information of the node
  1280  func (n *Node) ScanData(ctx context.Context) (scanned bool, virus string, scantime time.Time) {
  1281  	ti, _ := n.XattrString(ctx, prefixes.ScanDatePrefix)
  1282  	if ti == "" {
  1283  		return // not scanned yet
  1284  	}
  1285  
  1286  	t, err := time.Parse(time.RFC3339Nano, ti)
  1287  	if err != nil {
  1288  		return
  1289  	}
  1290  
  1291  	i, err := n.XattrString(ctx, prefixes.ScanStatusPrefix)
  1292  	if err != nil {
  1293  		return
  1294  	}
  1295  
  1296  	return true, i, t
  1297  }
  1298  
  1299  // CheckQuota checks if both disk space and available quota are sufficient
  1300  // Overwrite must be set to true if the new file replaces the old file e.g.
  1301  // when creating a new file version. In such a case the function will
  1302  // reduce the used bytes by the old file size and then add the new size.
  1303  // If overwrite is false oldSize will be ignored.
  1304  var CheckQuota = func(ctx context.Context, spaceRoot *Node, overwrite bool, oldSize, newSize uint64) (quotaSufficient bool, err error) {
  1305  	used, _ := spaceRoot.GetTreeSize(ctx)
  1306  	if !enoughDiskSpace(spaceRoot.InternalPath(), newSize) {
  1307  		return false, errtypes.InsufficientStorage("disk full")
  1308  	}
  1309  	quotaByteStr, _ := spaceRoot.XattrString(ctx, prefixes.QuotaAttr)
  1310  	switch quotaByteStr {
  1311  	case "":
  1312  		// if quota is not set, it means unlimited
  1313  		return true, nil
  1314  	case QuotaUnlimited:
  1315  		return true, nil
  1316  	case QuotaUncalculated:
  1317  		// treat it as unlimited
  1318  		return true, nil
  1319  	case QuotaUnknown:
  1320  		// treat it as unlimited
  1321  		return true, nil
  1322  	}
  1323  	quotaByte, _ := strconv.ParseUint(quotaByteStr, 10, 64)
  1324  	if overwrite {
  1325  		if quotaByte < used-oldSize+newSize {
  1326  			return false, errtypes.InsufficientStorage("quota exceeded")
  1327  		}
  1328  		// if total is smaller than used, total-used could overflow and be bigger than fileSize
  1329  	} else if newSize > quotaByte-used || quotaByte < used {
  1330  		return false, errtypes.InsufficientStorage("quota exceeded")
  1331  	}
  1332  	return true, nil
  1333  }
  1334  
  1335  func enoughDiskSpace(path string, fileSize uint64) bool {
  1336  	avalB, err := GetAvailableSize(path)
  1337  	if err != nil {
  1338  		return false
  1339  	}
  1340  	return avalB > fileSize
  1341  }
  1342  
  1343  // CalculateChecksums calculates the sha1, md5 and adler32 checksums of a file
  1344  func CalculateChecksums(ctx context.Context, path string) (hash.Hash, hash.Hash, hash.Hash32, error) {
  1345  	sha1h := sha1.New()
  1346  	md5h := md5.New()
  1347  	adler32h := adler32.New()
  1348  
  1349  	_, subspan := tracer.Start(ctx, "os.Open")
  1350  	f, err := os.Open(path)
  1351  	subspan.End()
  1352  	if err != nil {
  1353  		return nil, nil, nil, err
  1354  	}
  1355  	defer f.Close()
  1356  
  1357  	r1 := io.TeeReader(f, sha1h)
  1358  	r2 := io.TeeReader(r1, md5h)
  1359  
  1360  	_, subspan = tracer.Start(ctx, "io.Copy")
  1361  	_, err = io.Copy(adler32h, r2)
  1362  	subspan.End()
  1363  	if err != nil {
  1364  		return nil, nil, nil, err
  1365  	}
  1366  
  1367  	return sha1h, md5h, adler32h, nil
  1368  }
  1369  
  1370  // GetMTime reads the mtime from the extended attributes
  1371  func (n *Node) GetMTime(ctx context.Context) (time.Time, error) {
  1372  	return n.lu.TimeManager().MTime(ctx, n)
  1373  }
  1374  
  1375  // GetTMTime reads the tmtime from the extended attributes
  1376  func (n *Node) GetTMTime(ctx context.Context) (time.Time, error) {
  1377  	return n.lu.TimeManager().TMTime(ctx, n)
  1378  }
  1379  
  1380  // SetTMTime writes the UTC tmtime to the extended attributes or removes the attribute if nil is passed
  1381  func (n *Node) SetTMTime(ctx context.Context, t *time.Time) (err error) {
  1382  	return n.lu.TimeManager().SetTMTime(ctx, n, t)
  1383  }
  1384  
  1385  // GetDTime reads the dmtime from the extended attributes
  1386  func (n *Node) GetDTime(ctx context.Context) (time.Time, error) {
  1387  	return n.lu.TimeManager().DTime(ctx, n)
  1388  }
  1389  
  1390  // SetDTime writes the UTC dmtime to the extended attributes or removes the attribute if nil is passed
  1391  func (n *Node) SetDTime(ctx context.Context, t *time.Time) (err error) {
  1392  	return n.lu.TimeManager().SetDTime(ctx, n, t)
  1393  }