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 }