github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/cephfs/user.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  //go:build ceph
    20  // +build ceph
    21  
    22  package cephfs
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"path/filepath"
    28  	"strconv"
    29  	"strings"
    30  	"syscall"
    31  
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  
    34  	cephfs2 "github.com/ceph/go-ceph/cephfs"
    35  	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    36  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    37  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    38  	ctx2 "github.com/cs3org/reva/v2/pkg/ctx"
    39  	"github.com/cs3org/reva/v2/pkg/mime"
    40  	"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
    41  	"github.com/pkg/errors"
    42  )
    43  
    44  type callBack func(cb *cacheVal)
    45  
    46  // User custom type to add functionality to current struct
    47  type User struct {
    48  	*userv1beta1.User
    49  	fs   *cephfs
    50  	ctx  context.Context
    51  	home string
    52  }
    53  
    54  func (fs *cephfs) makeUser(ctx context.Context) *User {
    55  	u := ctx2.ContextMustGetUser(ctx)
    56  	home := filepath.Join(fs.conf.Root, templates.WithUser(u, fs.conf.UserLayout))
    57  	return &User{u, fs, ctx, home}
    58  }
    59  
    60  func (user *User) absPath(path string) string {
    61  	//shares will always be absolute to avoid prepending the user path to the path of the file's owner
    62  	if !filepath.IsAbs(path) {
    63  		path = filepath.Join(user.home, path)
    64  	}
    65  
    66  	return path
    67  }
    68  
    69  func (user *User) op(cb callBack) {
    70  	conn := user.fs.conn
    71  	if err := conn.lock.Acquire(conn.ctx, 1); err != nil {
    72  		return
    73  	}
    74  	defer conn.lock.Release(1)
    75  
    76  	val, found := conn.cache.Get(user.Id.OpaqueId)
    77  	if !found {
    78  		cvalue := newConn(user)
    79  		if cvalue != nil {
    80  			conn.cache.Set(user.Id.OpaqueId, cvalue, 1)
    81  		} else {
    82  			return
    83  		}
    84  		cb(cvalue)
    85  		return
    86  	}
    87  
    88  	cb(val.(*cacheVal))
    89  }
    90  
    91  func (user *User) fileAsResourceInfo(cv *cacheVal, path string, stat *cephfs2.CephStatx, mdKeys []string) (ri *provider.ResourceInfo, err error) {
    92  	var (
    93  		_type  provider.ResourceType
    94  		target string
    95  		size   uint64
    96  		buf    []byte
    97  	)
    98  
    99  	switch int(stat.Mode) & syscall.S_IFMT {
   100  	case syscall.S_IFDIR:
   101  		_type = provider.ResourceType_RESOURCE_TYPE_CONTAINER
   102  		if buf, err = cv.mount.GetXattr(path, "ceph.dir.rbytes"); err == nil {
   103  			size, err = strconv.ParseUint(string(buf), 10, 64)
   104  		}
   105  	case syscall.S_IFLNK:
   106  		_type = provider.ResourceType_RESOURCE_TYPE_SYMLINK
   107  		target, err = cv.mount.Readlink(path)
   108  	case syscall.S_IFREG:
   109  		_type = provider.ResourceType_RESOURCE_TYPE_FILE
   110  		size = stat.Size
   111  	default:
   112  		return nil, errors.New("cephfs: unknown entry type")
   113  	}
   114  
   115  	if err != nil {
   116  		return
   117  	}
   118  
   119  	var xattrs []string
   120  	keys := make(map[string]bool, len(mdKeys))
   121  	for _, key := range mdKeys {
   122  		keys[key] = true
   123  	}
   124  	if keys["*"] || len(keys) == 0 {
   125  		mdKeys = []string{}
   126  		keys = map[string]bool{}
   127  	}
   128  	mx := make(map[string]string)
   129  	if xattrs, err = cv.mount.ListXattr(path); err == nil {
   130  		for _, xattr := range xattrs {
   131  			if len(mdKeys) == 0 || keys[xattr] {
   132  				if buf, err := cv.mount.GetXattr(path, xattr); err == nil {
   133  					mx[xattr] = string(buf)
   134  				}
   135  			}
   136  		}
   137  	}
   138  
   139  	//TODO(tmourati): Add entry id logic here
   140  
   141  	var etag string
   142  	if isDir(_type) {
   143  		rctime, _ := cv.mount.GetXattr(path, "ceph.dir.rctime")
   144  		etag = fmt.Sprint(stat.Inode) + ":" + string(rctime)
   145  	} else {
   146  		etag = fmt.Sprint(stat.Inode) + ":" + strconv.FormatInt(stat.Ctime.Sec, 10)
   147  	}
   148  
   149  	mtime := &typesv1beta1.Timestamp{
   150  		Seconds: uint64(stat.Mtime.Sec),
   151  		Nanos:   uint32(stat.Mtime.Nsec),
   152  	}
   153  
   154  	perms := getPermissionSet(user, stat, cv.mount, path)
   155  
   156  	for key := range mx {
   157  		if !strings.HasPrefix(key, xattrUserNs) {
   158  			delete(mx, key)
   159  		}
   160  	}
   161  
   162  	var checksum provider.ResourceChecksum
   163  	var md5 string
   164  	if _type == provider.ResourceType_RESOURCE_TYPE_FILE {
   165  		md5tsBA, err := cv.mount.GetXattr(path, xattrMd5ts) //local error inside if scope
   166  		if err == nil {
   167  			md5ts, _ := strconv.ParseInt(string(md5tsBA), 10, 64)
   168  			if stat.Mtime.Sec == md5ts {
   169  				md5BA, err := cv.mount.GetXattr(path, xattrMd5)
   170  				if err != nil {
   171  					md5, err = calcChecksum(path, cv.mount, stat)
   172  				} else {
   173  					md5 = string(md5BA)
   174  				}
   175  			} else {
   176  				md5, err = calcChecksum(path, cv.mount, stat)
   177  			}
   178  		} else {
   179  			md5, err = calcChecksum(path, cv.mount, stat)
   180  		}
   181  
   182  		if err != nil && err.Error() == errPermissionDenied {
   183  			checksum.Type = provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_UNSET
   184  		} else if err != nil {
   185  			return nil, errors.New("cephfs: error calculating checksum of file")
   186  		} else {
   187  			checksum.Type = provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_MD5
   188  			checksum.Sum = md5
   189  		}
   190  	} else {
   191  		checksum.Type = provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_UNSET
   192  	}
   193  
   194  	var ownerID *userv1beta1.UserId
   195  	if stat.Uid != 0 {
   196  		var owner *userv1beta1.User
   197  		if int64(stat.Uid) != user.UidNumber {
   198  			owner, err = user.fs.getUserByID(user.ctx, fmt.Sprint(stat.Uid))
   199  		} else {
   200  			owner = user.User
   201  		}
   202  
   203  		if owner == nil {
   204  			return nil, errors.New("cephfs: error getting owner of entry: " + path)
   205  		}
   206  
   207  		ownerID = owner.Id
   208  	} else {
   209  		ownerID = &userv1beta1.UserId{OpaqueId: "root"}
   210  	}
   211  
   212  	ri = &provider.ResourceInfo{
   213  		Type:              _type,
   214  		Id:                &provider.ResourceId{OpaqueId: fmt.Sprint(stat.Inode)},
   215  		Checksum:          &checksum,
   216  		Etag:              etag,
   217  		MimeType:          mime.Detect(isDir(_type), path),
   218  		Mtime:             mtime,
   219  		Path:              path,
   220  		PermissionSet:     perms,
   221  		Size:              size,
   222  		Owner:             ownerID,
   223  		Target:            target,
   224  		ArbitraryMetadata: &provider.ArbitraryMetadata{Metadata: mx},
   225  	}
   226  
   227  	return
   228  }
   229  
   230  func (user *User) resolveRef(ref *provider.Reference) (str string, err error) {
   231  	if ref == nil {
   232  		return "", fmt.Errorf("cephfs: nil reference")
   233  	}
   234  
   235  	if str = ref.GetPath(); str == "" {
   236  		return "", errtypes.NotSupported("cephfs: entry IDs not currently supported")
   237  	}
   238  
   239  	str = removeLeadingSlash(str) //path must be relative
   240  
   241  	return
   242  }