github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/localfs/localfs.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 localfs 20 21 import ( 22 "context" 23 "database/sql" 24 "fmt" 25 "io" 26 "net/url" 27 "os" 28 "path" 29 "strconv" 30 "strings" 31 "time" 32 33 grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" 34 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 35 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 36 types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 37 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 38 "github.com/cs3org/reva/v2/pkg/errtypes" 39 "github.com/cs3org/reva/v2/pkg/mime" 40 "github.com/cs3org/reva/v2/pkg/storage" 41 "github.com/cs3org/reva/v2/pkg/storage/utils/acl" 42 "github.com/cs3org/reva/v2/pkg/storage/utils/chunking" 43 "github.com/cs3org/reva/v2/pkg/storage/utils/grants" 44 "github.com/cs3org/reva/v2/pkg/storage/utils/templates" 45 "github.com/cs3org/reva/v2/pkg/utils" 46 "github.com/pkg/errors" 47 ) 48 49 // Config holds the configuration details for the local fs. 50 type Config struct { 51 Root string `mapstructure:"root"` 52 DisableHome bool `mapstructure:"disable_home"` 53 UserLayout string `mapstructure:"user_layout"` 54 ShareFolder string `mapstructure:"share_folder"` 55 DataTransfersFolder string `mapstructure:"data_transfers_folder"` 56 Uploads string `mapstructure:"uploads"` 57 DataDirectory string `mapstructure:"data_directory"` 58 RecycleBin string `mapstructure:"recycle_bin"` 59 Versions string `mapstructure:"versions"` 60 Shadow string `mapstructure:"shadow"` 61 References string `mapstructure:"references"` 62 } 63 64 func (c *Config) init() { 65 if c.Root == "" { 66 c.Root = "/var/tmp/reva" 67 } 68 69 if c.UserLayout == "" { 70 c.UserLayout = "{{.Username}}" 71 } 72 73 if c.ShareFolder == "" { 74 c.ShareFolder = "/MyShares" 75 } 76 77 if c.DataTransfersFolder == "" { 78 c.DataTransfersFolder = "/DataTransfers" 79 } 80 81 // ensure share folder always starts with slash 82 c.ShareFolder = path.Join("/", c.ShareFolder) 83 84 c.DataDirectory = path.Join(c.Root, "data") 85 c.Uploads = path.Join(c.Root, ".uploads") 86 c.Shadow = path.Join(c.Root, ".shadow") 87 88 c.References = path.Join(c.Shadow, "references") 89 c.RecycleBin = path.Join(c.Shadow, "recycle_bin") 90 c.Versions = path.Join(c.Shadow, "versions") 91 92 } 93 94 type localfs struct { 95 conf *Config 96 db *sql.DB 97 chunkHandler *chunking.ChunkHandler 98 } 99 100 // NewLocalFS returns a storage.FS interface implementation that controls then 101 // local filesystem. 102 func NewLocalFS(c *Config) (storage.FS, error) { 103 c.init() 104 105 // create namespaces if they do not exist 106 namespaces := []string{c.DataDirectory, c.Uploads, c.Shadow, c.References, c.RecycleBin, c.Versions} 107 for _, v := range namespaces { 108 if err := os.MkdirAll(v, 0755); err != nil { 109 return nil, errors.Wrap(err, "could not create home dir "+v) 110 } 111 } 112 113 dbName := "localfs.db" 114 if !c.DisableHome { 115 dbName = "localhomefs.db" 116 } 117 118 db, err := initializeDB(c.Root, dbName) 119 if err != nil { 120 return nil, errors.Wrap(err, "localfs: error initializing db") 121 } 122 123 return &localfs{ 124 conf: c, 125 db: db, 126 chunkHandler: chunking.NewChunkHandler(c.Uploads), 127 }, nil 128 } 129 130 func (fs *localfs) Shutdown(ctx context.Context) error { 131 err := fs.db.Close() 132 if err != nil { 133 return errors.Wrap(err, "localfs: error closing db connection") 134 } 135 return nil 136 } 137 138 func (fs *localfs) resolve(ctx context.Context, ref *provider.Reference) (p string, err error) { 139 if ref.ResourceId != nil { 140 if p, err = fs.GetPathByID(ctx, ref.ResourceId); err != nil { 141 return "", err 142 } 143 return path.Join(p, path.Join("/", ref.Path)), nil 144 } 145 146 if ref.Path != "" { 147 return path.Join("/", ref.Path), nil 148 } 149 150 // reference is invalid 151 return "", fmt.Errorf("invalid reference %+v. at least resource_id or path must be set", ref) 152 } 153 154 func getUser(ctx context.Context) (*userpb.User, error) { 155 u, ok := ctxpkg.ContextGetUser(ctx) 156 if !ok { 157 err := errors.Wrap(errtypes.UserRequired(""), "local: error getting user from ctx") 158 return nil, err 159 } 160 return u, nil 161 } 162 163 func (fs *localfs) wrap(ctx context.Context, p string) string { 164 // This is to prevent path traversal. 165 // With this p can't break out of its parent folder 166 p = path.Join("/", p) 167 var internal string 168 if !fs.conf.DisableHome { 169 layout, err := fs.GetHome(ctx) 170 if err != nil { 171 panic(err) 172 } 173 internal = path.Join(fs.conf.DataDirectory, layout, p) 174 } else { 175 internal = path.Join(fs.conf.DataDirectory, p) 176 } 177 return internal 178 } 179 180 func (fs *localfs) wrapReferences(ctx context.Context, p string) string { 181 var internal string 182 if !fs.conf.DisableHome { 183 layout, err := fs.GetHome(ctx) 184 if err != nil { 185 panic(err) 186 } 187 internal = path.Join(fs.conf.References, layout, p) 188 } else { 189 internal = path.Join(fs.conf.References, p) 190 } 191 return internal 192 } 193 194 func (fs *localfs) wrapRecycleBin(ctx context.Context, p string) string { 195 var internal string 196 if !fs.conf.DisableHome { 197 layout, err := fs.GetHome(ctx) 198 if err != nil { 199 panic(err) 200 } 201 internal = path.Join(fs.conf.RecycleBin, layout, p) 202 } else { 203 internal = path.Join(fs.conf.RecycleBin, p) 204 } 205 return internal 206 } 207 208 func (fs *localfs) wrapVersions(ctx context.Context, p string) string { 209 p = path.Join("/", p) 210 var internal string 211 if !fs.conf.DisableHome { 212 layout, err := fs.GetHome(ctx) 213 if err != nil { 214 panic(err) 215 } 216 internal = path.Join(fs.conf.Versions, layout, p) 217 } else { 218 internal = path.Join(fs.conf.Versions, p) 219 } 220 return internal 221 } 222 223 func (fs *localfs) unwrap(ctx context.Context, np string) string { 224 ns := fs.getNsMatch(np, []string{fs.conf.DataDirectory, fs.conf.References, fs.conf.RecycleBin, fs.conf.Versions}) 225 var external string 226 if !fs.conf.DisableHome { 227 layout, err := fs.GetHome(ctx) 228 if err != nil { 229 panic(err) 230 } 231 ns = path.Join(ns, layout) 232 } 233 234 external = strings.TrimPrefix(np, ns) 235 if external == "" { 236 external = "/" 237 } 238 return external 239 } 240 241 func (fs *localfs) getNsMatch(internal string, nss []string) string { 242 var match string 243 for _, ns := range nss { 244 if strings.HasPrefix(internal, ns) && len(ns) > len(match) { 245 match = ns 246 } 247 } 248 if match == "" { 249 panic(fmt.Sprintf("local: path is outside namespaces: path=%s namespaces=%+v", internal, nss)) 250 } 251 252 return match 253 } 254 255 func (fs *localfs) isShareFolder(ctx context.Context, p string) bool { 256 return strings.HasPrefix(p, fs.conf.ShareFolder) 257 } 258 259 func (fs *localfs) isDataTransfersFolder(ctx context.Context, p string) bool { 260 return strings.HasPrefix(p, fs.conf.DataTransfersFolder) 261 } 262 263 func (fs *localfs) isShareFolderRoot(ctx context.Context, p string) bool { 264 return path.Clean(p) == fs.conf.ShareFolder 265 } 266 267 func (fs *localfs) isShareFolderChild(ctx context.Context, p string) bool { 268 p = path.Clean(p) 269 vals := strings.Split(p, fs.conf.ShareFolder+"/") 270 return len(vals) > 1 && vals[1] != "" 271 } 272 273 // permissionSet returns the permission set for the current user 274 func (fs *localfs) permissionSet(ctx context.Context, owner *userpb.UserId) *provider.ResourcePermissions { 275 u, ok := ctxpkg.ContextGetUser(ctx) 276 if !ok { 277 return &provider.ResourcePermissions{ 278 // no permissions 279 } 280 } 281 if u.Id == nil { 282 return &provider.ResourcePermissions{ 283 // no permissions 284 } 285 } 286 if u.Id.OpaqueId == owner.OpaqueId && u.Id.Idp == owner.Idp { 287 return &provider.ResourcePermissions{ 288 // owner has all permissions 289 AddGrant: true, 290 CreateContainer: true, 291 Delete: true, 292 GetPath: true, 293 GetQuota: true, 294 InitiateFileDownload: true, 295 InitiateFileUpload: true, 296 ListContainer: true, 297 ListFileVersions: true, 298 ListGrants: true, 299 ListRecycle: true, 300 Move: true, 301 PurgeRecycle: true, 302 RemoveGrant: true, 303 RestoreFileVersion: true, 304 RestoreRecycleItem: true, 305 Stat: true, 306 UpdateGrant: true, 307 } 308 } 309 // TODO fix permissions for share recipients by traversing reading acls up to the root? cache acls for the parent node and reuse it 310 return &provider.ResourcePermissions{ 311 AddGrant: true, 312 CreateContainer: true, 313 Delete: true, 314 GetPath: true, 315 GetQuota: true, 316 InitiateFileDownload: true, 317 InitiateFileUpload: true, 318 ListContainer: true, 319 ListFileVersions: true, 320 ListGrants: true, 321 ListRecycle: true, 322 Move: true, 323 PurgeRecycle: true, 324 RemoveGrant: true, 325 RestoreFileVersion: true, 326 RestoreRecycleItem: true, 327 Stat: true, 328 UpdateGrant: true, 329 } 330 } 331 332 func (fs *localfs) normalize(ctx context.Context, fi os.FileInfo, fn string, mdKeys []string) (*provider.ResourceInfo, error) { 333 fp := fs.unwrap(ctx, path.Join("/", fn)) 334 owner, err := getUser(ctx) 335 if err != nil { 336 return nil, err 337 } 338 metadata, err := fs.retrieveArbitraryMetadata(ctx, fn, mdKeys) 339 if err != nil { 340 return nil, err 341 } 342 343 var layout string 344 if !fs.conf.DisableHome { 345 layout, err = fs.GetHome(ctx) 346 if err != nil { 347 return nil, err 348 } 349 } 350 351 // A fileid is constructed like `fileid-url_encoded_path`. See GetPathByID for the inverse conversion 352 md := &provider.ResourceInfo{ 353 Id: &provider.ResourceId{OpaqueId: "fileid-" + url.QueryEscape(path.Join(layout, fp))}, 354 Path: fp, 355 Type: getResourceType(fi.IsDir()), 356 Etag: calcEtag(ctx, fi), 357 MimeType: mime.Detect(fi.IsDir(), fp), 358 Size: uint64(fi.Size()), 359 PermissionSet: fs.permissionSet(ctx, owner.Id), 360 Mtime: &types.Timestamp{ 361 Seconds: uint64(fi.ModTime().Unix()), 362 }, 363 Owner: owner.Id, 364 ArbitraryMetadata: metadata, 365 } 366 367 return md, nil 368 } 369 370 func (fs *localfs) convertToFileReference(ctx context.Context, fi os.FileInfo, fn string, mdKeys []string) (*provider.ResourceInfo, error) { 371 info, err := fs.normalize(ctx, fi, fn, mdKeys) 372 if err != nil { 373 return nil, err 374 } 375 info.Type = provider.ResourceType_RESOURCE_TYPE_REFERENCE 376 target, err := fs.getReferenceEntry(ctx, fn) 377 if err != nil { 378 return nil, err 379 } 380 info.Target = target 381 return info, nil 382 } 383 384 func getResourceType(isDir bool) provider.ResourceType { 385 if isDir { 386 return provider.ResourceType_RESOURCE_TYPE_CONTAINER 387 } 388 return provider.ResourceType_RESOURCE_TYPE_FILE 389 } 390 391 func (fs *localfs) retrieveArbitraryMetadata(ctx context.Context, fn string, mdKeys []string) (*provider.ArbitraryMetadata, error) { 392 md, err := fs.getMetadata(ctx, fn) 393 if err != nil { 394 return nil, errors.Wrap(err, "localfs: error listing metadata") 395 } 396 var mdKey, mdVal string 397 metadata := map[string]string{} 398 399 mdKeysMap := make(map[string]struct{}) 400 for _, k := range mdKeys { 401 mdKeysMap[k] = struct{}{} 402 } 403 404 var returnAllKeys bool 405 if _, ok := mdKeysMap["*"]; len(mdKeys) == 0 || ok { 406 returnAllKeys = true 407 } 408 409 for md.Next() { 410 err = md.Scan(&mdKey, &mdVal) 411 if err != nil { 412 return nil, errors.Wrap(err, "localfs: error scanning db rows") 413 } 414 if _, ok := mdKeysMap[mdKey]; returnAllKeys || ok { 415 metadata[mdKey] = mdVal 416 } 417 } 418 return &provider.ArbitraryMetadata{ 419 Metadata: metadata, 420 }, nil 421 } 422 423 // GetPathByID returns the path pointed by the file id 424 // In this implementation the file id is in the form `fileid-url_encoded_path` 425 func (fs *localfs) GetPathByID(ctx context.Context, ref *provider.ResourceId) (string, error) { 426 var layout string 427 if !fs.conf.DisableHome { 428 var err error 429 layout, err = fs.GetHome(ctx) 430 if err != nil { 431 return "", err 432 } 433 } 434 unescapedID, err := url.QueryUnescape(ref.OpaqueId) 435 if err != nil { 436 return "", err 437 } 438 return strings.TrimPrefix(unescapedID, "fileid-"+layout), nil 439 } 440 441 func (fs *localfs) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error { 442 return errtypes.NotSupported("localfs: deny grant not supported") 443 } 444 445 func (fs *localfs) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 446 fn, err := fs.resolve(ctx, ref) 447 if err != nil { 448 return errors.Wrap(err, "localfs: error resolving ref") 449 } 450 fn = fs.wrap(ctx, fn) 451 452 role, err := grants.GetACLPerm(g.Permissions) 453 if err != nil { 454 return errors.Wrap(err, "localfs: unknown set permissions") 455 } 456 457 granteeType, err := grants.GetACLType(g.Grantee.Type) 458 if err != nil { 459 return errors.Wrap(err, "localfs: error getting grantee type") 460 } 461 var grantee string 462 if granteeType == acl.TypeUser { 463 grantee = fmt.Sprintf("%s:%s:%s@%s", granteeType, g.Grantee.GetUserId().OpaqueId, utils.UserTypeToString(g.Grantee.GetUserId().Type), g.Grantee.GetUserId().Idp) 464 } else if granteeType == acl.TypeGroup { 465 grantee = fmt.Sprintf("%s::%s@%s", granteeType, g.Grantee.GetGroupId().OpaqueId, g.Grantee.GetGroupId().Idp) 466 } 467 468 err = fs.addToACLDB(ctx, fn, grantee, role) 469 if err != nil { 470 return errors.Wrap(err, "localfs: error adding entry to DB") 471 } 472 473 return fs.propagate(ctx, fn) 474 } 475 476 func (fs *localfs) ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) { 477 fn, err := fs.resolve(ctx, ref) 478 if err != nil { 479 return nil, errors.Wrap(err, "localfs: error resolving ref") 480 } 481 fn = fs.wrap(ctx, fn) 482 483 g, err := fs.getACLs(ctx, fn) 484 if err != nil { 485 return nil, errors.Wrap(err, "localfs: error listing grants") 486 } 487 var granteeID, role string 488 var grantList []*provider.Grant 489 490 for g.Next() { 491 err = g.Scan(&granteeID, &role) 492 if err != nil { 493 return nil, errors.Wrap(err, "localfs: error scanning db rows") 494 } 495 grantSplit := strings.Split(granteeID, ":") 496 grantee := &provider.Grantee{Type: grants.GetGranteeType(grantSplit[0])} 497 parts := strings.Split(grantSplit[2], "@") 498 if grantSplit[0] == acl.TypeUser { 499 grantee.Id = &provider.Grantee_UserId{UserId: &userpb.UserId{OpaqueId: parts[0], Idp: parts[1], Type: utils.UserTypeMap(grantSplit[1])}} 500 } else if grantSplit[0] == acl.TypeGroup { 501 grantee.Id = &provider.Grantee_GroupId{GroupId: &grouppb.GroupId{OpaqueId: parts[0], Idp: parts[1]}} 502 } 503 permissions := grants.GetGrantPermissionSet(role) 504 505 grantList = append(grantList, &provider.Grant{ 506 Grantee: grantee, 507 Permissions: permissions, 508 }) 509 } 510 return grantList, nil 511 512 } 513 514 func (fs *localfs) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 515 fn, err := fs.resolve(ctx, ref) 516 if err != nil { 517 return errors.Wrap(err, "localfs: error resolving ref") 518 } 519 fn = fs.wrap(ctx, fn) 520 521 granteeType, err := grants.GetACLType(g.Grantee.Type) 522 if err != nil { 523 return errors.Wrap(err, "localfs: error getting grantee type") 524 } 525 var grantee string 526 if granteeType == acl.TypeUser { 527 grantee = fmt.Sprintf("%s:%s@%s", granteeType, g.Grantee.GetUserId().OpaqueId, g.Grantee.GetUserId().Idp) 528 } else if granteeType == acl.TypeGroup { 529 grantee = fmt.Sprintf("%s:%s@%s", granteeType, g.Grantee.GetGroupId().OpaqueId, g.Grantee.GetGroupId().Idp) 530 } 531 532 err = fs.removeFromACLDB(ctx, fn, grantee) 533 if err != nil { 534 return errors.Wrap(err, "localfs: error removing from DB") 535 } 536 537 return fs.propagate(ctx, fn) 538 } 539 540 func (fs *localfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { 541 return fs.AddGrant(ctx, ref, g) 542 } 543 544 func (fs *localfs) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { 545 var fn string 546 switch { 547 case fs.isShareFolder(ctx, path): 548 fn = fs.wrapReferences(ctx, path) 549 case fs.isDataTransfersFolder(ctx, path): 550 fn = fs.wrap(ctx, path) 551 default: 552 return errtypes.PermissionDenied("localfs: cannot create references outside the share folder and data transfers folder") 553 } 554 555 err := os.MkdirAll(fn, 0700) 556 if err != nil { 557 if os.IsNotExist(err) { 558 return errtypes.NotFound(fn) 559 } 560 return errors.Wrap(err, "localfs: error creating dir "+fn) 561 } 562 563 if err = fs.addToReferencesDB(ctx, fn, targetURI.String()); err != nil { 564 return errors.Wrap(err, "localfs: error adding entry to DB") 565 } 566 567 return fs.propagate(ctx, fn) 568 } 569 570 // CreateStorageSpace creates a storage space 571 func (fs *localfs) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { 572 return nil, errtypes.NotSupported("unimplemented: CreateStorageSpace") 573 } 574 575 func (fs *localfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error { 576 577 np, err := fs.resolve(ctx, ref) 578 if err != nil { 579 return errors.Wrap(err, "localfs: error resolving ref") 580 } 581 582 if fs.isShareFolderRoot(ctx, np) { 583 return errtypes.PermissionDenied("localfs: cannot set metadata for the virtual share folder") 584 } 585 586 if fs.isShareFolderChild(ctx, np) { 587 np = fs.wrapReferences(ctx, np) 588 } else { 589 np = fs.wrap(ctx, np) 590 } 591 592 fi, err := os.Stat(np) 593 if err != nil { 594 if os.IsNotExist(err) { 595 return errtypes.NotFound(fs.unwrap(ctx, np)) 596 } 597 return errors.Wrap(err, "localfs: error stating "+np) 598 } 599 600 if md.Metadata != nil { 601 if val, ok := md.Metadata["mtime"]; ok { 602 if mtime, err := parseMTime(val); err == nil { 603 // updating mtime also updates atime 604 if err := os.Chtimes(np, mtime, mtime); err != nil { 605 return errors.Wrap(err, "could not set mtime") 606 } 607 } else { 608 return errors.Wrap(err, "could not parse mtime") 609 } 610 delete(md.Metadata, "mtime") 611 } 612 613 if _, ok := md.Metadata["etag"]; ok { 614 etag := calcEtag(ctx, fi) 615 if etag != md.Metadata["etag"] { 616 err = fs.addToMetadataDB(ctx, np, "etag", etag) 617 if err != nil { 618 return errors.Wrap(err, "localfs: error adding entry to DB") 619 } 620 } 621 delete(md.Metadata, "etag") 622 } 623 624 if _, ok := md.Metadata["favorite"]; ok { 625 u, err := getUser(ctx) 626 if err != nil { 627 return err 628 } 629 if uid := u.GetId(); uid != nil { 630 usr := fmt.Sprintf("u:%s@%s", uid.GetOpaqueId(), uid.GetIdp()) 631 if err = fs.addToFavoritesDB(ctx, np, usr); err != nil { 632 return errors.Wrap(err, "localfs: error adding entry to DB") 633 } 634 } else { 635 return errors.Wrap(errtypes.UserRequired("userrequired"), "user has no id") 636 } 637 delete(md.Metadata, "favorite") 638 } 639 } 640 641 for k, v := range md.Metadata { 642 err = fs.addToMetadataDB(ctx, np, k, v) 643 if err != nil { 644 return errors.Wrap(err, "localfs: error adding entry to DB") 645 } 646 } 647 648 return fs.propagate(ctx, np) 649 } 650 651 func parseMTime(v string) (t time.Time, err error) { 652 p := strings.SplitN(v, ".", 2) 653 var sec, nsec int64 654 if sec, err = strconv.ParseInt(p[0], 10, 64); err == nil { 655 if len(p) > 1 { 656 nsec, err = strconv.ParseInt(p[1], 10, 64) 657 } 658 } 659 return time.Unix(sec, nsec), err 660 } 661 662 func (fs *localfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error { 663 664 np, err := fs.resolve(ctx, ref) 665 if err != nil { 666 return errors.Wrap(err, "localfs: error resolving ref") 667 } 668 669 if fs.isShareFolderRoot(ctx, np) { 670 return errtypes.PermissionDenied("localfs: cannot set metadata for the virtual share folder") 671 } 672 673 if fs.isShareFolderChild(ctx, np) { 674 np = fs.wrapReferences(ctx, np) 675 } else { 676 np = fs.wrap(ctx, np) 677 } 678 679 _, err = os.Stat(np) 680 if err != nil { 681 if os.IsNotExist(err) { 682 return errtypes.NotFound(fs.unwrap(ctx, np)) 683 } 684 return errors.Wrap(err, "localfs: error stating "+np) 685 } 686 687 for _, k := range keys { 688 switch k { 689 case "favorite": 690 u, err := getUser(ctx) 691 if err != nil { 692 return err 693 } 694 if uid := u.GetId(); uid != nil { 695 usr := fmt.Sprintf("u:%s@%s", uid.GetOpaqueId(), uid.GetIdp()) 696 if err = fs.removeFromFavoritesDB(ctx, np, usr); err != nil { 697 return errors.Wrap(err, "localfs: error removing entry from DB") 698 } 699 } else { 700 return errors.Wrap(errtypes.UserRequired("userrequired"), "user has no id") 701 } 702 case "etag": 703 return errors.Wrap(errtypes.NotSupported("unsetting etag not supported"), "could not unset metadata") 704 case "mtime": 705 return errors.Wrap(errtypes.NotSupported("unsetting mtime not supported"), "could not unset metadata") 706 default: 707 err = fs.removeFromMetadataDB(ctx, np, k) 708 if err != nil { 709 return errors.Wrap(err, "localfs: error adding entry to DB") 710 } 711 } 712 } 713 714 return fs.propagate(ctx, np) 715 } 716 717 // GetLock returns an existing lock on the given reference 718 func (fs *localfs) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { 719 return nil, errtypes.NotSupported("unimplemented") 720 } 721 722 // SetLock puts a lock on the given reference 723 func (fs *localfs) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 724 return errtypes.NotSupported("unimplemented") 725 } 726 727 // RefreshLock refreshes an existing lock on the given reference 728 func (fs *localfs) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error { 729 return errtypes.NotSupported("unimplemented") 730 } 731 732 // Unlock removes an existing lock from the given reference 733 func (fs *localfs) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { 734 return errtypes.NotSupported("unimplemented") 735 } 736 737 func (fs *localfs) GetHome(ctx context.Context) (string, error) { 738 if fs.conf.DisableHome { 739 return "", errtypes.NotSupported("local: get home not supported") 740 } 741 742 u, err := getUser(ctx) 743 if err != nil { 744 err = errors.Wrap(err, "local: wrap: no user in ctx and home is enabled") 745 return "", err 746 } 747 relativeHome := templates.WithUser(u, fs.conf.UserLayout) 748 749 return relativeHome, nil 750 } 751 752 func (fs *localfs) CreateHome(ctx context.Context) error { 753 if fs.conf.DisableHome { 754 return errtypes.NotSupported("localfs: create home not supported") 755 } 756 757 homePaths := []string{fs.wrap(ctx, "/"), fs.wrapRecycleBin(ctx, "/"), fs.wrapVersions(ctx, "/"), fs.wrapReferences(ctx, fs.conf.ShareFolder)} 758 759 for _, v := range homePaths { 760 if err := fs.createHomeInternal(ctx, v); err != nil { 761 return errors.Wrap(err, "local: error creating home dir "+v) 762 } 763 } 764 765 return nil 766 } 767 768 func (fs *localfs) createHomeInternal(ctx context.Context, fn string) error { 769 _, err := os.Stat(fn) 770 if err != nil { 771 if !os.IsNotExist(err) { 772 return errors.Wrap(err, "local: error stating:"+fn) 773 } 774 } 775 err = os.MkdirAll(fn, 0700) 776 if err != nil { 777 return errors.Wrap(err, "local: error creating dir:"+fn) 778 } 779 return nil 780 } 781 782 func (fs *localfs) CreateDir(ctx context.Context, ref *provider.Reference) error { 783 784 fn, err := fs.resolve(ctx, ref) 785 if err != nil { 786 return nil 787 } 788 789 if fs.isShareFolder(ctx, fn) { 790 return errtypes.PermissionDenied("localfs: cannot create folder under the share folder") 791 } 792 793 fn = fs.wrap(ctx, fn) 794 if _, err := os.Stat(fn); err == nil { 795 return errtypes.AlreadyExists(fn) 796 } 797 err = os.Mkdir(fn, 0700) 798 if err != nil { 799 if os.IsNotExist(err) { 800 return errtypes.PreconditionFailed(fn) 801 } 802 return errors.Wrap(err, "localfs: error creating dir "+fn) 803 } 804 805 return fs.propagate(ctx, path.Dir(fn)) 806 } 807 808 // TouchFile as defined in the storage.FS interface 809 func (fs *localfs) TouchFile(ctx context.Context, ref *provider.Reference, _ bool, _ string) error { 810 return fmt.Errorf("unimplemented: TouchFile") 811 } 812 813 func (fs *localfs) Delete(ctx context.Context, ref *provider.Reference) error { 814 fn, err := fs.resolve(ctx, ref) 815 if err != nil { 816 return errors.Wrap(err, "localfs: error resolving ref") 817 } 818 819 if fs.isShareFolderRoot(ctx, fn) { 820 return errtypes.PermissionDenied("localfs: cannot delete the virtual share folder") 821 } 822 823 var fp string 824 if fs.isShareFolderChild(ctx, fn) { 825 fp = fs.wrapReferences(ctx, fn) 826 } else { 827 fp = fs.wrap(ctx, fn) 828 } 829 830 _, err = os.Stat(fp) 831 if err != nil { 832 if os.IsNotExist(err) { 833 return errtypes.NotFound(fn) 834 } 835 return errors.Wrap(err, "localfs: error stating "+fp) 836 } 837 838 key := fmt.Sprintf("%s.d%d", path.Base(fn), time.Now().UnixNano()/int64(time.Millisecond)) 839 if err := os.Rename(fp, fs.wrapRecycleBin(ctx, key)); err != nil { 840 return errors.Wrap(err, "localfs: could not delete item") 841 } 842 843 err = fs.addToRecycledDB(ctx, key, fn) 844 if err != nil { 845 return errors.Wrap(err, "localfs: error adding entry to DB") 846 } 847 848 return fs.propagate(ctx, path.Dir(fp)) 849 } 850 851 func (fs *localfs) Move(ctx context.Context, oldRef, newRef *provider.Reference) error { 852 oldName, err := fs.resolve(ctx, oldRef) 853 if err != nil { 854 return errors.Wrap(err, "localfs: error resolving ref") 855 } 856 857 newName, err := fs.resolve(ctx, newRef) 858 if err != nil { 859 return errors.Wrap(err, "localfs: error resolving ref") 860 } 861 862 if fs.isShareFolder(ctx, oldName) || fs.isShareFolder(ctx, newName) { 863 return fs.moveReferences(ctx, oldName, newName) 864 } 865 866 oldName = fs.wrap(ctx, oldName) 867 newName = fs.wrap(ctx, newName) 868 869 if err := os.Rename(oldName, newName); err != nil { 870 return errors.Wrap(err, "localfs: error moving "+oldName+" to "+newName) 871 } 872 873 if err := fs.copyMD(oldName, newName); err != nil { 874 return errors.Wrap(err, "localfs: error copying metadata") 875 } 876 877 if err := fs.propagate(ctx, newName); err != nil { 878 return err 879 } 880 if err := fs.propagate(ctx, path.Dir(oldName)); err != nil { 881 return err 882 } 883 884 return nil 885 } 886 887 func (fs *localfs) moveReferences(ctx context.Context, oldName, newName string) error { 888 889 if fs.isShareFolderRoot(ctx, oldName) || fs.isShareFolderRoot(ctx, newName) { 890 return errtypes.PermissionDenied("localfs: cannot move/rename the virtual share folder") 891 } 892 893 // only rename of the reference is allowed, hence having the same basedir 894 bold, _ := path.Split(oldName) 895 bnew, _ := path.Split(newName) 896 897 if bold != bnew { 898 return errtypes.PermissionDenied("localfs: cannot move references under the virtual share folder") 899 } 900 901 oldName = fs.wrapReferences(ctx, oldName) 902 newName = fs.wrapReferences(ctx, newName) 903 904 if err := os.Rename(oldName, newName); err != nil { 905 return errors.Wrap(err, "localfs: error moving "+oldName+" to "+newName) 906 } 907 908 if err := fs.copyMD(oldName, newName); err != nil { 909 return errors.Wrap(err, "localfs: error copying metadata") 910 } 911 912 if err := fs.propagate(ctx, newName); err != nil { 913 return err 914 } 915 if err := fs.propagate(ctx, path.Dir(oldName)); err != nil { 916 return err 917 } 918 919 return nil 920 } 921 922 func (fs *localfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string, fieldMask []string) (*provider.ResourceInfo, error) { 923 fn, err := fs.resolve(ctx, ref) 924 if err != nil { 925 return nil, errors.Wrap(err, "localfs: error resolving ref") 926 } 927 928 if !fs.conf.DisableHome { 929 if fs.isShareFolder(ctx, fn) { 930 return fs.getMDShareFolder(ctx, fn, mdKeys) 931 } 932 } 933 934 fn = fs.wrap(ctx, fn) 935 md, err := os.Stat(fn) 936 if err != nil { 937 if os.IsNotExist(err) { 938 return nil, errtypes.NotFound(fn) 939 } 940 return nil, errors.Wrap(err, "localfs: error stating "+fn) 941 } 942 943 return fs.normalize(ctx, md, fn, mdKeys) 944 } 945 946 func (fs *localfs) getMDShareFolder(ctx context.Context, p string, mdKeys []string) (*provider.ResourceInfo, error) { 947 948 fn := fs.wrapReferences(ctx, p) 949 md, err := os.Stat(fn) 950 if err != nil { 951 if os.IsNotExist(err) { 952 return nil, errtypes.NotFound(fn) 953 } 954 return nil, errors.Wrap(err, "localfs: error stating "+fn) 955 } 956 957 if fs.isShareFolderRoot(ctx, p) { 958 return fs.normalize(ctx, md, fn, mdKeys) 959 } 960 return fs.convertToFileReference(ctx, md, fn, mdKeys) 961 } 962 963 func (fs *localfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys, fieldMask []string) ([]*provider.ResourceInfo, error) { 964 fn, err := fs.resolve(ctx, ref) 965 if err != nil { 966 return nil, errors.Wrap(err, "localfs: error resolving ref") 967 } 968 969 if fn == "/" { 970 homeFiles, err := fs.listFolder(ctx, fn, mdKeys) 971 if err != nil { 972 return nil, err 973 } 974 if !fs.conf.DisableHome { 975 sharedReferences, err := fs.listShareFolderRoot(ctx, fn, mdKeys) 976 if err != nil { 977 return nil, err 978 } 979 homeFiles = append(homeFiles, sharedReferences...) 980 } 981 return homeFiles, nil 982 } 983 984 if fs.isShareFolderRoot(ctx, fn) { 985 return fs.listShareFolderRoot(ctx, fn, mdKeys) 986 } 987 988 if fs.isShareFolderChild(ctx, fn) { 989 return nil, errtypes.PermissionDenied("localfs: error listing folders inside the shared folder, only file references are stored inside") 990 } 991 992 return fs.listFolder(ctx, fn, mdKeys) 993 } 994 995 func (fs *localfs) listFolder(ctx context.Context, fn string, mdKeys []string) ([]*provider.ResourceInfo, error) { 996 997 fn = fs.wrap(ctx, fn) 998 999 mds, err := os.ReadDir(fn) 1000 if err != nil { 1001 if os.IsNotExist(err) { 1002 return nil, errtypes.NotFound(fn) 1003 } 1004 return nil, errors.Wrap(err, "localfs: error listing "+fn) 1005 } 1006 1007 finfos := []*provider.ResourceInfo{} 1008 for _, md := range mds { 1009 mdInfo, _ := md.Info() 1010 info, err := fs.normalize(ctx, mdInfo, path.Join(fn, md.Name()), mdKeys) 1011 if err == nil { 1012 finfos = append(finfos, info) 1013 } 1014 } 1015 return finfos, nil 1016 } 1017 1018 func (fs *localfs) listShareFolderRoot(ctx context.Context, home string, mdKeys []string) ([]*provider.ResourceInfo, error) { 1019 1020 fn := fs.wrapReferences(ctx, home) 1021 1022 mds, err := os.ReadDir(fn) 1023 if err != nil { 1024 if os.IsNotExist(err) { 1025 return nil, errtypes.NotFound(fn) 1026 } 1027 return nil, errors.Wrap(err, "localfs: error listing "+fn) 1028 } 1029 1030 finfos := []*provider.ResourceInfo{} 1031 for _, md := range mds { 1032 var info *provider.ResourceInfo 1033 var err error 1034 if fs.isShareFolderRoot(ctx, path.Join("/", md.Name())) { 1035 mdInfo, _ := md.Info() 1036 info, err = fs.normalize(ctx, mdInfo, path.Join(fn, md.Name()), mdKeys) 1037 } else { 1038 mdInfo, _ := md.Info() 1039 info, err = fs.convertToFileReference(ctx, mdInfo, path.Join(fn, md.Name()), mdKeys) 1040 } 1041 if err == nil { 1042 finfos = append(finfos, info) 1043 } 1044 } 1045 return finfos, nil 1046 } 1047 1048 func (fs *localfs) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { 1049 fn, err := fs.resolve(ctx, ref) 1050 if err != nil { 1051 return nil, nil, errors.Wrap(err, "localfs: error resolving ref") 1052 } 1053 1054 if fs.isShareFolder(ctx, fn) { 1055 return nil, nil, errtypes.PermissionDenied("localfs: cannot download under the virtual share folder") 1056 } 1057 1058 fn = fs.wrap(ctx, fn) 1059 md, err := os.Stat(fn) 1060 if err != nil { 1061 if os.IsNotExist(err) { 1062 return nil, nil, errtypes.NotFound(fn) 1063 } 1064 return nil, nil, errors.Wrap(err, "localfs: error stating "+fn) 1065 } 1066 1067 ri, err := fs.normalize(ctx, md, fn, []string{"size", "mimetype", "etag"}) 1068 if err != nil { 1069 return nil, nil, err 1070 } 1071 1072 if !openReaderfunc(ri) { 1073 return ri, nil, nil 1074 } 1075 1076 r, err := os.Open(fn) 1077 if err != nil { 1078 if os.IsNotExist(err) { 1079 return nil, nil, errtypes.NotFound(fn) 1080 } 1081 return nil, nil, errors.Wrap(err, "localfs: error reading "+fn) 1082 } 1083 return ri, r, nil 1084 } 1085 1086 func (fs *localfs) archiveRevision(ctx context.Context, np string) error { 1087 1088 versionsDir := fs.wrapVersions(ctx, fs.unwrap(ctx, np)) 1089 if err := os.MkdirAll(versionsDir, 0700); err != nil { 1090 return errors.Wrap(err, "localfs: error creating file versions dir "+versionsDir) 1091 } 1092 1093 vp := path.Join(versionsDir, fmt.Sprintf("v%d", time.Now().UnixNano()/int64(time.Millisecond))) 1094 if err := os.Rename(np, vp); err != nil { 1095 return errors.Wrap(err, "localfs: error renaming from "+np+" to "+vp) 1096 } 1097 1098 return nil 1099 } 1100 1101 func (fs *localfs) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { 1102 np, err := fs.resolve(ctx, ref) 1103 if err != nil { 1104 return nil, errors.Wrap(err, "localfs: error resolving ref") 1105 } 1106 1107 if fs.isShareFolder(ctx, np) { 1108 return nil, errtypes.PermissionDenied("localfs: cannot list revisions under the virtual share folder") 1109 } 1110 1111 versionsDir := fs.wrapVersions(ctx, np) 1112 revisions := []*provider.FileVersion{} 1113 mds, err := os.ReadDir(versionsDir) 1114 if err != nil { 1115 return nil, errors.Wrap(err, "localfs: error reading"+versionsDir) 1116 } 1117 1118 for i := range mds { 1119 // versions resemble v12345678 1120 version := mds[i].Name()[1:] 1121 1122 mtime, err := strconv.Atoi(version) 1123 if err != nil { 1124 continue 1125 } 1126 mdsInfo, _ := mds[i].Info() 1127 revisions = append(revisions, &provider.FileVersion{ 1128 Key: version, 1129 Size: uint64(mdsInfo.Size()), 1130 Mtime: uint64(mtime), 1131 Etag: calcEtag(ctx, mdsInfo), 1132 }) 1133 } 1134 return revisions, nil 1135 } 1136 1137 func (fs *localfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { 1138 np, err := fs.resolve(ctx, ref) 1139 if err != nil { 1140 return nil, nil, errors.Wrap(err, "localfs: error resolving ref") 1141 } 1142 1143 if fs.isShareFolder(ctx, np) { 1144 return nil, nil, errtypes.PermissionDenied("localfs: cannot download revisions under the virtual share folder") 1145 } 1146 1147 versionsDir := fs.wrapVersions(ctx, np) 1148 vp := path.Join(versionsDir, revisionKey) 1149 1150 md, err := os.Stat(vp) 1151 if err != nil { 1152 if os.IsNotExist(err) { 1153 return nil, nil, errtypes.NotFound(vp) 1154 } 1155 return nil, nil, errors.Wrap(err, "localfs: error stating "+vp) 1156 } 1157 1158 ri, err := fs.normalize(ctx, md, vp, []string{"size", "mimetype", "etag"}) 1159 if err != nil { 1160 return nil, nil, err 1161 } 1162 1163 if !openReaderfunc(ri) { 1164 return ri, nil, nil 1165 } 1166 1167 r, err := os.Open(vp) 1168 if err != nil { 1169 return nil, nil, errors.Wrap(err, "localfs: error reading "+vp) 1170 } 1171 1172 return ri, r, nil 1173 } 1174 1175 func (fs *localfs) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { 1176 np, err := fs.resolve(ctx, ref) 1177 if err != nil { 1178 return errors.Wrap(err, "localfs: error resolving ref") 1179 } 1180 1181 if fs.isShareFolder(ctx, np) { 1182 return errtypes.PermissionDenied("localfs: cannot restore revisions under the virtual share folder") 1183 } 1184 1185 versionsDir := fs.wrapVersions(ctx, np) 1186 vp := path.Join(versionsDir, revisionKey) 1187 np = fs.wrap(ctx, np) 1188 1189 // check revision exists 1190 vs, err := os.Stat(vp) 1191 if err != nil { 1192 if os.IsNotExist(err) { 1193 return errtypes.NotFound(revisionKey) 1194 } 1195 return errors.Wrap(err, "localfs: error stating "+vp) 1196 } 1197 1198 if !vs.Mode().IsRegular() { 1199 return fmt.Errorf("%s is not a regular file", vp) 1200 } 1201 1202 if err := fs.archiveRevision(ctx, np); err != nil { 1203 return err 1204 } 1205 1206 if err := os.Rename(vp, np); err != nil { 1207 return errors.Wrap(err, "localfs: error renaming from "+vp+" to "+np) 1208 } 1209 1210 return fs.propagate(ctx, np) 1211 } 1212 1213 func (fs *localfs) PurgeRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string) error { 1214 rp := fs.wrapRecycleBin(ctx, key) 1215 1216 if err := os.Remove(rp); err != nil { 1217 return errors.Wrap(err, "localfs: error deleting recycle item") 1218 } 1219 return nil 1220 } 1221 1222 func (fs *localfs) EmptyRecycle(ctx context.Context, ref *provider.Reference) error { 1223 rp := fs.wrapRecycleBin(ctx, "/") 1224 1225 if err := os.RemoveAll(rp); err != nil { 1226 return errors.Wrap(err, "localfs: error deleting recycle files") 1227 } 1228 if err := fs.createHomeInternal(ctx, rp); err != nil { 1229 return errors.Wrap(err, "localfs: error deleting recycle files") 1230 } 1231 return nil 1232 } 1233 1234 func (fs *localfs) convertToRecycleItem(ctx context.Context, rp string, md os.FileInfo) *provider.RecycleItem { 1235 // trashbin items have filename.txt.d12345678 1236 suffix := path.Ext(md.Name()) 1237 if len(suffix) == 0 || !strings.HasPrefix(suffix, ".d") { 1238 return nil 1239 } 1240 1241 trashtime := suffix[2:] 1242 ttime, err := strconv.Atoi(trashtime) 1243 if err != nil { 1244 return nil 1245 } 1246 1247 filePath, err := fs.getRecycledEntry(ctx, md.Name()) 1248 if err != nil { 1249 return nil 1250 } 1251 1252 return &provider.RecycleItem{ 1253 Type: getResourceType(md.IsDir()), 1254 Key: md.Name(), 1255 Ref: &provider.Reference{Path: filePath}, 1256 Size: uint64(md.Size()), 1257 DeletionTime: &types.Timestamp{ 1258 Seconds: uint64(ttime), 1259 }, 1260 } 1261 } 1262 1263 func (fs *localfs) ListRecycle(ctx context.Context, ref *provider.Reference, key, relativePath string) ([]*provider.RecycleItem, error) { 1264 1265 rp := fs.wrapRecycleBin(ctx, "/") 1266 1267 mds, err := os.ReadDir(rp) 1268 if err != nil { 1269 return nil, errors.Wrap(err, "localfs: error listing deleted files") 1270 } 1271 items := []*provider.RecycleItem{} 1272 for i := range mds { 1273 mdsInfo, _ := mds[i].Info() 1274 ri := fs.convertToRecycleItem(ctx, rp, mdsInfo) 1275 if ri != nil { 1276 items = append(items, ri) 1277 } 1278 } 1279 return items, nil 1280 } 1281 1282 func (fs *localfs) RestoreRecycleItem(ctx context.Context, ref *provider.Reference, key, relativePath string, restoreRef *provider.Reference) error { 1283 1284 suffix := path.Ext(key) 1285 if len(suffix) == 0 || !strings.HasPrefix(suffix, ".d") { 1286 return errors.New("localfs: invalid trash item suffix") 1287 } 1288 1289 filePath, err := fs.getRecycledEntry(ctx, key) 1290 if err != nil { 1291 return errors.Wrap(err, "localfs: invalid key") 1292 } 1293 1294 var localRestorePath string 1295 switch { 1296 case restoreRef != nil && restoreRef.Path != "": 1297 localRestorePath = fs.wrap(ctx, restoreRef.Path) 1298 case fs.isShareFolder(ctx, filePath): 1299 localRestorePath = fs.wrapReferences(ctx, filePath) 1300 default: 1301 localRestorePath = fs.wrap(ctx, filePath) 1302 } 1303 1304 if _, err = os.Stat(localRestorePath); err == nil { 1305 return errors.New("localfs: can't restore - file already exists at original path") 1306 } 1307 1308 rp := fs.wrapRecycleBin(ctx, key) 1309 if _, err = os.Stat(rp); err != nil { 1310 if os.IsNotExist(err) { 1311 return errtypes.NotFound(key) 1312 } 1313 return errors.Wrap(err, "localfs: error stating "+rp) 1314 } 1315 1316 if err := os.Rename(rp, localRestorePath); err != nil { 1317 return errors.Wrap(err, "ocfs: could not restore item") 1318 } 1319 1320 err = fs.removeFromRecycledDB(ctx, key) 1321 if err != nil { 1322 return errors.Wrap(err, "localfs: error adding entry to DB") 1323 } 1324 1325 return fs.propagate(ctx, localRestorePath) 1326 } 1327 1328 func (fs *localfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter, unrestricted bool) ([]*provider.StorageSpace, error) { 1329 return nil, errtypes.NotSupported("list storage spaces") 1330 } 1331 1332 // UpdateStorageSpace updates a storage space 1333 func (fs *localfs) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { 1334 return nil, errtypes.NotSupported("update storage space") 1335 } 1336 1337 // DeleteStorageSpace deletes a storage space 1338 func (fs *localfs) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) error { 1339 return errtypes.NotSupported("delete storage space") 1340 } 1341 1342 func (fs *localfs) propagate(ctx context.Context, leafPath string) error { 1343 1344 var root string 1345 if fs.isShareFolderChild(ctx, leafPath) || strings.HasSuffix(path.Clean(leafPath), fs.conf.ShareFolder) { 1346 root = fs.wrapReferences(ctx, "/") 1347 } else { 1348 root = fs.wrap(ctx, "/") 1349 } 1350 1351 if !strings.HasPrefix(leafPath, root) { 1352 return errors.New("internal path: " + leafPath + " outside root: " + root) 1353 } 1354 1355 fi, err := os.Stat(leafPath) 1356 if err != nil { 1357 return err 1358 } 1359 1360 parts := strings.Split(strings.TrimPrefix(leafPath, root), "/") 1361 // root never ends in / so the split returns an empty first element, which we can skip 1362 // we do not need to chmod the last element because it is the leaf path (< and not <= comparison) 1363 for i := 1; i < len(parts); i++ { 1364 if err := os.Chtimes(root, fi.ModTime(), fi.ModTime()); err != nil { 1365 return err 1366 } 1367 root = path.Join(root, parts[i]) 1368 } 1369 return nil 1370 }