github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/cephfs/cephfs.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 "io" 28 "net/url" 29 "os" 30 "path/filepath" 31 "strconv" 32 "strings" 33 34 cephfs2 "github.com/ceph/go-ceph/cephfs" 35 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 36 "github.com/mitchellh/mapstructure" 37 "github.com/pkg/errors" 38 "github.com/rs/zerolog" 39 40 "github.com/cs3org/reva/v2/pkg/appctx" 41 "github.com/cs3org/reva/v2/pkg/errtypes" 42 "github.com/cs3org/reva/v2/pkg/events" 43 "github.com/cs3org/reva/v2/pkg/storage" 44 "github.com/cs3org/reva/v2/pkg/storage/fs/registry" 45 ) 46 47 const ( 48 xattrTrustedNs = "trusted." 49 xattrEID = xattrTrustedNs + "eid" 50 xattrMd5 = xattrTrustedNs + "checksum" 51 xattrMd5ts = xattrTrustedNs + "checksumTS" 52 xattrRef = xattrTrustedNs + "ref" 53 xattrUserNs = "user." 54 snap = ".snap" 55 ) 56 57 type cephfs struct { 58 conf *Options 59 conn *connections 60 adminConn *adminConn 61 chunkHandler *ChunkHandler 62 } 63 64 func init() { 65 registry.Register("cephfs", New) 66 } 67 68 // New returns an implementation to of the storage.FS interface that talk to 69 // a ceph filesystem. 70 func New(m map[string]interface{}, _ events.Stream, _ *zerolog.Logger) (fs storage.FS, err error) { 71 c := &Options{} 72 if err = mapstructure.Decode(m, c); err != nil { 73 return nil, errors.Wrap(err, "error decoding conf") 74 } 75 76 c.fillDefaults() 77 78 var cache *connections 79 if cache, err = newCache(); err != nil { 80 return nil, errors.New("cephfs: can't create caches") 81 } 82 83 adminConn := newAdminConn(c.IndexPool) 84 if adminConn == nil { 85 return nil, errors.Wrap(err, "cephfs: Couldn't create admin connections") 86 } 87 88 for _, dir := range []string{c.ShadowFolder, c.UploadFolder} { 89 err = adminConn.adminMount.MakeDir(dir, dirPermFull) 90 if err != nil && err.Error() != errFileExists { 91 return nil, errors.New("cephfs: can't initialise system dir " + dir + ":" + err.Error()) 92 } 93 } 94 95 return &cephfs{ 96 conf: c, 97 conn: cache, 98 adminConn: adminConn, 99 }, nil 100 } 101 102 func (fs *cephfs) GetHome(ctx context.Context) (string, error) { 103 if fs.conf.DisableHome { 104 return "", errtypes.NotSupported("cephfs: GetHome() home supported disabled") 105 } 106 107 user := fs.makeUser(ctx) 108 109 return user.home, nil 110 } 111 112 func (fs *cephfs) CreateHome(ctx context.Context) (err error) { 113 if fs.conf.DisableHome { 114 return errtypes.NotSupported("cephfs: GetHome() home supported disabled") 115 } 116 117 user := fs.makeUser(ctx) 118 119 // Stop createhome from running the whole thing because it is called multiple times 120 if _, err = fs.adminConn.adminMount.Statx(user.home, cephfs2.StatxMode, 0); err == nil { 121 return 122 } 123 124 err = walkPath(user.home, func(path string) error { 125 return fs.adminConn.adminMount.MakeDir(path, dirPermDefault) 126 }, false) 127 if err != nil { 128 return getRevaError(err) 129 } 130 131 err = fs.adminConn.adminMount.Chown(user.home, uint32(user.UidNumber), uint32(user.GidNumber)) 132 if err != nil { 133 return getRevaError(err) 134 } 135 136 err = fs.adminConn.adminMount.SetXattr(user.home, "ceph.quota.max_bytes", []byte(fmt.Sprint(fs.conf.UserQuotaBytes)), 0) 137 if err != nil { 138 return getRevaError(err) 139 } 140 141 user.op(func(cv *cacheVal) { 142 err = cv.mount.MakeDir(removeLeadingSlash(fs.conf.ShareFolder), dirPermDefault) 143 if err != nil && err.Error() == errFileExists { 144 err = nil 145 } 146 }) 147 148 return getRevaError(err) 149 } 150 151 func (fs *cephfs) CreateDir(ctx context.Context, ref *provider.Reference) error { 152 user := fs.makeUser(ctx) 153 path, err := user.resolveRef(ref) 154 if err != nil { 155 return getRevaError(err) 156 } 157 158 user.op(func(cv *cacheVal) { 159 if err = cv.mount.MakeDir(path, dirPermDefault); err != nil { 160 return 161 } 162 163 //TODO(tmourati): Add entry id logic 164 }) 165 166 return getRevaError(err) 167 } 168 169 // TouchFile as defined in the storage.FS interface 170 func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference, markprocessing bool, mtime string) error { 171 return fmt.Errorf("unimplemented: TouchFile") 172 } 173 174 func (fs *cephfs) Delete(ctx context.Context, ref *provider.Reference) (err error) { 175 var path string 176 user := fs.makeUser(ctx) 177 path, err = user.resolveRef(ref) 178 if err != nil { 179 return err 180 } 181 182 user.op(func(cv *cacheVal) { 183 if err = cv.mount.Unlink(path); err != nil && err.Error() == errIsADirectory { 184 err = cv.mount.RemoveDir(path) 185 } 186 187 //TODO(tmourati): Add entry id logic 188 }) 189 190 //has already been deleted by direct mount 191 if err != nil && err.Error() == errNotFound { 192 return nil 193 } 194 195 return getRevaError(err) 196 } 197 198 func (fs *cephfs) Move(ctx context.Context, oldRef, newRef *provider.Reference) (err error) { 199 var oldPath, newPath string 200 user := fs.makeUser(ctx) 201 if oldPath, err = user.resolveRef(oldRef); err != nil { 202 return 203 } 204 if newPath, err = user.resolveRef(newRef); err != nil { 205 return 206 } 207 208 user.op(func(cv *cacheVal) { 209 if err = cv.mount.Rename(oldPath, newPath); err != nil { 210 return 211 } 212 213 //TODO(tmourati): Add entry id logic, handle already moved file error 214 }) 215 216 // has already been moved by direct mount 217 if err != nil && err.Error() == errNotFound { 218 return nil 219 } 220 221 return getRevaError(err) 222 } 223 224 func (fs *cephfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string, fieldMask []string) (ri *provider.ResourceInfo, err error) { 225 var path string 226 user := fs.makeUser(ctx) 227 228 if path, err = user.resolveRef(ref); err != nil { 229 return nil, err 230 } 231 232 user.op(func(cv *cacheVal) { 233 var stat Statx 234 if stat, err = cv.mount.Statx(path, cephfs2.StatxBasicStats, 0); err != nil { 235 return 236 } 237 ri, err = user.fileAsResourceInfo(cv, path, stat, mdKeys) 238 }) 239 240 return ri, getRevaError(err) 241 } 242 243 func (fs *cephfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string, fieldMask []string) (files []*provider.ResourceInfo, err error) { 244 var path string 245 user := fs.makeUser(ctx) 246 if path, err = user.resolveRef(ref); err != nil { 247 return 248 } 249 250 user.op(func(cv *cacheVal) { 251 var dir *cephfs2.Directory 252 if dir, err = cv.mount.OpenDir(path); err != nil { 253 return 254 } 255 defer closeDir(dir) 256 257 var entry *cephfs2.DirEntryPlus 258 var ri *provider.ResourceInfo 259 for entry, err = dir.ReadDirPlus(cephfs2.StatxBasicStats, 0); entry != nil && err == nil; entry, err = dir.ReadDirPlus(cephfs2.StatxBasicStats, 0) { 260 if fs.conf.HiddenDirs[entry.Name()] { 261 continue 262 } 263 264 ri, err = user.fileAsResourceInfo(cv, filepath.Join(path, entry.Name()), entry.Statx(), mdKeys) 265 if ri == nil || err != nil { 266 if err != nil { 267 log := appctx.GetLogger(ctx) 268 log.Err(err).Msg("cephfs: error in file as resource info") 269 } 270 err = nil 271 continue 272 } 273 274 files = append(files, ri) 275 } 276 }) 277 278 return files, getRevaError(err) 279 } 280 281 func (fs *cephfs) Download(ctx context.Context, ref *provider.Reference, openReaderFunc func(md *provider.ResourceInfo) bool) (ri *provider.ResourceInfo, rc io.ReadCloser, err error) { 282 var path string 283 user := fs.makeUser(ctx) 284 if path, err = user.resolveRef(ref); err != nil { 285 return nil, nil, errors.Wrap(err, "cephfs: error resolving ref") 286 } 287 288 user.op(func(cv *cacheVal) { 289 if strings.HasPrefix(strings.TrimPrefix(path, user.home), fs.conf.ShareFolder) { 290 err = errtypes.PermissionDenied("cephfs: cannot download under the virtual share folder") 291 return 292 } 293 294 var stat Statx 295 if stat, err = cv.mount.Statx(path, cephfs2.StatxBasicStats, 0); err != nil { 296 return 297 } 298 ri, err = user.fileAsResourceInfo(cv, path, stat, nil) 299 if err != nil { 300 return 301 } 302 303 if !openReaderFunc(ri) { 304 return 305 } 306 307 rc, err = cv.mount.Open(path, os.O_RDONLY, 0) 308 }) 309 310 return ri, rc, getRevaError(err) 311 } 312 313 func (fs *cephfs) ListRevisions(ctx context.Context, ref *provider.Reference) (fvs []*provider.FileVersion, err error) { 314 //TODO(tmourati): Fix entry id logic 315 var path string 316 user := fs.makeUser(ctx) 317 if path, err = user.resolveRef(ref); err != nil { 318 return nil, errors.Wrap(err, "cephfs: error resolving ref") 319 } 320 321 user.op(func(cv *cacheVal) { 322 if strings.HasPrefix(path, removeLeadingSlash(fs.conf.ShareFolder)) { 323 err = errtypes.PermissionDenied("cephfs: cannot download under the virtual share folder") 324 return 325 } 326 var dir *cephfs2.Directory 327 if dir, err = cv.mount.OpenDir(".snap"); err != nil { 328 return 329 } 330 defer closeDir(dir) 331 332 for d, _ := dir.ReadDir(); d != nil; d, _ = dir.ReadDir() { 333 var revPath string 334 var stat Statx 335 var e error 336 337 if strings.HasPrefix(d.Name(), ".") { 338 continue 339 } 340 341 revPath, e = resolveRevRef(cv.mount, ref, d.Name()) 342 if e != nil { 343 continue 344 } 345 stat, e = cv.mount.Statx(revPath, cephfs2.StatxMtime|cephfs2.StatxSize, 0) 346 if e != nil { 347 continue 348 } 349 fvs = append(fvs, &provider.FileVersion{ 350 Key: d.Name(), 351 Size: stat.Size, 352 Mtime: uint64(stat.Mtime.Sec), 353 }) 354 } 355 }) 356 357 return fvs, getRevaError(err) 358 } 359 360 func (fs *cephfs) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (ri *provider.ResourceInfo, file io.ReadCloser, err error) { 361 //TODO(tmourati): Fix entry id logic 362 user := fs.makeUser(ctx) 363 364 user.op(func(cv *cacheVal) { 365 var revPath string 366 revPath, err = resolveRevRef(cv.mount, ref, key) 367 if err != nil { 368 return 369 } 370 371 var stat Statx 372 stat, err = cv.mount.Statx(revPath, cephfs2.StatxMtime|cephfs2.StatxSize, 0) 373 if err != nil { 374 return 375 } 376 ri, err = user.fileAsResourceInfo(cv, revPath, stat, nil) 377 if err != nil { 378 return 379 } 380 if !openReaderFunc(ri) { 381 return 382 } 383 file, err = cv.mount.Open(revPath, os.O_RDONLY, 0) 384 }) 385 386 return ri, file, getRevaError(err) 387 } 388 389 func (fs *cephfs) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) (err error) { 390 //TODO(tmourati): Fix entry id logic 391 var path string 392 user := fs.makeUser(ctx) 393 if path, err = user.resolveRef(ref); err != nil { 394 return errors.Wrap(err, "cephfs: error resolving ref") 395 } 396 397 user.op(func(cv *cacheVal) { 398 var revPath string 399 if revPath, err = resolveRevRef(cv.mount, ref, key); err != nil { 400 err = errors.Wrap(err, "cephfs: error resolving revision ref "+ref.String()) 401 return 402 } 403 404 var src, dst *cephfs2.File 405 if src, err = cv.mount.Open(revPath, os.O_RDONLY, 0); err != nil { 406 return 407 } 408 defer closeFile(src) 409 410 if dst, err = cv.mount.Open(path, os.O_WRONLY|os.O_TRUNC, 0); err != nil { 411 return 412 } 413 defer closeFile(dst) 414 415 _, err = io.Copy(dst, src) 416 }) 417 418 return getRevaError(err) 419 } 420 421 func (fs *cephfs) GetPathByID(ctx context.Context, id *provider.ResourceId) (str string, err error) { 422 //TODO(tmourati): Add entry id logic 423 return "", errtypes.NotSupported("cephfs: entry IDs currently not supported") 424 } 425 426 func (fs *cephfs) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { 427 var path string 428 user := fs.makeUser(ctx) 429 if path, err = user.resolveRef(ref); err != nil { 430 return 431 } 432 433 user.op(func(cv *cacheVal) { 434 err = fs.changePerms(ctx, cv.mount, g, path, updateGrant) 435 }) 436 437 return getRevaError(err) 438 } 439 440 func (fs *cephfs) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { 441 var path string 442 user := fs.makeUser(ctx) 443 if path, err = user.resolveRef(ref); err != nil { 444 return 445 } 446 447 user.op(func(cv *cacheVal) { 448 err = fs.changePerms(ctx, cv.mount, g, path, removeGrant) 449 }) 450 451 return getRevaError(err) 452 } 453 454 func (fs *cephfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { 455 var path string 456 user := fs.makeUser(ctx) 457 if path, err = user.resolveRef(ref); err != nil { 458 return 459 } 460 461 user.op(func(cv *cacheVal) { 462 err = fs.changePerms(ctx, cv.mount, g, path, updateGrant) 463 }) 464 465 return getRevaError(err) 466 } 467 468 func (fs *cephfs) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) (err error) { 469 var path string 470 user := fs.makeUser(ctx) 471 if path, err = user.resolveRef(ref); err != nil { 472 return 473 } 474 475 user.op(func(cv *cacheVal) { 476 grant := &provider.Grant{Grantee: g} //nil perms will remove the whole grant 477 err = fs.changePerms(ctx, cv.mount, grant, path, removeGrant) 478 }) 479 480 return getRevaError(err) 481 } 482 483 func (fs *cephfs) ListGrants(ctx context.Context, ref *provider.Reference) (glist []*provider.Grant, err error) { 484 var path string 485 user := fs.makeUser(ctx) 486 if path, err = user.resolveRef(ref); err != nil { 487 return 488 } 489 490 user.op(func(cv *cacheVal) { 491 glist = fs.getFullPermissionSet(ctx, cv.mount, path) 492 493 if glist == nil { 494 err = errors.New("cephfs: error listing grants on " + path) 495 } 496 }) 497 498 return glist, getRevaError(err) 499 } 500 501 func (fs *cephfs) GetQuota(ctx context.Context, ref *provider.Reference) (total uint64, used uint64, remaining uint64, err error) { 502 user := fs.makeUser(ctx) 503 504 log := appctx.GetLogger(ctx) 505 user.op(func(cv *cacheVal) { 506 var buf []byte 507 buf, err = cv.mount.GetXattr(".", "ceph.quota.max_bytes") 508 if err != nil { 509 log.Warn().Msg("cephfs: user quota bytes not set") 510 total = fs.conf.UserQuotaBytes 511 } else { 512 total, _ = strconv.ParseUint(string(buf), 10, 64) 513 } 514 515 buf, err = cv.mount.GetXattr(".", "ceph.dir.rbytes") 516 if err == nil { 517 used, err = strconv.ParseUint(string(buf), 10, 64) 518 } 519 }) 520 521 if used >= total { 522 remaining = 0 523 } else { 524 remaining = total - used 525 } 526 527 return total, used, remaining, getRevaError(err) 528 } 529 530 func (fs *cephfs) CreateReference(ctx context.Context, path string, targetURI *url.URL) (err error) { 531 user := fs.makeUser(ctx) 532 533 user.op(func(cv *cacheVal) { 534 if !strings.HasPrefix(strings.TrimPrefix(path, user.home), fs.conf.ShareFolder) { 535 err = errors.New("cephfs: can't create reference outside a share folder") 536 } else { 537 err = cv.mount.MakeDir(path, dirPermDefault) 538 } 539 }) 540 if err != nil { 541 return getRevaError(err) 542 } 543 544 user.op(func(cv *cacheVal) { 545 err = cv.mount.SetXattr(path, xattrRef, []byte(targetURI.String()), 0) 546 }) 547 548 return getRevaError(err) 549 } 550 551 func (fs *cephfs) Shutdown(ctx context.Context) (err error) { 552 ctx.Done() 553 fs.conn.clearCache() 554 _ = fs.adminConn.adminMount.Unmount() 555 _ = fs.adminConn.adminMount.Release() 556 fs.adminConn.radosConn.Shutdown() 557 558 return 559 } 560 561 func (fs *cephfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) (err error) { 562 var path string 563 user := fs.makeUser(ctx) 564 if path, err = user.resolveRef(ref); err != nil { 565 return err 566 } 567 568 user.op(func(cv *cacheVal) { 569 for k, v := range md.Metadata { 570 if !strings.HasPrefix(k, xattrUserNs) { 571 k = xattrUserNs + k 572 } 573 if e := cv.mount.SetXattr(path, k, []byte(v), 0); e != nil { 574 err = errors.Wrap(err, e.Error()) 575 return 576 } 577 } 578 }) 579 580 return getRevaError(err) 581 } 582 583 func (fs *cephfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) (err error) { 584 var path string 585 user := fs.makeUser(ctx) 586 if path, err = user.resolveRef(ref); err != nil { 587 return err 588 } 589 590 user.op(func(cv *cacheVal) { 591 for _, key := range keys { 592 if !strings.HasPrefix(key, xattrUserNs) { 593 key = xattrUserNs + key 594 } 595 if e := cv.mount.RemoveXattr(path, key); e != nil { 596 err = errors.Wrap(err, e.Error()) 597 return 598 } 599 } 600 }) 601 602 return getRevaError(err) 603 } 604 605 func (fs *cephfs) EmptyRecycle(ctx context.Context, ref *provider.Reference) error { 606 return errtypes.NotSupported("cephfs: empty recycle not supported") 607 } 608 609 func (fs *cephfs) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (r *provider.CreateStorageSpaceResponse, err error) { 610 return nil, errors.New("cephfs: createStorageSpace not supported") 611 } 612 613 func (fs *cephfs) ListRecycle(ctx context.Context, ref *provider.Reference, key, relativePath string) ([]*provider.RecycleItem, error) { 614 panic("implement me") 615 } 616 617 func (fs *cephfs) RestoreRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string, restoreRef *provider.Reference) error { 618 return errors.New("cephfs: restoreRecycleItem not supported") 619 } 620 621 func (fs *cephfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string) error { 622 return errors.New("cephfs: purgeRecycleItem not supported") 623 } 624 625 func (fs *cephfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, unrestricted bool) ([]*provider.StorageSpace, error) { 626 return nil, errors.New("cephfs: listStorageSpaces not supported") 627 } 628 629 func (fs *cephfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 630 return nil, errors.New("cephfs: updateStorageSpace not supported") 631 } 632 633 func (fs *cephfs) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) error { 634 return errors.New("cephfs: deleteStorageSpace not supported") 635 } 636 637 // GetLock returns an existing lock on the given reference 638 func (fs *cephfs) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { 639 return nil, errtypes.NotSupported("unimplemented") 640 } 641 642 // SetLock puts a lock on the given reference 643 func (fs *cephfs) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 644 return errtypes.NotSupported("unimplemented") 645 } 646 647 // RefreshLock refreshes an existing lock on the given reference 648 func (fs *cephfs) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error { 649 return errtypes.NotSupported("unimplemented") 650 } 651 652 // Unlock removes an existing lock from the given reference 653 func (fs *cephfs) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 654 return errtypes.NotSupported("unimplemented") 655 }