github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/kbfs/simplefs/simplefs.go (about) 1 // Copyright 2016-2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package simplefs 6 7 import ( 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 "io" 12 "log" 13 "mime" 14 "net/http" 15 "net/url" 16 "os" 17 "path" 18 stdpath "path" 19 "path/filepath" 20 "regexp" 21 "sort" 22 "strings" 23 "sync" 24 "time" 25 26 "github.com/keybase/client/go/kbfs/data" 27 "github.com/keybase/client/go/kbfs/env" 28 "github.com/keybase/client/go/kbfs/idutil" 29 "github.com/keybase/client/go/kbfs/kbfscrypto" 30 "github.com/keybase/client/go/kbfs/kbfsgit" 31 "github.com/keybase/client/go/kbfs/kbfsmd" 32 "github.com/keybase/client/go/kbfs/libcontext" 33 "github.com/keybase/client/go/kbfs/libfs" 34 "github.com/keybase/client/go/kbfs/libgit" 35 "github.com/keybase/client/go/kbfs/libhttpserver" 36 "github.com/keybase/client/go/kbfs/libkbfs" 37 "github.com/keybase/client/go/kbfs/search" 38 "github.com/keybase/client/go/kbfs/tlf" 39 "github.com/keybase/client/go/kbfs/tlfhandle" 40 "github.com/keybase/client/go/libkb" 41 "github.com/keybase/client/go/logger" 42 "github.com/keybase/client/go/protocol/keybase1" 43 "github.com/keybase/client/go/runtimestats" 44 "github.com/pkg/errors" 45 "golang.org/x/net/context" 46 "golang.org/x/sync/errgroup" 47 billy "gopkg.in/src-d/go-billy.v4" 48 "gopkg.in/src-d/go-billy.v4/osfs" 49 ) 50 51 const ( 52 // CtxOpID is the display name for the unique operation SimpleFS ID tag. 53 ctxOpID = "SFSID" 54 ) 55 56 // CtxTagKey is the type used for unique context tags 57 type ctxTagKey int 58 59 const ( 60 // CtxIDKey is the type of the tag for unique operation IDs. 61 ctxIDKey ctxTagKey = iota 62 ) 63 64 // simpleFSError wraps errors for SimpleFS 65 type simpleFSError struct { 66 reason string 67 code keybase1.StatusCode 68 } 69 70 // Error implements the error interface for simpleFSError 71 func (e simpleFSError) Error() string { return e.reason } 72 73 // ToStatus implements the keybase1.ToStatusAble interface for simpleFSError 74 func (e simpleFSError) ToStatus() keybase1.Status { 75 code := e.code 76 if code == 0 { 77 code = keybase1.StatusCode_SCGeneric 78 } 79 return keybase1.Status{ 80 Name: e.reason, 81 Code: int(code), 82 Desc: e.Error(), 83 } 84 } 85 86 var errOnlyRemotePathSupported = simpleFSError{reason: "Only remote paths are supported for this operation"} 87 var errInvalidRemotePath = simpleFSError{reason: "Invalid remote path"} 88 var errNoSuchHandle = simpleFSError{reason: "No such handle"} 89 var errNoResult = simpleFSError{reason: "Async result not found"} 90 var errNameExists = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNameExists, reason: "name exists"} 91 var errDirNotEmpty = simpleFSError{code: keybase1.StatusCode_SCSimpleFSDirNotEmpty, reason: "dir not empty"} 92 var errNotExist = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNotExist, reason: "file or folder does not exist"} 93 var errNoAccess = simpleFSError{code: keybase1.StatusCode_SCSimpleFSNoAccess, reason: "no access to this file or folder"} 94 95 func translateErr(err error) error { 96 cause := errors.Cause(err) 97 98 switch { 99 case os.IsExist(cause): 100 return errNameExists 101 case os.IsNotExist(cause): 102 return errNotExist 103 case cause == os.ErrPermission: 104 return errNoAccess 105 } 106 107 switch cause.(type) { 108 case data.NameExistsError: 109 return errNameExists 110 case libkbfs.DirNotEmptyError: 111 return errDirNotEmpty 112 case idutil.BadTLFNameError, tlf.BadNameError, tlfhandle.ReadAccessError: 113 return errNoAccess 114 } 115 116 return err 117 } 118 119 type newFSFunc func( 120 context.Context, libkbfs.Config, *tlfhandle.Handle, data.BranchName, 121 string, bool) (billy.Filesystem, error) 122 123 func defaultNewFS(ctx context.Context, config libkbfs.Config, 124 tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string, 125 create bool) ( 126 billy.Filesystem, error) { 127 maker := libfs.NewFS 128 if !create { 129 maker = libfs.NewFSIfExists 130 } 131 return maker( 132 ctx, config, tlfHandle, branch, subdir, "", keybase1.MDPriorityNormal) 133 } 134 135 // SimpleFS is the simple filesystem rpc layer implementation. 136 type SimpleFS struct { 137 // logs for logging - constant, do not need locking. 138 log logger.Logger 139 vlog *libkb.VDebugLog 140 // config for the fs - constant, does not need locking. 141 config libkbfs.Config 142 // The function to call for constructing a new KBFS file system. 143 // Overrideable for testing purposes. 144 newFS newFSFunc 145 // For dumping debug info to the logs. 146 idd *libkbfs.ImpatientDebugDumper 147 148 // lock protects handles and inProgress 149 lock sync.RWMutex 150 // handles contains handles opened by SimpleFSOpen, 151 // closed by SimpleFSClose (or SimpleFSCancel) and used 152 // by listing, reading and writing. 153 handles map[keybase1.OpID]*handle 154 // inProgress is for keeping state of operations in progress, 155 // values are removed by SimpleFSWait (or SimpleFSCancel). 156 inProgress map[keybase1.OpID]*inprogress 157 158 subscribeLock sync.RWMutex 159 subscribeCurrTlfPathFromGUI string 160 subscribeCurrFB data.FolderBranch 161 subscribeToEmptyTlf string 162 163 localHTTPServer *libhttpserver.Server 164 165 subscriptionNotifier libkbfs.SubscriptionNotifier 166 167 shutdownLock sync.RWMutex 168 downloadManager *downloadManager 169 uploadManager *uploadManager 170 archiveManager *archiveManager 171 indexer *search.Indexer // Indexes synced TLFs. 172 173 httpClient *http.Client 174 } 175 176 var _ libkbfs.ResetForLoginer = (*SimpleFS)(nil) 177 178 func (k *SimpleFS) getDownloadManager() *downloadManager { 179 k.shutdownLock.RLock() 180 defer k.shutdownLock.RUnlock() 181 return k.downloadManager 182 } 183 184 func (k *SimpleFS) getUploadManager() *uploadManager { 185 k.shutdownLock.RLock() 186 defer k.shutdownLock.RUnlock() 187 return k.uploadManager 188 } 189 190 func (k *SimpleFS) getArchiveManager() *archiveManager { 191 k.shutdownLock.RLock() 192 defer k.shutdownLock.RUnlock() 193 return k.archiveManager 194 } 195 196 func (k *SimpleFS) getIndexer() *search.Indexer { 197 k.shutdownLock.RLock() 198 defer k.shutdownLock.RUnlock() 199 return k.indexer 200 } 201 202 // ResetForLogin (partially) resets simplefs state when user logs out. 203 func (k *SimpleFS) ResetForLogin(ctx context.Context, 204 username libkb.NormalizedUsername) (err error) { 205 k.log.CDebugf(ctx, "+ SimpleFS.ResetForLogin username=%s", username) 206 defer func() { k.log.CDebugf(ctx, "- SimpleFS.ResetForLogin err=%v", err) }() 207 208 k.shutdownLock.Lock() 209 defer k.shutdownLock.Unlock() 210 211 k.archiveManager.shutdown(ctx) 212 k.archiveManager, err = newArchiveManager(k, username) 213 if err != nil { 214 log.Fatalf("initializing archive manager error: %v", err) 215 } 216 217 if k.indexer != nil { 218 err = k.indexer.Shutdown(ctx) 219 if err != nil { 220 return err 221 } 222 k.indexer, err = search.NewIndexer(k.config) 223 if err != nil { 224 k.log.Warning("Couldn't make indexer: %+v", err) 225 } 226 } 227 228 k.downloadManager = newDownloadManager(k) 229 k.uploadManager = newUploadManager(k) 230 231 return nil 232 } 233 234 type inprogress struct { 235 desc keybase1.OpDescription 236 cancel context.CancelFunc 237 done chan error 238 progress keybase1.OpProgress 239 } 240 241 type handle struct { 242 file billy.File 243 async interface{} 244 path keybase1.Path 245 cancel context.CancelFunc 246 } 247 248 // make sure the interface is implemented 249 var _ keybase1.SimpleFSInterface = (*SimpleFS)(nil) 250 251 // We need this wrapper because at the time simpleFS is initialized 252 // KeybaseService is not initialized yet. So just save config here, and get 253 // the KeybaseService() later. 254 type subscriptionNotifier struct { 255 config libkbfs.Config 256 } 257 258 var _ libkbfs.SubscriptionNotifier = subscriptionNotifier{} 259 260 // OnNonPathChange implements the libkbfs.SubscriptionNotifier interface. 261 func (s subscriptionNotifier) OnPathChange( 262 clientID libkbfs.SubscriptionManagerClientID, 263 subscriptionIDs []libkbfs.SubscriptionID, 264 path string, topics []keybase1.PathSubscriptionTopic) { 265 ks := s.config.KeybaseService() 266 if ks == nil { 267 return 268 } 269 ks.OnPathChange(clientID, subscriptionIDs, path, topics) 270 } 271 272 // OnPathChange implements the libkbfs.SubscriptionNotifier interface. 273 func (s subscriptionNotifier) OnNonPathChange( 274 clientID libkbfs.SubscriptionManagerClientID, 275 subscriptionIDs []libkbfs.SubscriptionID, 276 topic keybase1.SubscriptionTopic) { 277 ks := s.config.KeybaseService() 278 if ks == nil { 279 return 280 } 281 ks.OnNonPathChange(clientID, subscriptionIDs, topic) 282 } 283 284 func newSimpleFS(appStateUpdater env.AppStateUpdater, config libkbfs.Config) *SimpleFS { 285 log := config.MakeLogger("simplefs") 286 var localHTTPServer *libhttpserver.Server 287 var err error 288 if config.Mode().LocalHTTPServerEnabled() { 289 localHTTPServer, err = libhttpserver.New(appStateUpdater, config) 290 if err != nil { 291 log.Fatalf("initializing localHTTPServer error: %v", err) 292 } 293 } 294 295 var indexer *search.Indexer 296 if config.Mode().IndexingEnabled() { 297 newIndexer, err := search.NewIndexer(config) 298 if err != nil { 299 log.Warning("Couldn't make indexer: %+v", err) 300 } else { 301 indexer = newIndexer 302 } 303 } 304 305 k := &SimpleFS{ 306 config: config, 307 308 handles: map[keybase1.OpID]*handle{}, 309 inProgress: map[keybase1.OpID]*inprogress{}, 310 log: log, 311 vlog: config.MakeVLogger(log), 312 newFS: defaultNewFS, 313 idd: libkbfs.NewImpatientDebugDumperForForcedDumps(config), 314 indexer: indexer, 315 localHTTPServer: localHTTPServer, 316 subscriptionNotifier: subscriptionNotifier{config}, 317 httpClient: &http.Client{}, 318 } 319 k.downloadManager = newDownloadManager(k) 320 k.uploadManager = newUploadManager(k) 321 k.archiveManager, err = newArchiveManager(k, config.KbEnv().GetUsername()) 322 if err != nil { 323 log.Fatalf("initializing archive manager error: %v", err) 324 } 325 return k 326 } 327 328 // NewSimpleFS creates a new SimpleFS instance. 329 func NewSimpleFS( 330 appStateUpdater env.AppStateUpdater, config libkbfs.Config) ( 331 iface *SimpleFS, 332 shutdownFn func(context.Context) error) { 333 simpleFS := newSimpleFS(appStateUpdater, config) 334 return simpleFS, simpleFS.Shutdown 335 } 336 337 func (k *SimpleFS) makeContext(ctx context.Context) context.Context { 338 return libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, k.log) 339 } 340 341 func (k *SimpleFS) makeContextWithIdentifyBehavior(ctx context.Context, identifyBehavior *keybase1.TLFIdentifyBehavior) (newCtx context.Context, err error) { 342 newCtx = libkbfs.CtxWithRandomIDReplayable(ctx, ctxIDKey, ctxOpID, k.log) 343 if identifyBehavior != nil { 344 newCtx, err = tlfhandle.MakeExtendedIdentify(newCtx, *identifyBehavior) 345 if err != nil { 346 return nil, err 347 } 348 } 349 return newCtx, nil 350 } 351 352 func getIdentifyBehaviorFromPath(path *keybase1.Path) (*keybase1.TLFIdentifyBehavior, error) { 353 if path == nil { 354 return nil, nil 355 } 356 pt, err := path.PathType() 357 if err != nil { 358 return nil, err 359 } 360 switch pt { 361 case keybase1.PathType_KBFS: 362 return path.Kbfs().IdentifyBehavior, nil 363 case keybase1.PathType_KBFS_ARCHIVED: 364 return path.KbfsArchived().IdentifyBehavior, nil 365 default: 366 return nil, nil 367 } 368 } 369 370 func populateIdentifyBehaviorIfNeeded(ctx context.Context, path1 *keybase1.Path, path2 *keybase1.Path) (context.Context, error) { 371 ib1, err := getIdentifyBehaviorFromPath(path1) 372 if err != nil { 373 return nil, err 374 } 375 ib2, err := getIdentifyBehaviorFromPath(path2) 376 if err != nil { 377 return nil, err 378 } 379 380 if ib1 == nil && ib2 == nil { 381 return ctx, nil 382 } 383 if ib1 == nil && ib2 != nil { 384 return tlfhandle.MakeExtendedIdentify(ctx, *ib2) 385 } 386 if ib1 != nil && ib2 == nil { 387 return tlfhandle.MakeExtendedIdentify(ctx, *ib1) 388 } 389 if *ib1 == *ib2 { 390 return tlfhandle.MakeExtendedIdentify(ctx, *ib1) 391 } 392 return nil, errors.New("inconsistent IdentifyBehavior set in both paths") 393 } 394 395 func rawPathFromKbfsPath(path keybase1.Path) (string, error) { 396 pt, err := path.PathType() 397 if err != nil { 398 return "", err 399 } 400 401 switch pt { 402 case keybase1.PathType_KBFS: 403 return stdpath.Clean(path.Kbfs().Path), nil 404 case keybase1.PathType_KBFS_ARCHIVED: 405 return stdpath.Clean(path.KbfsArchived().Path), nil 406 default: 407 return "", errOnlyRemotePathSupported 408 } 409 } 410 411 func splitPathFromKbfsPath(path keybase1.Path) ([]string, error) { 412 raw, err := rawPathFromKbfsPath(path) 413 if err != nil { 414 return nil, err 415 } 416 if stdpath.IsAbs(raw) { 417 raw = raw[1:] 418 } 419 return strings.Split(raw, `/`), nil 420 } 421 422 // remoteTlfAndPath decodes a remote path for us. 423 func remoteTlfAndPath(path keybase1.Path) ( 424 t tlf.Type, tlfName, middlePath, finalElem string, err error) { 425 ps, err := splitPathFromKbfsPath(path) 426 if err != nil { 427 return tlf.Private, "", "", "", err 428 } 429 switch { 430 case len(ps) < 2: 431 return tlf.Private, "", "", "", errInvalidRemotePath 432 case ps[0] == `private`: 433 t = tlf.Private 434 case ps[0] == `public`: 435 t = tlf.Public 436 case ps[0] == `team`: 437 t = tlf.SingleTeam 438 default: 439 return tlf.Private, "", "", "", errInvalidRemotePath 440 } 441 if len(ps) >= 3 { 442 finalElem = ps[len(ps)-1] 443 middlePath = stdpath.Join(ps[2 : len(ps)-1]...) 444 } 445 return t, ps[1], middlePath, finalElem, nil 446 } 447 448 func (k *SimpleFS) branchNameFromPath( 449 ctx context.Context, tlfHandle *tlfhandle.Handle, path keybase1.Path) ( 450 data.BranchName, error) { 451 pt, err := path.PathType() 452 if err != nil { 453 return "", err 454 } 455 switch pt { 456 case keybase1.PathType_KBFS: 457 if tlfHandle.IsLocalConflict() { 458 b, ok := data.MakeConflictBranchName(tlfHandle) 459 if ok { 460 return b, nil 461 } 462 } 463 return data.MasterBranch, nil 464 case keybase1.PathType_KBFS_ARCHIVED: 465 archivedParam := path.KbfsArchived().ArchivedParam 466 archivedType, err := archivedParam.KBFSArchivedType() 467 if err != nil { 468 return "", err 469 } 470 switch archivedType { 471 case keybase1.KBFSArchivedType_REVISION: 472 return data.MakeRevBranchName( 473 kbfsmd.Revision(archivedParam.Revision())), nil 474 case keybase1.KBFSArchivedType_TIME: 475 t := keybase1.FromTime(archivedParam.Time()) 476 rev, err := libkbfs.GetMDRevisionByTime(ctx, k.config, tlfHandle, t) 477 if err != nil { 478 return "", err 479 } 480 return data.MakeRevBranchName(rev), nil 481 case keybase1.KBFSArchivedType_TIME_STRING: 482 t := archivedParam.TimeString() 483 rev, err := libfs.RevFromTimeString(ctx, k.config, tlfHandle, t) 484 if err != nil { 485 return "", err 486 } 487 return data.MakeRevBranchName(rev), nil 488 case keybase1.KBFSArchivedType_REL_TIME_STRING: 489 t := archivedParam.RelTimeString() 490 rev, err := libfs.RevFromRelativeTimeString( 491 ctx, k.config, tlfHandle, t) 492 if err != nil { 493 return "", err 494 } 495 return data.MakeRevBranchName(rev), nil 496 default: 497 return "", simpleFSError{reason: "Invalid archived type for branch name"} 498 } 499 default: 500 return "", simpleFSError{reason: "Invalid path type for branch name"} 501 } 502 } 503 504 func (k *SimpleFS) getKBPKI(ctx context.Context) (libkbfs.KBPKI, error) { 505 // Just make sure KBPKI exists before we try to use it. It 506 // actually gets created after SimpleFS registers as an RPC 507 // service.. 508 kbpki := k.config.KBPKI() 509 for kbpki == nil { 510 t := time.NewTimer(250 * time.Millisecond) 511 select { 512 case <-t.C: 513 kbpki = k.config.KBPKI() 514 case <-ctx.Done(): 515 _ = t.Stop() 516 return nil, ctx.Err() 517 } 518 } 519 return kbpki, nil 520 } 521 522 func (k *SimpleFS) getFSWithMaybeCreate( 523 ctx context.Context, path keybase1.Path, create bool) ( 524 fs billy.Filesystem, finalElem string, err error) { 525 pt, err := path.PathType() 526 if err != nil { 527 return nil, "", err 528 } 529 switch pt { 530 case keybase1.PathType_KBFS, keybase1.PathType_KBFS_ARCHIVED: 531 // Check for the root FS first. 532 ps, err := splitPathFromKbfsPath(path) 533 if err != nil { 534 return nil, "", err 535 } 536 if len(ps) < 2 || len(ps) < 3 && strings.HasPrefix(ps[0], ".kbfs_") { 537 fs = libfs.NewRootFS(k.config) 538 if len(ps) == 1 { 539 finalElem = ps[0] 540 } else if len(ps) == 2 { 541 fs, err = fs.Chroot(ps[0]) 542 if err != nil { 543 return nil, "", err 544 } 545 finalElem = ps[1] 546 } 547 return fs, finalElem, nil 548 } 549 550 t, tlfName, restOfPath, finalElem, err := remoteTlfAndPath(path) 551 if err != nil { 552 return nil, "", err 553 } 554 kbpki, err := k.getKBPKI(ctx) 555 if err != nil { 556 return nil, "", err 557 } 558 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 559 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 560 if err != nil { 561 return nil, "", err 562 } 563 branch, err := k.branchNameFromPath(ctx, tlfHandle, path) 564 if err != nil { 565 return nil, "", err 566 } 567 fs, err := k.newFS( 568 ctx, k.config, tlfHandle, branch, restOfPath, create) 569 if err != nil { 570 if exitEarly, _ := libfs.FilterTLFEarlyExitError( 571 ctx, err, k.log, tlfHandle.GetCanonicalName()); exitEarly { 572 return nil, finalElem, libfs.TlfDoesNotExist{} 573 } 574 return nil, "", err 575 } 576 if create { 577 err = k.checkEmptySubscription(ctx, path) 578 if err != nil { 579 return nil, "", err 580 } 581 } 582 return fs, finalElem, nil 583 case keybase1.PathType_LOCAL: 584 fs = osfs.New(stdpath.Dir(path.Local())) 585 return fs, stdpath.Base(path.Local()), nil 586 default: 587 return nil, "", simpleFSError{reason: "Invalid path type"} 588 } 589 } 590 591 func (k *SimpleFS) getFS( 592 ctx context.Context, path keybase1.Path) ( 593 fs billy.Filesystem, finalElem string, err error) { 594 return k.getFSWithMaybeCreate(ctx, path, true) 595 } 596 597 func (k *SimpleFS) getFSIfExists( 598 ctx context.Context, path keybase1.Path) ( 599 fs billy.Filesystem, finalElem string, err error) { 600 return k.getFSWithMaybeCreate(ctx, path, false) 601 } 602 603 func deTy2Ty(et data.EntryType) keybase1.DirentType { 604 switch et { 605 case data.Exec: 606 return keybase1.DirentType_EXEC 607 case data.File: 608 return keybase1.DirentType_FILE 609 case data.Dir: 610 return keybase1.DirentType_DIR 611 case data.Sym: 612 return keybase1.DirentType_SYM 613 } 614 panic("deTy2Ty unreachable") 615 } 616 617 func (k *SimpleFS) favoriteList(ctx context.Context, t tlf.Type) ([]keybase1.Dirent, error) { 618 kbpki, err := k.getKBPKI(ctx) 619 if err != nil { 620 return nil, err 621 } 622 session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true) 623 if err != nil { 624 return nil, err 625 } 626 // Return empty directory listing if we are not logged in. 627 if session.UID.IsNil() { 628 return nil, nil 629 } 630 631 k.config.GetPerfLog().CDebugf(ctx, "GetFavorites simplefs.favoriteList") 632 favs, err := k.config.KBFSOps().GetFavorites(ctx) 633 if err != nil { 634 return nil, err 635 } 636 637 res := make([]keybase1.Dirent, 0, len(favs)) 638 for _, fav := range favs { 639 if fav.Type != t { 640 continue 641 } 642 pname, err := tlf.CanonicalToPreferredName( 643 session.Name, tlf.CanonicalName(fav.Name)) 644 if err != nil { 645 k.log.Errorf("CanonicalToPreferredName: %q %v", fav.Name, err) 646 continue 647 } 648 res = append(res, keybase1.Dirent{}) 649 res[len(res)-1].Name = string(pname) 650 res[len(res)-1].DirentType = deTy2Ty(data.Dir) 651 652 handle, err := tlfhandle.ParseHandlePreferredQuick( 653 ctx, kbpki, k.config, string(pname), t) 654 if err != nil { 655 k.log.Errorf("ParseTlfHandlePreferredQuick: %s %q %v", t, pname, err) 656 continue 657 } 658 res[len(res)-1].Writable, err = libfs.IsWriter( 659 ctx, kbpki, k.config, handle) 660 if err != nil { 661 k.log.Errorf("libfs.IsWriter: %q %+v", pname, err) 662 continue 663 } 664 } 665 return res, nil 666 } 667 668 func (k *SimpleFS) setStat( 669 de *keybase1.Dirent, fi os.FileInfo, linkFS billy.Filesystem) error { 670 de.Time = keybase1.ToTime(fi.ModTime()) 671 de.Size = int(fi.Size()) // TODO: FIX protocol 672 673 t := data.File 674 switch { 675 case fi.IsDir(): 676 t = data.Dir 677 case fi.Mode()&0100 != 0: 678 t = data.Exec 679 case fi.Mode()&os.ModeSymlink != 0: 680 t = data.Sym 681 682 link, err := linkFS.Readlink(fi.Name()) 683 if err != nil { 684 return err 685 } 686 de.SymlinkTarget = link 687 } 688 de.DirentType = deTy2Ty(t) 689 de.Writable = (fi.Mode()&0222 != 0) 690 691 if lwg, ok := fi.Sys().(libfs.KBFSMetadataForSimpleFSGetter); ok { 692 md, err := lwg.KBFSMetadataForSimpleFS() 693 if err != nil { 694 return err 695 } 696 de.LastWriterUnverified = md.LastWriter 697 de.PrefetchStatus = md.PrefetchStatus 698 de.PrefetchProgress = md.PrefetchProgress.ToProtocolProgress( 699 k.config.Clock()) 700 } 701 de.Name = fi.Name() 702 return nil 703 } 704 705 func (k *SimpleFS) setResult(opid keybase1.OpID, val interface{}) { 706 k.lock.Lock() 707 k.handles[opid] = &handle{async: val} 708 k.lock.Unlock() 709 } 710 711 func (k *SimpleFS) startOp(ctx context.Context, opid keybase1.OpID, 712 opType keybase1.AsyncOps, desc keybase1.OpDescription) ( 713 _ context.Context, w *inprogress, err error) { 714 ctx = k.makeContext(ctx) 715 ctx, cancel := context.WithCancel(ctx) 716 w = &inprogress{ 717 desc, 718 cancel, 719 make(chan error, 1), 720 keybase1.OpProgress{OpType: opType}, 721 } 722 k.lock.Lock() 723 k.inProgress[opid] = w 724 k.lock.Unlock() 725 // ignore error, this is just for logging. 726 descBS, _ := json.Marshal(desc) 727 k.vlog.CLogf(ctx, libkb.VLog1, "start %X %s", opid, descBS) 728 newCtx, err := k.startOpWrapContext(ctx) 729 return newCtx, w, err 730 } 731 732 func (k *SimpleFS) doneOp(ctx context.Context, opid keybase1.OpID, w *inprogress, err error) { 733 // We aren't accessing w.progress directionly but w can still be in there 734 // so is still protected by the lock. 735 k.lock.Lock() 736 w.progress.EndEstimate = keybase1.ToTime(k.config.Clock().Now()) 737 k.lock.Unlock() 738 739 w.done <- err 740 close(w.done) 741 k.log.CDebugf(ctx, "done op %X, status=%+v", opid, err) 742 if ctx != nil { 743 err := libcontext.CleanupCancellationDelayer(ctx) 744 if err != nil { 745 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 746 } 747 } 748 } 749 750 func (k *SimpleFS) startAsync( 751 ctx context.Context, opid keybase1.OpID, opType keybase1.AsyncOps, 752 desc keybase1.OpDescription, 753 path1ForIdentifyBehavior *keybase1.Path, 754 path2ForIdentifyBehavior *keybase1.Path, 755 callback func(context.Context) error) (err error) { 756 ctxAsync, w, e0 := k.startOp(context.Background(), opid, opType, desc) 757 if e0 != nil { 758 return e0 759 } 760 ctxAsync, err = populateIdentifyBehaviorIfNeeded( 761 ctxAsync, path1ForIdentifyBehavior, path2ForIdentifyBehavior) 762 if err != nil { 763 return err 764 } 765 // Bind the old context to the new context, for debugging purposes. 766 k.log.CDebugf(ctx, "Launching new async operation with SFSID=%s", 767 ctxAsync.Value(ctxIDKey)) 768 go func() { 769 var err error 770 // Capture the inprogress reference here rather than let doneOp 771 // retrieve it when called, so that doneOp always has it when it's 772 // called. This is needed when SimpleFSCancel is called when a 773 // SimpleFSWait is already in the air. Since SimpleFSCancel deletes the 774 // inprogress object from k.inProgress, doneOp wouldn't be able to get 775 // the object when it's called. To make sure SimpleFSWait returns and 776 // returns the correct error, we just pass in the inprogress reference 777 // here. 778 defer func() { k.doneOp(ctxAsync, opid, w, err) }() 779 err = callback(ctxAsync) 780 if err != nil { 781 k.log.CDebugf(ctxAsync, "Error making async callback: %+v", err) 782 } 783 }() 784 return nil 785 } 786 787 func (k *SimpleFS) setProgressTotals( 788 opid keybase1.OpID, totalBytes, totalFiles int64) { 789 k.lock.Lock() 790 defer k.lock.Unlock() 791 w, ok := k.inProgress[opid] 792 if !ok { 793 return 794 } 795 w.progress.BytesTotal = totalBytes 796 w.progress.FilesTotal = totalFiles 797 w.progress.Start = keybase1.ToTime(k.config.Clock().Now()) 798 } 799 800 func (k *SimpleFS) updateReadProgress( 801 opid keybase1.OpID, readBytes, readFiles int64) { 802 k.lock.Lock() 803 defer k.lock.Unlock() 804 w, ok := k.inProgress[opid] 805 if !ok { 806 return 807 } 808 w.progress.BytesRead += readBytes 809 if w.progress.BytesRead > w.progress.BytesTotal { 810 // Our original total was wrong or we didn't get one. 811 w.progress.BytesTotal = w.progress.BytesRead 812 } 813 w.progress.FilesRead += readFiles 814 if w.progress.FilesRead > w.progress.FilesTotal { 815 // Our original total was wrong or we didn't get one. 816 w.progress.FilesTotal = w.progress.FilesRead 817 } 818 } 819 820 func (k *SimpleFS) updateWriteProgress( 821 opid keybase1.OpID, wroteBytes, wroteFiles int64) { 822 k.lock.Lock() 823 defer k.lock.Unlock() 824 w, ok := k.inProgress[opid] 825 if !ok { 826 return 827 } 828 w.progress.BytesWritten += wroteBytes 829 if w.progress.BytesWritten > w.progress.BytesTotal { 830 // Our original total was wrong or we didn't get one. 831 w.progress.BytesTotal = w.progress.BytesWritten 832 } 833 w.progress.FilesWritten += wroteFiles 834 if w.progress.FilesWritten > w.progress.FilesTotal { 835 // Our original total was wrong or we didn't get one. 836 w.progress.FilesTotal = w.progress.FilesWritten 837 } 838 } 839 840 var filesToIgnore = map[string]bool{ 841 ".Trashes": true, 842 ".fseventsd": true, 843 ".DS_Store": true, 844 } 845 var prefixesToIgnore = []string{"._"} 846 847 func isFiltered(filter keybase1.ListFilter, name string) bool { 848 switch filter { 849 case keybase1.ListFilter_NO_FILTER: 850 return false 851 case keybase1.ListFilter_FILTER_ALL_HIDDEN: 852 return strings.HasPrefix(name, ".") 853 case keybase1.ListFilter_FILTER_SYSTEM_HIDDEN: 854 if filesToIgnore[name] { 855 return true 856 } 857 for _, prefix := range prefixesToIgnore { 858 if strings.HasPrefix(name, prefix) { 859 return true 860 } 861 } 862 return false 863 } 864 return false 865 } 866 867 func (k *SimpleFS) getFolderBranchFromPath( 868 ctx context.Context, path keybase1.Path) ( 869 data.FolderBranch, string, error) { 870 t, tlfName, _, _, err := remoteTlfAndPath(path) 871 if err != nil { 872 return data.FolderBranch{}, "", err 873 } 874 kbpki, err := k.getKBPKI(ctx) 875 if err != nil { 876 return data.FolderBranch{}, "", err 877 } 878 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 879 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 880 if err != nil { 881 return data.FolderBranch{}, "", err 882 } 883 884 // Get the root node first to initialize the TLF. 885 node, _, err := k.config.KBFSOps().GetRootNode( 886 ctx, tlfHandle, data.MasterBranch) 887 if err != nil { 888 return data.FolderBranch{}, "", err 889 } 890 if node == nil { 891 return data.FolderBranch{}, tlfHandle.GetCanonicalPath(), nil 892 } 893 return node.GetFolderBranch(), tlfHandle.GetCanonicalPath(), nil 894 } 895 896 func (k *SimpleFS) refreshSubscriptionLocked( 897 ctx context.Context, path keybase1.Path, tlfPathFromGUI string) error { 898 // TODO: when favorites caching is ready, handle folder-list paths 899 // like `/keybase/private` here. 900 901 fb, _, err := k.getFolderBranchFromPath(ctx, path) 902 if err != nil { 903 return err 904 } 905 if fb == (data.FolderBranch{}) { 906 k.log.CDebugf( 907 ctx, "Ignoring subscription for empty TLF %q", path) 908 k.subscribeToEmptyTlf = tlfPathFromGUI 909 return nil 910 } 911 912 if k.subscribeCurrFB == fb { 913 k.subscribeToEmptyTlf = "" 914 return nil 915 } 916 917 if k.subscribeCurrFB != (data.FolderBranch{}) { 918 err = k.config.Notifier().UnregisterFromChanges( 919 []data.FolderBranch{k.subscribeCurrFB}, k) 920 if err != nil { 921 return err 922 } 923 } 924 925 k.log.CDebugf(ctx, "Subscribing to %s", tlfPathFromGUI) 926 err = k.config.Notifier().RegisterForChanges( 927 []data.FolderBranch{fb}, k) 928 if err != nil { 929 return err 930 } 931 // We are subscribing on TLF level anyway, so just use TLF path when 932 // notifying GUI. 933 k.subscribeCurrTlfPathFromGUI = tlfPathFromGUI 934 k.subscribeCurrFB = fb 935 k.subscribeToEmptyTlf = "" 936 return nil 937 } 938 939 func tlfNameFromPath(path keybase1.Path) (string, error) { 940 pType, err := path.PathType() 941 if err != nil { 942 return "", err 943 } 944 if pType != keybase1.PathType_KBFS { 945 return "", nil 946 } 947 948 tlfType, tlfNameFromGUI, _, _, err := remoteTlfAndPath(path) 949 if err != nil { 950 return "", err 951 } 952 return tlfhandle.BuildCanonicalPathForTlfType( 953 tlfType, tlfNameFromGUI), nil 954 } 955 956 func (k *SimpleFS) refreshSubscription( 957 ctx context.Context, path keybase1.Path) error { 958 tlfPathFromGUI, err := tlfNameFromPath(path) 959 if err != nil { 960 return err 961 } 962 if tlfPathFromGUI == "" { 963 k.log.CDebugf(ctx, "Ignoring subscription for path %s", path) 964 return nil 965 } 966 967 k.subscribeLock.Lock() 968 defer k.subscribeLock.Unlock() 969 return k.refreshSubscriptionLocked(ctx, path, tlfPathFromGUI) 970 } 971 972 func (k *SimpleFS) checkEmptySubscription( 973 ctx context.Context, path keybase1.Path) error { 974 k.subscribeLock.Lock() 975 defer k.subscribeLock.Unlock() 976 if k.subscribeToEmptyTlf == "" { 977 // Fast path. 978 return nil 979 } 980 981 tlfPathFromGUI, err := tlfNameFromPath(path) 982 if err != nil { 983 return err 984 } 985 if tlfPathFromGUI == "" { 986 return nil 987 } 988 989 if k.subscribeToEmptyTlf != tlfPathFromGUI { 990 return nil 991 } 992 993 k.log.CDebugf( 994 ctx, "Trying to subscribe to %s, which was previously empty", 995 tlfPathFromGUI) 996 return k.refreshSubscriptionLocked(ctx, path, tlfPathFromGUI) 997 } 998 999 // SimpleFSList - Begin list of items in directory at path 1000 // Retrieve results with readList() 1001 // Cannot be a single file to get flags/status, 1002 // must be a directory. 1003 func (k *SimpleFS) SimpleFSList(ctx context.Context, arg keybase1.SimpleFSListArg) (err error) { 1004 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST, 1005 keybase1.NewOpDescriptionWithList( 1006 keybase1.ListArgs{ 1007 OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter, 1008 }), 1009 &arg.Path, nil, 1010 func(ctx context.Context) (err error) { 1011 defer func() { err = translateErr(err) }() 1012 var res []keybase1.Dirent 1013 1014 rawPath, err := rawPathFromKbfsPath(arg.Path) 1015 if err != nil { 1016 return err 1017 } 1018 switch { 1019 case rawPath == "/": 1020 res = []keybase1.Dirent{ 1021 {Name: "private", DirentType: deTy2Ty(data.Dir)}, 1022 {Name: "public", DirentType: deTy2Ty(data.Dir)}, 1023 {Name: "team", DirentType: deTy2Ty(data.Dir)}, 1024 } 1025 case rawPath == `/public`: 1026 res, err = k.favoriteList(ctx, tlf.Public) 1027 case rawPath == `/private`: 1028 res, err = k.favoriteList(ctx, tlf.Private) 1029 case rawPath == `/team`: 1030 res, err = k.favoriteList(ctx, tlf.SingleTeam) 1031 default: 1032 fs, finalElem, err := k.getFSIfExists(ctx, arg.Path) 1033 switch errors.Cause(err).(type) { 1034 case nil: 1035 case libfs.TlfDoesNotExist: 1036 // TLF doesn't exist yet; just return an empty result. 1037 k.setResult(arg.OpID, keybase1.SimpleFSListResult{}) 1038 return nil 1039 default: 1040 return err 1041 } 1042 1043 if arg.RefreshSubscription { 1044 // TODO: move this higher when we handle 1045 // subscribing to the favorites list. 1046 err = k.refreshSubscription(ctx, arg.Path) 1047 if err != nil { 1048 return err 1049 } 1050 } 1051 1052 // With listing, we don't know the totals ahead of time, 1053 // so just start with a 0 total. 1054 k.setProgressTotals(arg.OpID, 0, 0) 1055 finalElemFI, err := fs.Lstat(finalElem) 1056 if err != nil { 1057 return err 1058 } 1059 var fis []os.FileInfo 1060 linkFS := fs 1061 if finalElemFI.IsDir() { 1062 fis, err = fs.ReadDir(finalElem) 1063 if err != nil { 1064 return err 1065 } 1066 linkFS, err = fs.Chroot(finalElem) 1067 if err != nil { 1068 return err 1069 } 1070 } else { 1071 fis = append(fis, finalElemFI) 1072 } 1073 for _, fi := range fis { 1074 if finalElemFI.IsDir() && 1075 isFiltered(arg.Filter, fi.Name()) { 1076 continue 1077 } 1078 1079 var d keybase1.Dirent 1080 err := k.setStat(&d, fi, linkFS) 1081 if err != nil { 1082 return err 1083 } 1084 res = append(res, d) 1085 } 1086 k.updateReadProgress(arg.OpID, 0, int64(len(fis))) 1087 } 1088 if err != nil { 1089 return err 1090 } 1091 k.setResult(arg.OpID, keybase1.SimpleFSListResult{Entries: res}) 1092 return nil 1093 }) 1094 } 1095 1096 // listRecursiveToDepthAsync returns a function that recursively lists folders, 1097 // up to a given depth. A depth of -1 is treated as unlimited. The function 1098 // also updates progress for the passed-in opID as it progresses, and then sets 1099 // the result for the opID when it completes. 1100 // 1101 // TODO: refactor SimpleFSList to use this too (finalDepth = 0) 1102 func (k *SimpleFS) listRecursiveToDepth(opID keybase1.OpID, 1103 path keybase1.Path, filter keybase1.ListFilter, 1104 finalDepth int, refreshSubscription bool) func(context.Context) error { 1105 return func(ctx context.Context) (err error) { 1106 defer func() { err = translateErr(err) }() 1107 // A stack of paths to process - ordering does not matter. 1108 // Here we don't walk symlinks, so no loops possible. 1109 type pathStackElem struct { 1110 path string 1111 depth int 1112 } 1113 var paths []pathStackElem 1114 1115 fs, finalElem, err := k.getFSIfExists(ctx, path) 1116 switch errors.Cause(err).(type) { 1117 case nil: 1118 case libfs.TlfDoesNotExist: 1119 // TLF doesn't exist yet; just return an empty result. 1120 k.setResult(opID, keybase1.SimpleFSListResult{}) 1121 return nil 1122 default: 1123 return err 1124 } 1125 1126 if refreshSubscription { 1127 err = k.refreshSubscription(ctx, path) 1128 if err != nil { 1129 return err 1130 } 1131 } 1132 1133 // With listing, we don't know the totals ahead of time, 1134 // so just start with a 0 total. 1135 k.setProgressTotals(opID, 0, 0) 1136 fi, err := fs.Lstat(finalElem) 1137 if err != nil { 1138 return err 1139 } 1140 var des []keybase1.Dirent 1141 if !fi.IsDir() { 1142 var d keybase1.Dirent 1143 err := k.setStat(&d, fi, fs) 1144 if err != nil { 1145 return err 1146 } 1147 d.Name = finalElem 1148 des = append(des, d) 1149 // Leave paths empty so we can skip the loop below. 1150 } else { 1151 // Start with a depth of 0. 1152 // A TLF root will have a `finalElem` of "". 1153 // A subdirectory will have a `finalElem` of just the name. 1154 paths = append(paths, pathStackElem{finalElem, 0}) 1155 } 1156 1157 for len(paths) > 0 { 1158 // Take last element and shorten. 1159 pathElem := paths[len(paths)-1] 1160 paths = paths[:len(paths)-1] 1161 pathName := "" 1162 if pathElem.path != finalElem { 1163 pathName = strings.TrimPrefix(pathElem.path, finalElem+"/") 1164 } 1165 1166 fis, err := fs.ReadDir(pathElem.path) 1167 if err != nil { 1168 return err 1169 } 1170 linkFS, err := fs.Chroot(pathElem.path) 1171 if err != nil { 1172 return err 1173 } 1174 for _, fi := range fis { 1175 // We can only get here if we're listing a 1176 // directory, not a single file, so we should 1177 // always filter. 1178 if isFiltered(filter, fi.Name()) { 1179 continue 1180 } 1181 1182 var de keybase1.Dirent 1183 err := k.setStat(&de, fi, linkFS) 1184 if err != nil { 1185 return err 1186 } 1187 de.Name = stdpath.Join(pathName, fi.Name()) 1188 des = append(des, de) 1189 // Only recurse if the caller requested infinite depth (-1), or 1190 // if the current path has a depth less than the desired final 1191 // depth of recursion. 1192 if fi.IsDir() && (finalDepth == -1 || pathElem.depth < finalDepth) { 1193 paths = append(paths, pathStackElem{stdpath.Join(pathElem.path, fi.Name()), pathElem.depth + 1}) 1194 } 1195 } 1196 k.updateReadProgress(opID, 0, int64(len(fis))) 1197 } 1198 k.setResult(opID, keybase1.SimpleFSListResult{Entries: des}) 1199 1200 return nil 1201 } 1202 } 1203 1204 // SimpleFSListRecursiveToDepth - Begin recursive list of items in directory at 1205 // path up to a given depth. 1206 func (k *SimpleFS) SimpleFSListRecursiveToDepth( 1207 ctx context.Context, arg keybase1.SimpleFSListRecursiveToDepthArg) (err error) { 1208 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST_RECURSIVE_TO_DEPTH, 1209 keybase1.NewOpDescriptionWithListRecursiveToDepth( 1210 keybase1.ListToDepthArgs{ 1211 OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter, Depth: arg.Depth, 1212 }), 1213 &arg.Path, nil, 1214 k.listRecursiveToDepth(arg.OpID, arg.Path, arg.Filter, arg.Depth, arg.RefreshSubscription), 1215 ) 1216 } 1217 1218 // SimpleFSListRecursive - Begin recursive list of items in directory at path 1219 func (k *SimpleFS) SimpleFSListRecursive( 1220 ctx context.Context, arg keybase1.SimpleFSListRecursiveArg) (err error) { 1221 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_LIST_RECURSIVE, 1222 keybase1.NewOpDescriptionWithListRecursive( 1223 keybase1.ListArgs{ 1224 OpID: arg.OpID, Path: arg.Path, Filter: arg.Filter, 1225 }), 1226 &arg.Path, nil, 1227 k.listRecursiveToDepth(arg.OpID, arg.Path, arg.Filter, -1, arg.RefreshSubscription), 1228 ) 1229 } 1230 1231 // SimpleFSReadList - Get list of Paths in progress. Can indicate status of pending 1232 // to get more entries. 1233 func (k *SimpleFS) SimpleFSReadList(_ context.Context, opid keybase1.OpID) (keybase1.SimpleFSListResult, error) { 1234 k.lock.Lock() 1235 res := k.handles[opid] 1236 var x interface{} 1237 if res != nil { 1238 x = res.async 1239 res.async = nil 1240 } 1241 k.lock.Unlock() 1242 1243 lr, ok := x.(keybase1.SimpleFSListResult) 1244 if !ok { 1245 return keybase1.SimpleFSListResult{}, errNoResult 1246 } 1247 1248 return lr, nil 1249 } 1250 1251 // SimpleFSListFavorites lists the favorite, new, 1252 // and ignored folders of the logged in user, 1253 // getting its data from the KBFS Favorites cache. If the cache is stale, 1254 // this will trigger a network request. 1255 func (k *SimpleFS) SimpleFSListFavorites(ctx context.Context) ( 1256 keybase1.FavoritesResult, error) { 1257 kbpki, err := k.getKBPKI(ctx) 1258 if err != nil { 1259 return keybase1.FavoritesResult{}, err 1260 } 1261 session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true) 1262 if err != nil { 1263 return keybase1.FavoritesResult{}, err 1264 } 1265 if session.UID.IsNil() { 1266 return keybase1.FavoritesResult{}, nil 1267 } 1268 1269 k.config.GetPerfLog().CDebugf(ctx, "GetFavorites simplefs.SimpleFSListFavorites") 1270 return k.config.KBFSOps().GetFavoritesAll(ctx) 1271 } 1272 1273 func recursiveByteAndFileCount(fs billy.Filesystem) ( 1274 bytes, files int64, err error) { 1275 fileInfos, err := fs.ReadDir("/") 1276 if err != nil { 1277 return 0, 0, err 1278 } 1279 1280 for _, fi := range fileInfos { 1281 if fi.IsDir() { 1282 if fi.Name() == "." { 1283 continue 1284 } 1285 chrootFS, err := fs.Chroot(fi.Name()) 1286 if err != nil { 1287 return 0, 0, err 1288 } 1289 chrootBytes, chrootFiles, err := recursiveByteAndFileCount(chrootFS) 1290 if err != nil { 1291 return 0, 0, err 1292 } 1293 bytes += chrootBytes 1294 files += chrootFiles 1295 } else { 1296 bytes += fi.Size() 1297 } 1298 files++ 1299 } 1300 return bytes, files, nil 1301 } 1302 1303 func copyWithCancellation(ctx context.Context, dst io.Writer, src io.Reader) error { 1304 for { 1305 select { 1306 case <-ctx.Done(): 1307 return ctx.Err() 1308 default: 1309 } 1310 _, err := io.CopyN(dst, src, 64*1024) 1311 if err == io.EOF { 1312 return nil 1313 } 1314 if err != nil { 1315 return err 1316 } 1317 } 1318 } 1319 1320 type progressReader struct { 1321 k *SimpleFS 1322 opID keybase1.OpID 1323 input io.Reader 1324 } 1325 1326 var _ io.Reader = (*progressReader)(nil) 1327 1328 func (pr *progressReader) Read(p []byte) (n int, err error) { 1329 n, err = pr.input.Read(p) 1330 if n > 0 { 1331 // Update read progress, even for errors. 1332 pr.k.updateReadProgress(pr.opID, int64(n), 0) 1333 } 1334 return n, err 1335 } 1336 1337 type progressWriter struct { 1338 k *SimpleFS 1339 opID keybase1.OpID 1340 output io.Writer 1341 } 1342 1343 var _ io.Writer = (*progressWriter)(nil) 1344 1345 func (pw *progressWriter) Write(p []byte) (n int, err error) { 1346 n, err = pw.output.Write(p) 1347 if n > 0 { 1348 // Update write progress, even for errors. 1349 pw.k.updateWriteProgress(pw.opID, int64(n), 0) 1350 } 1351 return n, err 1352 } 1353 1354 func (k *SimpleFS) doCopyFromSource( 1355 ctx context.Context, opID keybase1.OpID, 1356 srcFS billy.Filesystem, srcFI os.FileInfo, 1357 dstPath keybase1.Path, dstFS billy.Filesystem, 1358 finalDstElem string, overwriteExistingFiles bool) (err error) { 1359 defer func() { 1360 if err == nil { 1361 k.updateReadProgress(opID, 0, 1) 1362 k.updateWriteProgress(opID, 0, 1) 1363 } 1364 }() 1365 1366 if srcFI.IsDir() { 1367 return dstFS.MkdirAll(finalDstElem, 0755) 1368 } 1369 1370 src, err := srcFS.Open(srcFI.Name()) 1371 if err != nil { 1372 return err 1373 } 1374 defer src.Close() 1375 1376 mode := os.O_RDWR | os.O_CREATE | os.O_EXCL 1377 if overwriteExistingFiles { 1378 mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC 1379 } 1380 dst, err := dstFS.OpenFile(finalDstElem, mode, 0600) 1381 if err != nil { 1382 return err 1383 } 1384 defer dst.Close() 1385 1386 if pathType, _ := dstPath.PathType(); pathType == keybase1.PathType_LOCAL { 1387 defer func() { 1388 qerr := Quarantine(ctx, dstPath.Local()) 1389 if err == nil { 1390 err = qerr 1391 } 1392 }() 1393 } 1394 1395 err = copyWithCancellation( 1396 ctx, 1397 &progressWriter{k, opID, dst}, 1398 &progressReader{k, opID, src}, 1399 ) 1400 return err 1401 } 1402 1403 func (k *SimpleFS) doCopy( 1404 ctx context.Context, opID keybase1.OpID, 1405 srcPath, destPath keybase1.Path, overwriteExistingFiles bool) (err error) { 1406 // Note this is also used by move, so if this changes update SimpleFSMove 1407 // code also. 1408 srcFS, finalSrcElem, err := k.getFS(ctx, srcPath) 1409 if err != nil { 1410 return err 1411 } 1412 srcFI, err := srcFS.Stat(finalSrcElem) 1413 if err != nil { 1414 return err 1415 } 1416 if srcFI.IsDir() { 1417 // The byte count for making a single directory is meaningless. 1418 k.setProgressTotals(opID, 0, 1) 1419 } else { 1420 k.setProgressTotals(opID, srcFI.Size(), 1) 1421 } 1422 destFS, finalDestElem, err := k.getFS(ctx, destPath) 1423 if err != nil { 1424 return err 1425 } 1426 1427 return k.doCopyFromSource(ctx, opID, 1428 srcFS, srcFI, destPath, destFS, finalDestElem, overwriteExistingFiles) 1429 } 1430 1431 // SimpleFSCopy - Begin copy of file or directory 1432 func (k *SimpleFS) SimpleFSCopy( 1433 ctx context.Context, arg keybase1.SimpleFSCopyArg) (err error) { 1434 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_COPY, 1435 keybase1.NewOpDescriptionWithCopy(keybase1.CopyArgs(arg)), 1436 &arg.Src, &arg.Dest, 1437 func(ctx context.Context) (err error) { 1438 defer func() { err = translateErr(err) }() 1439 return k.doCopy(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles) 1440 }) 1441 } 1442 1443 // SimpleFSSymlink starts making a symlink of a file or directory 1444 func (k *SimpleFS) SimpleFSSymlink( 1445 ctx context.Context, arg keybase1.SimpleFSSymlinkArg) (err error) { 1446 // This is not async. 1447 ctx, err = k.startSyncOp(ctx, "Symlink", arg, &arg.Link, nil) 1448 if err != nil { 1449 return err 1450 } 1451 defer func() { k.doneSyncOp(ctx, err) }() 1452 1453 destFS, finalDestElem, err := k.getFS(ctx, arg.Link) 1454 if err != nil { 1455 return err 1456 } 1457 1458 err = destFS.Symlink(arg.Target, finalDestElem) 1459 return err 1460 } 1461 1462 type copyNode struct { 1463 dest keybase1.Path 1464 srcFS, destFS billy.Filesystem 1465 srcFinalElem, destFinalElem string 1466 } 1467 1468 func pathAppend(p keybase1.Path, leaf string) keybase1.Path { 1469 switch { 1470 case p.Local__ != nil: 1471 var s = stdpath.Join(*p.Local__, leaf) 1472 p.Local__ = &s 1473 case p.Kbfs__ != nil: 1474 var s = stdpath.Join(p.Kbfs__.Path, leaf) 1475 p = p.DeepCopy() 1476 p.Kbfs__.Path = s 1477 case p.KbfsArchived__ != nil: 1478 var s = stdpath.Join(p.KbfsArchived__.Path, leaf) 1479 p = p.DeepCopy() 1480 p.KbfsArchived__.Path = s 1481 } 1482 return p 1483 } 1484 1485 func (k *SimpleFS) doCopyRecursive(ctx context.Context, 1486 opID keybase1.OpID, src, dest keybase1.Path, overwriteExistingFiles bool) error { 1487 // Get the full byte/file count. 1488 srcFS, finalSrcElem, err := k.getFSIfExists(ctx, src) 1489 if err != nil { 1490 return err 1491 } 1492 srcFI, err := srcFS.Stat(finalSrcElem) 1493 if err != nil { 1494 return err 1495 } 1496 if srcFI.IsDir() { 1497 chrootFS, err := srcFS.Chroot(srcFI.Name()) 1498 if err != nil { 1499 return err 1500 } 1501 bytes, files, err := recursiveByteAndFileCount(chrootFS) 1502 if err != nil { 1503 return err 1504 } 1505 // Add one to files to account for the src dir itself. 1506 k.setProgressTotals(opID, bytes, files+1) 1507 } else { 1508 // No need for recursive. 1509 return k.doCopy(ctx, opID, src, dest, overwriteExistingFiles) 1510 } 1511 1512 destFS, finalDestElem, err := k.getFS(ctx, dest) 1513 if err != nil { 1514 return err 1515 } 1516 1517 var nodes = []copyNode{{ 1518 dest: dest, 1519 srcFS: srcFS, 1520 destFS: destFS, 1521 srcFinalElem: finalSrcElem, 1522 destFinalElem: finalDestElem, 1523 }} 1524 for len(nodes) > 0 { 1525 select { 1526 case <-ctx.Done(): 1527 return ctx.Err() 1528 default: 1529 } 1530 1531 node := nodes[len(nodes)-1] 1532 nodes = nodes[:len(nodes)-1] 1533 1534 srcFI, err := node.srcFS.Stat(node.srcFinalElem) 1535 if err != nil { 1536 return err 1537 } 1538 1539 err = k.doCopyFromSource( 1540 ctx, opID, node.srcFS, srcFI, node.dest, node.destFS, 1541 node.destFinalElem, overwriteExistingFiles) 1542 if err != nil { 1543 return err 1544 } 1545 1546 // TODO symlinks 1547 if srcFI.IsDir() { 1548 fis, err := node.srcFS.ReadDir(srcFI.Name()) 1549 if err != nil { 1550 return err 1551 } 1552 1553 newSrcFS, err := node.srcFS.Chroot(node.srcFinalElem) 1554 if err != nil { 1555 return err 1556 } 1557 1558 newDestFS, err := node.destFS.Chroot(node.destFinalElem) 1559 if err != nil { 1560 return err 1561 } 1562 1563 for _, fi := range fis { 1564 name := fi.Name() 1565 nodes = append(nodes, copyNode{ 1566 dest: pathAppend(node.dest, name), 1567 srcFS: newSrcFS, 1568 destFS: newDestFS, 1569 srcFinalElem: name, 1570 destFinalElem: name, 1571 }) 1572 } 1573 } 1574 } 1575 return err 1576 } 1577 1578 // SimpleFSCopyRecursive - Begin recursive copy of directory 1579 func (k *SimpleFS) SimpleFSCopyRecursive(ctx context.Context, 1580 arg keybase1.SimpleFSCopyRecursiveArg) (err error) { 1581 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_COPY, 1582 keybase1.NewOpDescriptionWithCopy(keybase1.CopyArgs(arg)), 1583 &arg.Src, &arg.Dest, 1584 func(ctx context.Context) (err error) { 1585 defer func() { err = translateErr(err) }() 1586 return k.doCopyRecursive(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles) 1587 }) 1588 } 1589 1590 func (k *SimpleFS) doRemove( 1591 ctx context.Context, path keybase1.Path, recursive bool) error { 1592 fs, finalElem, err := k.getFS(ctx, path) 1593 if err != nil { 1594 return err 1595 } 1596 if !recursive { 1597 if finalElem == "" { 1598 // If this is trying to remove a TLF, use favorite removal 1599 // instead. 1600 if asLibFS, ok := fs.(*libfs.FS); ok { 1601 h := asLibFS.Handle() 1602 return k.config.KBFSOps().DeleteFavorite(ctx, h.ToFavorite()) 1603 } 1604 } 1605 return fs.Remove(finalElem) 1606 } else if finalElem == "" { 1607 // Give a nice error in the case where we're trying to 1608 // recursively delete a TLF. 1609 return errors.Errorf("Cannot recursively delete %s", fs.Root()) 1610 } 1611 fi, err := fs.Lstat(finalElem) 1612 if err != nil { 1613 return err 1614 } 1615 return libfs.RecursiveDelete(ctx, fs, fi) 1616 } 1617 1618 func (k *SimpleFS) pathsForSameTlfMove( 1619 ctx context.Context, src, dest keybase1.Path) ( 1620 sameTlf bool, srcPath, destPath string, tlfHandle *tlfhandle.Handle, 1621 err error) { 1622 srcType, err := src.PathType() 1623 if err != nil { 1624 return false, "", "", nil, err 1625 } 1626 if srcType != keybase1.PathType_KBFS { 1627 return false, "", "", nil, nil 1628 } 1629 destType, err := dest.PathType() 1630 if err != nil { 1631 return false, "", "", nil, err 1632 } 1633 if destType != keybase1.PathType_KBFS { 1634 return false, "", "", nil, nil 1635 } 1636 1637 // They are both KBFS paths -- are they in the same TLF? 1638 srcTlfType, srcTlfName, srcMid, srcFinal, err := remoteTlfAndPath(src) 1639 if err != nil { 1640 return false, "", "", nil, err 1641 } 1642 destTlfType, destTlfName, destMid, destFinal, err := remoteTlfAndPath(dest) 1643 if err != nil { 1644 return false, "", "", nil, err 1645 } 1646 if srcTlfType != destTlfType || srcTlfName != destTlfName { 1647 return false, "", "", nil, nil 1648 } 1649 1650 kbpki, err := k.getKBPKI(ctx) 1651 if err != nil { 1652 return false, "", "", nil, err 1653 } 1654 tlfHandle, err = libkbfs.GetHandleFromFolderNameAndType( 1655 ctx, kbpki, k.config.MDOps(), k.config, srcTlfName, 1656 srcTlfType) 1657 if err != nil { 1658 return false, "", "", nil, err 1659 } 1660 1661 return true, stdpath.Join(srcMid, srcFinal), stdpath.Join(destMid, destFinal), 1662 tlfHandle, nil 1663 } 1664 1665 // SimpleFSMove - Begin move of file or directory, from/to KBFS only 1666 func (k *SimpleFS) SimpleFSMove( 1667 ctx context.Context, arg keybase1.SimpleFSMoveArg) (err error) { 1668 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_MOVE, 1669 keybase1.NewOpDescriptionWithMove(keybase1.MoveArgs(arg)), 1670 &arg.Src, &arg.Dest, 1671 func(ctx context.Context) (err error) { 1672 defer func() { err = translateErr(err) }() 1673 sameTlf, srcPath, destPath, tlfHandle, err := k.pathsForSameTlfMove( 1674 ctx, arg.Src, arg.Dest) 1675 if err != nil { 1676 return err 1677 } 1678 if sameTlf { 1679 k.log.CDebugf(ctx, "Renaming within same TLF: %s", 1680 tlfHandle.GetCanonicalPath()) 1681 fs, err := k.newFS( 1682 ctx, k.config, tlfHandle, data.MasterBranch, "", false) 1683 if err != nil { 1684 return err 1685 } 1686 1687 if !arg.OverwriteExistingFiles { 1688 // If srcPath is a file, use fs.Create to make a 1689 // placeholder to avoid overwriting an existing file. This 1690 // doesn't avoid conflicts when journal flushes, but 1691 // protects from overwriting over an existing file without 1692 // causing conflicts. We don't need to do it for 1693 // directories since libkbfs makes sure we can't rename 1694 // over a non-empty directory. 1695 fi, err := fs.Stat(srcPath) 1696 if err != nil { 1697 return err 1698 } 1699 if !fi.IsDir() { 1700 if f, err := fs.OpenFile(destPath, 1701 os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600); err != nil { 1702 return err 1703 } else if err = f.Close(); err != nil { 1704 return err 1705 } 1706 } 1707 } 1708 1709 return fs.Rename(srcPath, destPath) 1710 } 1711 1712 err = k.doCopyRecursive(ctx, arg.OpID, arg.Src, arg.Dest, arg.OverwriteExistingFiles) 1713 if err != nil { 1714 return err 1715 } 1716 return k.doRemove(ctx, arg.Src, true) 1717 }) 1718 } 1719 1720 func (k *SimpleFS) startSyncOp( 1721 ctx context.Context, name string, logarg interface{}, 1722 path1ForIdentifyBehavior *keybase1.Path, 1723 path2ForIdentifyBehavior *keybase1.Path, 1724 ) (context.Context, error) { 1725 ctx = k.makeContext(ctx) 1726 ctx, err := populateIdentifyBehaviorIfNeeded( 1727 ctx, path1ForIdentifyBehavior, path2ForIdentifyBehavior) 1728 if err != nil { 1729 return nil, err 1730 } 1731 k.vlog.CLogf(ctx, libkb.VLog1, "start sync %s %v", name, logarg) 1732 return k.startOpWrapContext(ctx) 1733 } 1734 func (k *SimpleFS) startOpWrapContext(outer context.Context) (context.Context, error) { 1735 return libcontext.NewContextWithCancellationDelayer(libcontext.NewContextReplayable( 1736 outer, func(c context.Context) context.Context { 1737 return c 1738 })) 1739 } 1740 1741 func (k *SimpleFS) doneSyncOp(ctx context.Context, err error) { 1742 k.log.CDebugf(ctx, "done sync op, status=%+v", err) 1743 if ctx != nil { 1744 err := libcontext.CleanupCancellationDelayer(ctx) 1745 if err != nil { 1746 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 1747 } 1748 } 1749 } 1750 1751 // SimpleFSRename - Rename file or directory, KBFS side only 1752 func (k *SimpleFS) SimpleFSRename( 1753 ctx context.Context, arg keybase1.SimpleFSRenameArg) (err error) { 1754 // This is not async. 1755 ctx, err = k.startSyncOp(ctx, "Rename", arg, &arg.Src, &arg.Dest) 1756 if err != nil { 1757 return err 1758 } 1759 defer func() { k.doneSyncOp(ctx, err) }() 1760 1761 // Get root FS, to be shared by both src and dest. 1762 t, tlfName, restOfSrcPath, finalSrcElem, err := remoteTlfAndPath(arg.Src) 1763 if err != nil { 1764 return err 1765 } 1766 kbpki, err := k.getKBPKI(ctx) 1767 if err != nil { 1768 return err 1769 } 1770 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 1771 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 1772 if err != nil { 1773 return err 1774 } 1775 fs, err := k.newFS( 1776 ctx, k.config, tlfHandle, data.MasterBranch, "", false) 1777 if err != nil { 1778 return err 1779 } 1780 1781 // Make sure src and dest share the same TLF. 1782 tDest, tlfNameDest, restOfDestPath, finalDestElem, err := 1783 remoteTlfAndPath(arg.Dest) 1784 if err != nil { 1785 return err 1786 } 1787 if tDest != t || tlfName != tlfNameDest { 1788 return simpleFSError{reason: "Cannot rename across top-level folders"} 1789 } 1790 1791 err = fs.Rename( 1792 stdpath.Join(restOfSrcPath, finalSrcElem), 1793 stdpath.Join(restOfDestPath, finalDestElem)) 1794 return err 1795 } 1796 1797 // SimpleFSOpen - Create/open a file and leave it open 1798 // or create a directory 1799 // Files must be closed afterwards. 1800 func (k *SimpleFS) SimpleFSOpen( 1801 ctx context.Context, arg keybase1.SimpleFSOpenArg) (err error) { 1802 ctx, err = k.startSyncOp(ctx, "Open", arg, &arg.Dest, nil) 1803 if err != nil { 1804 return err 1805 } 1806 defer func() { k.doneSyncOp(ctx, err) }() 1807 1808 fs, finalElem, err := k.getFS(ctx, arg.Dest) 1809 if err != nil { 1810 return err 1811 } 1812 1813 // Make a directory if needed. This will return `nil` if the 1814 // directory already exists. 1815 if arg.Flags&keybase1.OpenFlags_DIRECTORY != 0 { 1816 return fs.MkdirAll(finalElem, 0755) 1817 } 1818 1819 var cflags = os.O_RDONLY 1820 // This must be first since it writes the flag, not just ORs into it. 1821 if arg.Flags&keybase1.OpenFlags_WRITE != 0 { 1822 cflags = os.O_RDWR 1823 } 1824 if arg.Flags&keybase1.OpenFlags_EXISTING == 0 { 1825 cflags |= os.O_CREATE 1826 } 1827 if arg.Flags&keybase1.OpenFlags_REPLACE != 0 { 1828 cflags |= os.O_TRUNC 1829 } 1830 1831 var cancel context.CancelFunc = func() {} 1832 if libfs, ok := fs.(*libfs.FS); ok { 1833 var fsCtx context.Context 1834 fsCtx, cancel = context.WithCancel(k.makeContext(context.Background())) 1835 fsCtx, err := k.startOpWrapContext(fsCtx) 1836 if err != nil { 1837 return err 1838 } 1839 libfs = libfs.WithContext(fsCtx) 1840 k.log.CDebugf(ctx, "New background context for open: SFSID=%s, OpID=%X", 1841 fsCtx.Value(ctxIDKey), arg.OpID) 1842 fs = libfs 1843 } 1844 1845 f, err := fs.OpenFile(finalElem, cflags, 0644) 1846 if err != nil { 1847 return err 1848 } 1849 1850 k.lock.Lock() 1851 k.handles[arg.OpID] = &handle{file: f, path: arg.Dest, cancel: cancel} 1852 k.lock.Unlock() 1853 1854 return nil 1855 } 1856 1857 // SimpleFSSetStat - Set/clear file bits - only executable for now 1858 func (k *SimpleFS) SimpleFSSetStat( 1859 ctx context.Context, arg keybase1.SimpleFSSetStatArg) (err error) { 1860 ctx, err = k.startSyncOp(ctx, "SetStat", arg, &arg.Dest, nil) 1861 if err != nil { 1862 return err 1863 } 1864 defer func() { k.doneSyncOp(ctx, err) }() 1865 1866 fs, finalElem, err := k.getFS(ctx, arg.Dest) 1867 if err != nil { 1868 return err 1869 } 1870 fi, err := fs.Lstat(finalElem) 1871 if err != nil { 1872 return err 1873 } 1874 1875 mode := fi.Mode() 1876 switch arg.Flag { 1877 case keybase1.DirentType_EXEC: 1878 mode |= 0100 1879 case keybase1.DirentType_FILE: 1880 mode &= 0677 1881 default: 1882 return nil 1883 } 1884 1885 changeFS, ok := fs.(billy.Change) 1886 if !ok { 1887 panic(fmt.Sprintf("Unexpected non-Change FS: %T", fs)) 1888 } 1889 1890 return changeFS.Chmod(finalElem, mode) 1891 } 1892 1893 func (k *SimpleFS) startReadWriteOp( 1894 ctx context.Context, opid keybase1.OpID, opType keybase1.AsyncOps, 1895 desc keybase1.OpDescription) (context.Context, error) { 1896 ctx, err := k.startSyncOp(ctx, desc.AsyncOp__.String(), desc, nil, nil) 1897 if err != nil { 1898 return nil, err 1899 } 1900 k.lock.Lock() 1901 k.inProgress[opid] = &inprogress{ 1902 desc, 1903 func() {}, 1904 make(chan error, 1), 1905 keybase1.OpProgress{OpType: opType}, 1906 } 1907 k.lock.Unlock() 1908 return ctx, err 1909 } 1910 1911 func (k *SimpleFS) doneReadWriteOp(ctx context.Context, opID keybase1.OpID, err error) { 1912 k.lock.Lock() 1913 // Read/write ops never set the end estimate since the progress is 1914 // just deleted immediately. 1915 delete(k.inProgress, opID) 1916 k.lock.Unlock() 1917 k.log.CDebugf(ctx, "doneReadWriteOp, status=%v", err) 1918 if ctx != nil { 1919 err := libcontext.CleanupCancellationDelayer(ctx) 1920 if err != nil { 1921 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 1922 } 1923 } 1924 } 1925 1926 // SimpleFSRead - Read (possibly partial) contents of open file, 1927 // up to the amount specified by size. 1928 // Repeat until zero bytes are returned or error. 1929 // If size is zero, read an arbitrary amount. 1930 func (k *SimpleFS) SimpleFSRead(ctx context.Context, 1931 arg keybase1.SimpleFSReadArg) (_ keybase1.FileContent, err error) { 1932 ctx = k.makeContext(ctx) 1933 k.lock.RLock() 1934 h, ok := k.handles[arg.OpID] 1935 k.lock.RUnlock() 1936 if !ok { 1937 return keybase1.FileContent{}, errNoSuchHandle 1938 } 1939 opDesc := keybase1.NewOpDescriptionWithRead( 1940 keybase1.ReadArgs{ 1941 OpID: arg.OpID, 1942 Path: h.path, 1943 Offset: arg.Offset, 1944 Size: arg.Size, 1945 }) 1946 ctx, err = k.startReadWriteOp(ctx, arg.OpID, keybase1.AsyncOps_READ, opDesc) 1947 if err != nil { 1948 return keybase1.FileContent{}, err 1949 } 1950 k.setProgressTotals(arg.OpID, int64(arg.Size), 1) 1951 defer func() { 1952 if err == nil { 1953 k.updateReadProgress(arg.OpID, 0, 1) 1954 } 1955 }() 1956 1957 defer func() { k.doneReadWriteOp(ctx, arg.OpID, err) }() 1958 1959 // Print this so we can correlate the ID in 1960 k.log.CDebugf(ctx, "Starting read for OpID=%X, offset=%d, size=%d", 1961 arg.OpID, arg.Offset, arg.Size) 1962 1963 _, err = h.file.Seek(arg.Offset, io.SeekStart) 1964 if err != nil { 1965 return keybase1.FileContent{}, err 1966 } 1967 1968 bs := make([]byte, arg.Size) 1969 // TODO: make this a proper buffered read so we can get finer progress? 1970 reader := &progressReader{k, arg.OpID, h.file} 1971 n, err := reader.Read(bs) 1972 if err != nil && err != io.EOF { 1973 return keybase1.FileContent{}, err 1974 } 1975 bs = bs[:n] 1976 return keybase1.FileContent{ 1977 Data: bs, 1978 }, nil 1979 } 1980 1981 // SimpleFSWrite - Append content to opened file. 1982 // May be repeated until OpID is closed. 1983 func (k *SimpleFS) SimpleFSWrite( 1984 ctx context.Context, arg keybase1.SimpleFSWriteArg) (err error) { 1985 ctx = k.makeContext(ctx) 1986 k.lock.RLock() 1987 h, ok := k.handles[arg.OpID] 1988 k.lock.RUnlock() 1989 if !ok { 1990 return errNoSuchHandle 1991 } 1992 1993 opDesc := keybase1.NewOpDescriptionWithWrite( 1994 keybase1.WriteArgs{ 1995 OpID: arg.OpID, Path: h.path, Offset: arg.Offset, 1996 }) 1997 1998 ctx, err = k.startReadWriteOp( 1999 ctx, arg.OpID, keybase1.AsyncOps_WRITE, opDesc) 2000 if err != nil { 2001 return err 2002 } 2003 defer func() { k.doneReadWriteOp(ctx, arg.OpID, err) }() 2004 2005 k.setProgressTotals(arg.OpID, int64(len(arg.Content)), 1) 2006 defer func() { 2007 if err == nil { 2008 k.updateWriteProgress(arg.OpID, 0, 1) 2009 } 2010 }() 2011 2012 k.log.CDebugf(ctx, "Starting write for OpID=%X, offset=%d, size=%d", 2013 arg.OpID, arg.Offset, len(arg.Content)) 2014 2015 _, err = h.file.Seek(arg.Offset, io.SeekStart) 2016 if err != nil { 2017 return err 2018 } 2019 2020 writer := &progressWriter{k, arg.OpID, h.file} 2021 _, err = writer.Write(arg.Content) 2022 return err 2023 } 2024 2025 // SimpleFSRemove - Remove file or directory from filesystem 2026 func (k *SimpleFS) SimpleFSRemove(ctx context.Context, 2027 arg keybase1.SimpleFSRemoveArg) (err error) { 2028 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_REMOVE, 2029 keybase1.NewOpDescriptionWithRemove(keybase1.RemoveArgs(arg)), 2030 &arg.Path, 2031 nil, 2032 func(ctx context.Context) (err error) { 2033 return k.doRemove(ctx, arg.Path, arg.Recursive) 2034 }) 2035 } 2036 2037 // SimpleFSStat - Get info about file 2038 func (k *SimpleFS) SimpleFSStat(ctx context.Context, arg keybase1.SimpleFSStatArg) (de keybase1.Dirent, err error) { 2039 defer func() { err = translateErr(err) }() 2040 ctx, err = k.startSyncOp(ctx, "Stat", arg.Path, &arg.Path, nil) 2041 if err != nil { 2042 return keybase1.Dirent{}, err 2043 } 2044 defer func() { k.doneSyncOp(ctx, err) }() 2045 2046 fs, finalElem, err := k.getFSIfExists(ctx, arg.Path) 2047 switch errors.Cause(err).(type) { 2048 case nil: 2049 case libfs.TlfDoesNotExist: 2050 k.log.CDebugf(ctx, "Return err for finalElem=%s", finalElem) 2051 if finalElem != "" && finalElem != "." { 2052 return keybase1.Dirent{}, err 2053 } 2054 2055 // TLF doesn't exist yet; just return an empty result. 2056 return keybase1.Dirent{ 2057 DirentType: keybase1.DirentType_DIR, 2058 Writable: false, 2059 }, nil 2060 default: 2061 return keybase1.Dirent{}, err 2062 } 2063 2064 if arg.RefreshSubscription { 2065 err = k.refreshSubscription(ctx, arg.Path) 2066 if err != nil { 2067 return keybase1.Dirent{}, err 2068 } 2069 } 2070 2071 // Use LStat so we don't follow symlinks. 2072 fi, err := fs.Lstat(finalElem) 2073 if err != nil { 2074 return keybase1.Dirent{}, err 2075 } 2076 2077 err = k.setStat(&de, fi, fs) 2078 return de, err 2079 } 2080 2081 func (k *SimpleFS) getRevisionsFromPath( 2082 ctx context.Context, path keybase1.Path) ( 2083 billy.Filesystem, os.FileInfo, data.PrevRevisions, error) { 2084 fs, finalElem, err := k.getFSIfExists(ctx, path) 2085 if err != nil { 2086 k.log.CDebugf(ctx, "Trouble getting fs for path: %+v", err) 2087 return nil, nil, nil, err 2088 } 2089 // Use LStat so we don't follow symlinks. 2090 fi, err := fs.Lstat(finalElem) 2091 if err != nil { 2092 return nil, nil, nil, err 2093 } 2094 2095 fipr, ok := fi.Sys().(libfs.PrevRevisionsGetter) 2096 if !ok { 2097 return nil, nil, nil, 2098 simpleFSError{reason: "Cannot get revisions for non-KBFS path"} 2099 } 2100 return fs, fi, fipr.PrevRevisions(), nil 2101 } 2102 2103 func (k *SimpleFS) doGetRevisions( 2104 ctx context.Context, opID keybase1.OpID, path keybase1.Path, 2105 spanType keybase1.RevisionSpanType) ( 2106 revs []keybase1.DirentWithRevision, err error) { 2107 k.vlog.CLogf(ctx, libkb.VLog1, "Getting revisions for path %s, spanType=%s", 2108 path, spanType) 2109 2110 // Both span types return up to 5 revisions. 2111 k.setProgressTotals(opID, 0, 5) 2112 2113 fs, fi, prs, err := k.getRevisionsFromPath(ctx, path) 2114 if err != nil { 2115 return nil, err 2116 } 2117 if len(prs) == 0 { 2118 return nil, simpleFSError{reason: "No previous revisions"} 2119 } 2120 2121 var currRev keybase1.DirentWithRevision 2122 err = k.setStat(&currRev.Entry, fi, fs) 2123 if err != nil { 2124 return nil, err 2125 } 2126 currRev.Revision = keybase1.KBFSRevision(prs[0].Revision) 2127 k.log.CDebugf(ctx, "Found current revision: %d", prs[0].Revision) 2128 k.updateReadProgress(opID, 0, 1) 2129 2130 var revPaths []keybase1.Path 2131 2132 // The next four depend on the span type. 2133 pathStr := path.String() 2134 switch spanType { 2135 case keybase1.RevisionSpanType_DEFAULT: 2136 // Use `prs` for the rest of the paths. 2137 for i := 1; i < len(prs); i++ { 2138 p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{ 2139 Path: pathStr, 2140 ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision( 2141 keybase1.KBFSRevision(prs[i].Revision)), 2142 }) 2143 revPaths = append(revPaths, p) 2144 } 2145 case keybase1.RevisionSpanType_LAST_FIVE: 2146 expectedCount := uint8(2) 2147 nextSlot := 1 2148 lastRevision := prs[0].Revision 2149 2150 // Step back through the previous revisions. If the next one 2151 // in the list happens to be the next in line (because the 2152 // count is one more than the current count), use it. 2153 // Otherwise, we have to fetch the stats from the MD revision 2154 // before the last one we processed, and use the 2155 // PreviousRevisions list from that version of the file. 2156 for len(revPaths) < 4 && nextSlot < len(prs) { 2157 var rev kbfsmd.Revision 2158 switch { 2159 case prs[nextSlot].Count == expectedCount: 2160 rev = prs[nextSlot].Revision 2161 case lastRevision > kbfsmd.RevisionInitial: 2162 k.log.CDebugf(ctx, "Inspecting revision %d to find previous", 2163 lastRevision-1) 2164 pathToPrev := keybase1.NewPathWithKbfsArchived( 2165 keybase1.KBFSArchivedPath{ 2166 Path: pathStr, 2167 ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision( 2168 keybase1.KBFSRevision(lastRevision - 1)), 2169 }) 2170 _, _, prevPRs, err := k.getRevisionsFromPath(ctx, pathToPrev) 2171 if _, isGC := err.(libkbfs.RevGarbageCollectedError); isGC { 2172 k.log.CDebugf(ctx, "Hit a GC'd revision: %d", 2173 lastRevision-1) 2174 break 2175 } else if err != nil { 2176 return nil, err 2177 } 2178 if len(prevPRs) == 0 { 2179 // This should never happen, because there is some 2180 // next slot in the `prs` list, but it doesn't 2181 // match the expected count, which means there 2182 // must be _some_ revision in between the last 2183 // revision and the one in the next slot, that we 2184 // should uncover by looking up `lastRevision-1`. 2185 return nil, simpleFSError{reason: fmt.Sprintf( 2186 "Revision %s unexpectedly lists no previous revisions", 2187 lastRevision-1)} 2188 } 2189 rev = prevPRs[0].Revision 2190 prs = prevPRs 2191 nextSlot = 0 // will be incremented below 2192 expectedCount = 1 // will be incremented below 2193 default: 2194 break 2195 } 2196 2197 p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{ 2198 Path: pathStr, 2199 ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision( 2200 keybase1.KBFSRevision(rev)), 2201 }) 2202 revPaths = append(revPaths, p) 2203 lastRevision = rev 2204 nextSlot++ 2205 expectedCount++ 2206 } 2207 default: 2208 return nil, simpleFSError{reason: fmt.Sprintf("Unknown span type: %s", spanType)} 2209 } 2210 2211 if len(revPaths) < 4 { 2212 // See if the final revision has a predecessor that's 2213 // still live, to fill out the list of 5. An older 2214 // revision could have slid off the previous revisions 2215 // list because that revision was garbage-collected, but 2216 // that doesn't guarantee that the older revision of the 2217 // file was garabge-collected too (since it was created, 2218 // not deleted, as of that garbage-collected revision). 2219 p := keybase1.NewPathWithKbfsArchived(keybase1.KBFSArchivedPath{ 2220 Path: pathStr, 2221 ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision( 2222 keybase1.KBFSRevision(prs[len(prs)-1].Revision - 1)), 2223 }) 2224 revPaths = append(revPaths, p) 2225 } 2226 2227 // Now that we have all the paths we need, stat them one-by-one. 2228 revs = make([]keybase1.DirentWithRevision, len(revPaths)+1) 2229 revs[0] = currRev 2230 2231 if len(revs) < 5 { 2232 // Discount the revisions that don't exist from the progress. 2233 k.updateReadProgress(opID, 0, int64(5-len(revs))) 2234 } 2235 2236 // Fetch all the past revisions in parallel to populate the 2237 // directory entry. 2238 eg, groupCtx := errgroup.WithContext(ctx) 2239 doStat := func(slot int) error { 2240 p := revPaths[slot] 2241 fs, finalElem, err := k.getFSIfExists(groupCtx, p) 2242 if _, isGC := err.(libkbfs.RevGarbageCollectedError); isGC { 2243 k.log.CDebugf(ctx, "Hit a GC'd revision: %d", 2244 p.KbfsArchived().ArchivedParam.Revision()) 2245 return nil 2246 } else if err != nil { 2247 return err 2248 } 2249 // Use LStat so we don't follow symlinks. 2250 fi, err := fs.Lstat(finalElem) 2251 if os.IsNotExist(err) { 2252 k.log.CDebugf(ctx, "Ran out of revisions as of %d", 2253 p.KbfsArchived().ArchivedParam.Revision()) 2254 return nil 2255 } 2256 if err != nil { 2257 return err 2258 } 2259 var rev keybase1.DirentWithRevision 2260 err = k.setStat(&rev.Entry, fi, fs) 2261 if err != nil { 2262 return err 2263 } 2264 rev.Revision = p.KbfsArchived().ArchivedParam.Revision() 2265 revs[slot+1] = rev 2266 k.updateReadProgress(opID, 0, 1) 2267 return nil 2268 } 2269 for i := range revPaths { 2270 i := i 2271 eg.Go(func() error { return doStat(i) }) 2272 } 2273 err = eg.Wait() 2274 if err != nil { 2275 return nil, err 2276 } 2277 2278 // Remove any GC'd revisions. 2279 for i, r := range revs { 2280 if kbfsmd.Revision(r.Revision) == kbfsmd.RevisionUninitialized { 2281 revs = revs[:i] 2282 break 2283 } 2284 } 2285 2286 return revs, nil 2287 } 2288 2289 // SimpleFSGetRevisions - Get revisions for a file 2290 func (k *SimpleFS) SimpleFSGetRevisions( 2291 ctx context.Context, arg keybase1.SimpleFSGetRevisionsArg) (err error) { 2292 return k.startAsync(ctx, arg.OpID, keybase1.AsyncOps_GET_REVISIONS, 2293 keybase1.NewOpDescriptionWithGetRevisions( 2294 keybase1.GetRevisionsArgs(arg)), 2295 &arg.Path, 2296 nil, 2297 func(ctx context.Context) (err error) { 2298 revs, err := k.doGetRevisions(ctx, arg.OpID, arg.Path, arg.SpanType) 2299 if err != nil { 2300 return err 2301 } 2302 k.setResult(arg.OpID, keybase1.GetRevisionsResult{ 2303 Revisions: revs, 2304 // For don't set any progress indicators. If we decide we want 2305 // to display partial results, we can fix this later. 2306 }) 2307 return nil 2308 }) 2309 } 2310 2311 // SimpleFSReadRevisions - Get list of revisions in progress. Can 2312 // indicate status of pending to get more revisions. 2313 func (k *SimpleFS) SimpleFSReadRevisions( 2314 _ context.Context, opid keybase1.OpID) ( 2315 keybase1.GetRevisionsResult, error) { 2316 k.lock.Lock() 2317 res := k.handles[opid] 2318 var x interface{} 2319 if res != nil { 2320 x = res.async 2321 res.async = nil 2322 } 2323 k.lock.Unlock() 2324 2325 lr, ok := x.(keybase1.GetRevisionsResult) 2326 if !ok { 2327 return keybase1.GetRevisionsResult{}, errNoResult 2328 } 2329 2330 return lr, nil 2331 } 2332 2333 // SimpleFSMakeOpid - Convenience helper for generating new random value 2334 func (k *SimpleFS) SimpleFSMakeOpid(_ context.Context) (keybase1.OpID, error) { 2335 var opid keybase1.OpID 2336 err := kbfscrypto.RandRead(opid[:]) 2337 return opid, err 2338 } 2339 2340 // SimpleFSClose - Close removes a handle associated with Open / List. 2341 func (k *SimpleFS) SimpleFSClose(ctx context.Context, opid keybase1.OpID) (err error) { 2342 ctx, err = k.startSyncOp(ctx, "Close", opid, nil, nil) 2343 if err != nil { 2344 return err 2345 } 2346 defer func() { k.doneSyncOp(ctx, err) }() 2347 2348 k.lock.Lock() 2349 defer k.lock.Unlock() 2350 delete(k.inProgress, opid) 2351 h, ok := k.handles[opid] 2352 if !ok { 2353 return errNoSuchHandle 2354 } 2355 delete(k.handles, opid) 2356 if h.file != nil { 2357 err = h.file.Close() 2358 } 2359 if h.cancel != nil { 2360 h.cancel() 2361 } 2362 return err 2363 } 2364 2365 // SimpleFSCancel starts to cancel op with the given opid. 2366 // Also remove any pending references of opid everywhere. 2367 // Returns before cancellation is guaranteeded to be done - that 2368 // may take some time. Currently always returns nil. 2369 func (k *SimpleFS) SimpleFSCancel(_ context.Context, opid keybase1.OpID) error { 2370 k.lock.Lock() 2371 defer k.lock.Unlock() 2372 delete(k.handles, opid) 2373 w, ok := k.inProgress[opid] 2374 if !ok { 2375 return nil 2376 } 2377 delete(k.inProgress, opid) 2378 w.cancel() 2379 return nil 2380 } 2381 2382 // SimpleFSCheck - Check progress of pending operation 2383 // Progress variable is still TBD. 2384 // Return errNoResult if no operation found. 2385 func (k *SimpleFS) SimpleFSCheck( 2386 ctx context.Context, opid keybase1.OpID) (keybase1.OpProgress, error) { 2387 k.lock.RLock() 2388 defer k.lock.RUnlock() 2389 if p, ok := k.inProgress[opid]; ok { 2390 // For now, estimate the ending time purely on the read progress. 2391 var n, d int64 2392 progress := p.progress 2393 if progress.BytesTotal > 0 { 2394 n = progress.BytesRead 2395 d = progress.BytesTotal 2396 } else if p.progress.FilesTotal > 0 { 2397 n = progress.FilesRead 2398 d = progress.FilesTotal 2399 } 2400 if n > 0 && d > 0 && !progress.Start.IsZero() && 2401 progress.EndEstimate.IsZero() { 2402 // Crudely estimate that the total time for the op is the 2403 // time spent so far, divided by the fraction of the 2404 // reading that's been done. 2405 start := keybase1.FromTime(progress.Start) 2406 timeRunning := k.config.Clock().Now().Sub(start) 2407 fracDone := float64(n) / float64(d) 2408 totalTimeEstimate := time.Duration(float64(timeRunning) / fracDone) 2409 progress.EndEstimate = 2410 keybase1.ToTime(start.Add(totalTimeEstimate)) 2411 k.log.CDebugf(ctx, "Start=%s, n=%d, d=%d, fracDone=%f, End=%s", 2412 start, n, d, fracDone, start.Add(totalTimeEstimate)) 2413 } 2414 2415 return progress, nil 2416 } else if _, ok := k.handles[opid]; ok { 2417 // Return an empty progress and nil error if there's no async 2418 // operation pending, but there is still an open handle. 2419 return keybase1.OpProgress{}, nil 2420 } 2421 return keybase1.OpProgress{}, errNoResult 2422 } 2423 2424 // SimpleFSGetOps - Get all the outstanding operations 2425 func (k *SimpleFS) SimpleFSGetOps(_ context.Context) ([]keybase1.OpDescription, error) { 2426 k.lock.RLock() 2427 r := make([]keybase1.OpDescription, 0, len(k.inProgress)) 2428 for _, p := range k.inProgress { 2429 r = append(r, p.desc) 2430 } 2431 k.lock.RUnlock() 2432 return r, nil 2433 } 2434 2435 // SimpleFSWait - Blocking wait for the pending operation to finish 2436 func (k *SimpleFS) SimpleFSWait(ctx context.Context, opid keybase1.OpID) error { 2437 ctx = k.makeContext(ctx) 2438 k.lock.RLock() 2439 w, ok := k.inProgress[opid] 2440 k.log.CDebugf(ctx, "Wait %X -> %v, %v", opid, w, ok) 2441 k.lock.RUnlock() 2442 if !ok { 2443 return errNoSuchHandle 2444 } 2445 2446 err, ok := <-w.done 2447 2448 k.lock.Lock() 2449 delete(k.inProgress, opid) 2450 k.lock.Unlock() 2451 2452 if !ok { 2453 return errNoResult 2454 } 2455 return err 2456 } 2457 2458 // SimpleFSDumpDebuggingInfo - Instructs KBFS to dump debugging info 2459 // into its logs. 2460 func (k *SimpleFS) SimpleFSDumpDebuggingInfo(ctx context.Context) error { 2461 ctx = k.makeContext(ctx) 2462 k.idd.ForceDump(ctx) 2463 return nil 2464 } 2465 2466 // This timeout needs to be smaller than the one in 2467 // keybase/client/go/service/simplefs.go so that for situations where error is 2468 // not critical (e.g. quota usage in journal status calls), we'll get (and 2469 // ignore) a timeout before service times us out in RPC. 2470 const simpleFSFastActionTimeout = 6 * time.Second 2471 2472 // SimpleFSSyncStatus - Get sync status. 2473 func (k *SimpleFS) SimpleFSSyncStatus(ctx context.Context, filter keybase1.ListFilter) (keybase1.FSSyncStatus, error) { 2474 ctx, cancel := context.WithTimeout( 2475 k.makeContext(ctx), simpleFSFastActionTimeout) 2476 defer cancel() 2477 jManager, jErr := libkbfs.GetJournalManager(k.config) 2478 if jErr != nil { 2479 k.log.CDebugf(ctx, "Journal not enabled; sending empty response") 2480 return keybase1.FSSyncStatus{}, nil 2481 } 2482 status, tlfIDs := jManager.Status(ctx) 2483 err := libkbfs.FillInJournalStatusUnflushedPaths( 2484 ctx, k.config, &status, tlfIDs) 2485 if err != nil { 2486 k.log.CDebugf(ctx, "Error setting unflushed paths: %+v; "+ 2487 "sending empty response", err) 2488 return keybase1.FSSyncStatus{}, nil 2489 } 2490 2491 var syncingPaths []string 2492 if filter == keybase1.ListFilter_NO_FILTER { 2493 syncingPaths = status.UnflushedPaths 2494 } else { 2495 for _, p := range status.UnflushedPaths { 2496 2497 if isFiltered(filter, stdpath.Base(p)) { 2498 continue 2499 } 2500 syncingPaths = append(syncingPaths, p) 2501 } 2502 } 2503 2504 k.log.CDebugf(ctx, "Sending sync status response with %d syncing bytes", 2505 status.UnflushedBytes) 2506 return keybase1.FSSyncStatus{ 2507 TotalSyncingBytes: status.UnflushedBytes, 2508 SyncingPaths: syncingPaths, 2509 EndEstimate: keybase1.ToTimePtr(status.EndEstimate), 2510 }, nil 2511 } 2512 2513 // SimpleFSUserEditHistory returns the edit history for the logged-in user. 2514 func (k *SimpleFS) SimpleFSUserEditHistory(ctx context.Context) ( 2515 res []keybase1.FSFolderEditHistory, err error) { 2516 kbpki, err := k.getKBPKI(ctx) 2517 if err != nil { 2518 return nil, err 2519 } 2520 session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true) 2521 // Return empty history if we are not logged in. 2522 if err != nil { 2523 return nil, nil 2524 } 2525 return k.config.UserHistory().Get(string(session.Name)), nil 2526 } 2527 2528 // SimpleFSFolderEditHistory returns the edit history for the given TLF. 2529 func (k *SimpleFS) SimpleFSFolderEditHistory( 2530 ctx context.Context, path keybase1.Path) ( 2531 res keybase1.FSFolderEditHistory, err error) { 2532 ctx = k.makeContext(ctx) 2533 fb, _, err := k.getFolderBranchFromPath(ctx, path) 2534 if err != nil { 2535 return keybase1.FSFolderEditHistory{}, err 2536 } 2537 if fb == (data.FolderBranch{}) { 2538 return keybase1.FSFolderEditHistory{}, nil 2539 } 2540 2541 // Now get the edit history. 2542 return k.config.KBFSOps().GetEditHistory(ctx, fb) 2543 } 2544 2545 // SimpleFSReset resets the given TLF. 2546 func (k *SimpleFS) SimpleFSReset( 2547 ctx context.Context, arg keybase1.SimpleFSResetArg) error { 2548 t, tlfName, _, _, err := remoteTlfAndPath(arg.Path) 2549 if err != nil { 2550 return err 2551 } 2552 kbpki, err := k.getKBPKI(ctx) 2553 if err != nil { 2554 return err 2555 } 2556 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 2557 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 2558 if err != nil { 2559 return err 2560 } 2561 2562 var newTlfID *tlf.ID 2563 if arg.TlfID != "" { 2564 tlfID, err := tlf.ParseID(arg.TlfID) 2565 if err != nil { 2566 return err 2567 } 2568 newTlfID = &tlfID 2569 } 2570 2571 return k.config.KBFSOps().Reset(ctx, tlfHandle, newTlfID) 2572 } 2573 2574 var _ libkbfs.Observer = (*SimpleFS)(nil) 2575 2576 // LocalChange implements the libkbfs.Observer interface for SimpleFS. 2577 func (k *SimpleFS) LocalChange( 2578 ctx context.Context, node libkbfs.Node, _ libkbfs.WriteRange) { 2579 k.subscribeLock.RLock() 2580 defer k.subscribeLock.RUnlock() 2581 if node.GetFolderBranch() == k.subscribeCurrFB { 2582 k.config.Reporter().NotifyPathUpdated(ctx, k.subscribeCurrTlfPathFromGUI) 2583 } 2584 } 2585 2586 // BatchChanges implements the libkbfs.Observer interface for SimpleFS. 2587 func (k *SimpleFS) BatchChanges( 2588 ctx context.Context, changes []libkbfs.NodeChange, _ []libkbfs.NodeID) { 2589 // Don't take any locks while processing these notifications, 2590 // since it risks deadlock. 2591 fbs := make(map[data.FolderBranch]bool, 1) 2592 for _, nc := range changes { 2593 fbs[nc.Node.GetFolderBranch()] = true 2594 } 2595 2596 go func() { 2597 k.subscribeLock.RLock() 2598 defer k.subscribeLock.RUnlock() 2599 if fbs[k.subscribeCurrFB] { 2600 k.config.Reporter().NotifyPathUpdated(ctx, k.subscribeCurrTlfPathFromGUI) 2601 } 2602 }() 2603 } 2604 2605 // TlfHandleChange implements the libkbfs.Observer interface for SimpleFS. 2606 func (k *SimpleFS) TlfHandleChange(_ context.Context, _ *tlfhandle.Handle) { 2607 // TODO: the GUI might eventually care about a handle change. 2608 } 2609 2610 // SimpleFSGetUserQuotaUsage returns the quota usage information for 2611 // the logged-in user. 2612 func (k *SimpleFS) SimpleFSGetUserQuotaUsage(ctx context.Context) ( 2613 res keybase1.SimpleFSQuotaUsage, err error) { 2614 ctx = k.makeContext(ctx) 2615 status, _, err := k.config.KBFSOps().Status(ctx) 2616 if err != nil { 2617 return keybase1.SimpleFSQuotaUsage{}, err 2618 } 2619 res.UsageBytes = status.UsageBytes 2620 res.ArchiveBytes = status.ArchiveBytes 2621 res.LimitBytes = status.LimitBytes 2622 res.GitUsageBytes = status.GitUsageBytes 2623 res.GitArchiveBytes = status.GitArchiveBytes 2624 res.GitLimitBytes = status.GitLimitBytes 2625 return res, nil 2626 } 2627 2628 // SimpleFSGetTeamQuotaUsage returns the quota usage information for 2629 // the given team. 2630 func (k *SimpleFS) SimpleFSGetTeamQuotaUsage( 2631 ctx context.Context, teamName keybase1.TeamName) ( 2632 res keybase1.SimpleFSQuotaUsage, err error) { 2633 ctx = k.makeContext(ctx) 2634 path := keybase1.NewPathWithKbfsPath( 2635 fmt.Sprintf("team/%s", teamName.String())) 2636 fb, _, err := k.getFolderBranchFromPath(ctx, path) 2637 if err != nil { 2638 return keybase1.SimpleFSQuotaUsage{}, err 2639 } 2640 if fb == (data.FolderBranch{}) { 2641 return keybase1.SimpleFSQuotaUsage{}, nil 2642 } 2643 2644 status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb) 2645 if err != nil { 2646 return keybase1.SimpleFSQuotaUsage{}, err 2647 } 2648 2649 res.UsageBytes = status.UsageBytes 2650 res.ArchiveBytes = status.ArchiveBytes 2651 res.LimitBytes = status.LimitBytes 2652 res.GitUsageBytes = status.GitUsageBytes 2653 res.GitArchiveBytes = status.GitArchiveBytes 2654 res.GitLimitBytes = status.GitLimitBytes 2655 return res, nil 2656 } 2657 2658 func (k *SimpleFS) getSyncConfig(ctx context.Context, path keybase1.Path) ( 2659 tlfID tlf.ID, config keybase1.FolderSyncConfig, 2660 err error) { 2661 t, tlfName, _, _, err := remoteTlfAndPath(path) 2662 if err != nil { 2663 return tlf.NullID, keybase1.FolderSyncConfig{}, err 2664 } 2665 kbpki, err := k.getKBPKI(ctx) 2666 if err != nil { 2667 return tlf.NullID, keybase1.FolderSyncConfig{}, err 2668 } 2669 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 2670 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 2671 if err != nil { 2672 return tlf.NullID, keybase1.FolderSyncConfig{}, err 2673 } 2674 2675 // Ensure the TLF is initialized by getting the root node first. 2676 _, _, err = k.config.KBFSOps().GetRootNode( 2677 ctx, tlfHandle, data.MasterBranch) 2678 if err != nil { 2679 return tlf.NullID, keybase1.FolderSyncConfig{}, err 2680 } 2681 2682 config, err = k.config.KBFSOps().GetSyncConfig(ctx, tlfHandle.TlfID()) 2683 if err != nil { 2684 return tlf.NullID, keybase1.FolderSyncConfig{}, err 2685 } 2686 return tlfHandle.TlfID(), config, nil 2687 } 2688 2689 func (k *SimpleFS) filterEmptyErr( 2690 ctx context.Context, path string, err error) error { 2691 exitEarly, _ := libfs.FilterTLFEarlyExitError( 2692 ctx, err, k.log, tlf.CanonicalName(path) /* just for logging */) 2693 if exitEarly { 2694 return nil 2695 } 2696 return err 2697 } 2698 2699 // SimpleFSFolderSyncConfigAndStatus gets the given folder's sync config. 2700 func (k *SimpleFS) SimpleFSFolderSyncConfigAndStatus( 2701 ctx context.Context, path keybase1.Path) ( 2702 _ keybase1.FolderSyncConfigAndStatus, err error) { 2703 defer func() { 2704 err = k.filterEmptyErr(ctx, path.String(), err) 2705 }() 2706 ctx = k.makeContext(ctx) 2707 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil) 2708 if err != nil { 2709 return keybase1.FolderSyncConfigAndStatus{}, err 2710 } 2711 _, config, err := k.getSyncConfig(ctx, path) 2712 if err != nil { 2713 return keybase1.FolderSyncConfigAndStatus{}, err 2714 } 2715 res := keybase1.FolderSyncConfigAndStatus{Config: config} 2716 2717 dbc := k.config.DiskBlockCache() 2718 if config.Mode != keybase1.FolderSyncMode_DISABLED { 2719 fs, finalElem, err := k.getFSIfExists(ctx, path) 2720 if err != nil { 2721 return res, err 2722 } 2723 // Use LStat so we don't follow symlinks. 2724 fi, err := fs.Lstat(finalElem) 2725 if err != nil { 2726 return res, err 2727 } 2728 2729 if kmg, ok := fi.Sys().(libfs.KBFSMetadataForSimpleFSGetter); ok { 2730 metadata, err := kmg.KBFSMetadataForSimpleFS() 2731 if err != nil { 2732 return keybase1.FolderSyncConfigAndStatus{}, err 2733 } 2734 res.Status.PrefetchStatus = metadata.PrefetchStatus 2735 res.Status.PrefetchProgress = 2736 metadata.PrefetchProgress.ToProtocolProgress(k.config.Clock()) 2737 2738 libfs, ok := fs.(*libfs.FS) 2739 if dbc != nil && ok { 2740 size, err := dbc.GetTlfSize( 2741 ctx, libfs.RootNode().GetFolderBranch().Tlf, 2742 libkbfs.DiskBlockSyncCache) 2743 if err != nil { 2744 return res, err 2745 } 2746 res.Status.StoredBytesTotal = int64(size) 2747 } 2748 } else { 2749 k.log.CDebugf(ctx, 2750 "Could not get prefetch status from filesys: %T", fi.Sys()) 2751 } 2752 } 2753 2754 libkbfs.FillInDiskSpaceStatus( 2755 ctx, &res.Status, res.Status.PrefetchStatus, dbc) 2756 return res, err 2757 } 2758 2759 // SimpleFSSetFolderSyncConfig implements the SimpleFSInterface. 2760 func (k *SimpleFS) SimpleFSSetFolderSyncConfig( 2761 ctx context.Context, arg keybase1.SimpleFSSetFolderSyncConfigArg) (err error) { 2762 ctx = k.makeContext(ctx) 2763 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &arg.Path, nil) 2764 if err != nil { 2765 return err 2766 } 2767 tlfID, _, err := k.getSyncConfig(ctx, arg.Path) 2768 if err != nil { 2769 return err 2770 } 2771 2772 _, err = k.config.KBFSOps().SetSyncConfig(ctx, tlfID, arg.Config) 2773 return err 2774 } 2775 2776 // SimpleFSGetFolder implements the SimpleFSInterface. 2777 func (k *SimpleFS) SimpleFSGetFolder( 2778 ctx context.Context, kbfsPath keybase1.KBFSPath) ( 2779 res keybase1.FolderWithFavFlags, err error) { 2780 defer func() { err = translateErr(err) }() 2781 ctx, err = k.makeContextWithIdentifyBehavior(ctx, kbfsPath.IdentifyBehavior) 2782 if err != nil { 2783 return keybase1.FolderWithFavFlags{}, err 2784 } 2785 t, tlfName, _, _, err := remoteTlfAndPath(keybase1.NewPathWithKbfs(kbfsPath)) 2786 if err != nil { 2787 return keybase1.FolderWithFavFlags{}, err 2788 } 2789 kbpki, err := k.getKBPKI(ctx) 2790 if err != nil { 2791 return keybase1.FolderWithFavFlags{}, err 2792 } 2793 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 2794 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 2795 if err != nil { 2796 return keybase1.FolderWithFavFlags{}, err 2797 } 2798 return k.config.KBFSOps().GetFolderWithFavFlags(ctx, tlfHandle) 2799 } 2800 2801 func (k *SimpleFS) getFolder( 2802 ctx context.Context, tlfID tlf.ID, md libkbfs.SyncedTlfMD, 2803 session idutil.SessionInfo) (keybase1.Folder, error) { 2804 return keybase1.Folder{ 2805 Name: string(md.Handle.GetPreferredFormat(session.Name)), 2806 FolderType: tlfID.Type().FolderType(), 2807 Private: tlfID.Type() != tlf.Public, 2808 }, nil 2809 } 2810 2811 // SimpleFSSyncConfigAndStatus implements the SimpleFSInterface. 2812 func (k *SimpleFS) SimpleFSSyncConfigAndStatus(ctx context.Context, 2813 identifyBehavior *keybase1.TLFIdentifyBehavior) ( 2814 res keybase1.SyncConfigAndStatusRes, err error) { 2815 ctx, err = k.makeContextWithIdentifyBehavior(ctx, identifyBehavior) 2816 if err != nil { 2817 return keybase1.SyncConfigAndStatusRes{}, err 2818 } 2819 dbc := k.config.DiskBlockCache() 2820 bytesAvail, bytesTotal := libkbfs.GetLocalDiskStats(ctx, dbc) 2821 2822 hasRoom := true 2823 if dbc != nil { 2824 hasRoom, _, err = dbc.DoesCacheHaveSpace(ctx, libkbfs.DiskBlockSyncCache) 2825 if err != nil { 2826 return keybase1.SyncConfigAndStatusRes{}, err 2827 } 2828 } 2829 2830 tlfMDs := k.config.KBFSOps().GetAllSyncedTlfMDs(ctx) 2831 kbpki, err := k.getKBPKI(ctx) 2832 if err != nil { 2833 return keybase1.SyncConfigAndStatusRes{}, err 2834 } 2835 session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true) 2836 if err != nil { 2837 return keybase1.SyncConfigAndStatusRes{}, err 2838 } 2839 2840 res.Folders = make( 2841 []keybase1.FolderSyncConfigAndStatusWithFolder, len(tlfMDs)) 2842 allNotStarted := true 2843 i := 0 2844 for tlfID, md := range tlfMDs { 2845 config, err := k.config.KBFSOps().GetSyncConfig(ctx, tlfID) 2846 if err != nil { 2847 return keybase1.SyncConfigAndStatusRes{}, err 2848 } 2849 2850 if config.Mode == keybase1.FolderSyncMode_DISABLED { 2851 return keybase1.SyncConfigAndStatusRes{}, errors.Errorf( 2852 "Folder %s has sync unexpectedly disabled", tlfID) 2853 } 2854 2855 f, err := k.getFolder(ctx, tlfID, md, session) 2856 if err != nil { 2857 return keybase1.SyncConfigAndStatusRes{}, err 2858 } 2859 2860 res.Folders[i].Folder = f 2861 res.Folders[i].Config = config 2862 status := md.MD.PrefetchStatus.ToProtocolStatus() 2863 res.Folders[i].Status.PrefetchStatus = status 2864 if status != keybase1.PrefetchStatus_NOT_STARTED { 2865 allNotStarted = false 2866 } 2867 if md.MD.PrefetchProgress != nil { 2868 res.Folders[i].Status.PrefetchProgress = 2869 md.MD.PrefetchProgress.ToProtocolProgress(k.config.Clock()) 2870 } 2871 res.Folders[i].Status.LocalDiskBytesAvailable = bytesAvail 2872 res.Folders[i].Status.LocalDiskBytesTotal = bytesTotal 2873 if res.Folders[i].Status.PrefetchStatus != 2874 keybase1.PrefetchStatus_COMPLETE { 2875 res.Folders[i].Status.OutOfSyncSpace = !hasRoom 2876 } 2877 2878 if dbc != nil { 2879 size, err := dbc.GetTlfSize(ctx, tlfID, libkbfs.DiskBlockSyncCache) 2880 if err != nil { 2881 return keybase1.SyncConfigAndStatusRes{}, err 2882 } 2883 res.Folders[i].Status.StoredBytesTotal = int64(size) 2884 } 2885 2886 i++ 2887 } 2888 2889 // Sort by folder name. 2890 sort.SliceStable(res.Folders, func(i, j int) bool { 2891 return res.Folders[i].Folder.ToString() < 2892 res.Folders[j].Folder.ToString() 2893 }) 2894 2895 if len(tlfMDs) > 0 { 2896 p := k.config.BlockOps().Prefetcher().OverallSyncStatus() 2897 res.OverallStatus.PrefetchProgress = p.ToProtocolProgress( 2898 k.config.Clock()) 2899 if allNotStarted { 2900 res.OverallStatus.PrefetchStatus = 2901 keybase1.PrefetchStatus_NOT_STARTED 2902 } else { 2903 res.OverallStatus.PrefetchStatus = p.ToProtocolStatus() 2904 } 2905 } 2906 2907 res.OverallStatus.LocalDiskBytesAvailable = bytesAvail 2908 res.OverallStatus.LocalDiskBytesTotal = bytesTotal 2909 if res.OverallStatus.PrefetchStatus != 2910 keybase1.PrefetchStatus_COMPLETE { 2911 res.OverallStatus.OutOfSyncSpace = !hasRoom 2912 } 2913 2914 if dbc != nil { 2915 statusMap := dbc.Status(ctx) 2916 status, ok := statusMap["SyncBlockCache"] 2917 if ok { 2918 res.OverallStatus.StoredBytesTotal = int64(status.BlockBytes) 2919 } 2920 } 2921 2922 return res, nil 2923 } 2924 2925 // SimpleFSClearConflictState implements the SimpleFS interface. 2926 func (k *SimpleFS) SimpleFSClearConflictState(ctx context.Context, 2927 path keybase1.Path) (err error) { 2928 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil) 2929 if err != nil { 2930 return err 2931 } 2932 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 2933 if err != nil { 2934 return err 2935 } 2936 defer func() { 2937 err := libcontext.CleanupCancellationDelayer(ctx) 2938 if err != nil { 2939 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 2940 } 2941 }() 2942 t, tlfName, _, _, err := remoteTlfAndPath(path) 2943 if err != nil { 2944 return err 2945 } 2946 kbpki, err := k.getKBPKI(ctx) 2947 if err != nil { 2948 return err 2949 } 2950 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 2951 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 2952 if err != nil { 2953 return err 2954 } 2955 tlfID := tlfHandle.TlfID() 2956 return k.config.KBFSOps().ClearConflictView(ctx, tlfID) 2957 } 2958 2959 // SimpleFSFinishResolvingConflict implements the SimpleFS interface. 2960 func (k *SimpleFS) SimpleFSFinishResolvingConflict(ctx context.Context, 2961 path keybase1.Path) (err error) { 2962 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil) 2963 if err != nil { 2964 return err 2965 } 2966 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 2967 if err != nil { 2968 return err 2969 } 2970 defer func() { 2971 err := libcontext.CleanupCancellationDelayer(ctx) 2972 if err != nil { 2973 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 2974 } 2975 }() 2976 t, tlfName, _, _, err := remoteTlfAndPath(path) 2977 if err != nil { 2978 return err 2979 } 2980 kbpki, err := k.getKBPKI(ctx) 2981 if err != nil { 2982 return err 2983 } 2984 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 2985 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 2986 if err != nil { 2987 return err 2988 } 2989 tlfID := tlfHandle.TlfID() 2990 branch, err := k.branchNameFromPath(ctx, tlfHandle, path) 2991 if err != nil { 2992 return err 2993 } 2994 return k.config.KBFSOps().FinishResolvingConflict(ctx, data.FolderBranch{ 2995 Tlf: tlfID, 2996 Branch: branch, 2997 }) 2998 } 2999 3000 // SimpleFSForceStuckConflict implements the SimpleFS interface. 3001 func (k *SimpleFS) SimpleFSForceStuckConflict( 3002 ctx context.Context, path keybase1.Path) (err error) { 3003 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &path, nil) 3004 if err != nil { 3005 return err 3006 } 3007 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 3008 if err != nil { 3009 return err 3010 } 3011 defer func() { 3012 err := libcontext.CleanupCancellationDelayer(ctx) 3013 if err != nil { 3014 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 3015 } 3016 }() 3017 t, tlfName, _, _, err := remoteTlfAndPath(path) 3018 if err != nil { 3019 return err 3020 } 3021 kbpki, err := k.getKBPKI(ctx) 3022 if err != nil { 3023 return err 3024 } 3025 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 3026 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 3027 if err != nil { 3028 return err 3029 } 3030 tlfID := tlfHandle.TlfID() 3031 return k.config.KBFSOps().ForceStuckConflictForTesting(ctx, tlfID) 3032 } 3033 3034 // SimpleFSGetOnlineStatus implements the SimpleFSInterface. 3035 func (k *SimpleFS) SimpleFSGetOnlineStatus( 3036 ctx context.Context, clientID string) (keybase1.KbfsOnlineStatus, error) { 3037 return k.subscriptionManager(clientID).OnlineStatusTracker().GetOnlineStatus(), nil 3038 } 3039 3040 // SimpleFSUserIn implements the SimpleFSInterface. 3041 func (k *SimpleFS) SimpleFSUserIn(ctx context.Context, clientID string) error { 3042 k.subscriptionManager(clientID).OnlineStatusTracker().UserIn(ctx, clientID) 3043 return nil 3044 } 3045 3046 // SimpleFSUserOut implements the SimpleFSInterface. 3047 func (k *SimpleFS) SimpleFSUserOut(ctx context.Context, clientID string) error { 3048 k.subscriptionManager(clientID).OnlineStatusTracker().UserOut(ctx, clientID) 3049 return nil 3050 } 3051 3052 // SimpleFSCheckReachability implements the SimpleFSInterface. 3053 func (k *SimpleFS) SimpleFSCheckReachability(ctx context.Context) error { 3054 ctx = k.makeContext(ctx) 3055 mdServer := k.config.MDServer() 3056 if mdServer != nil { 3057 // KeybaseService (which holds SimpleFS service) gets init'ed before 3058 // MDServer is set. HOTPOT-1269 3059 mdServer.CheckReachability(ctx) 3060 } 3061 return nil 3062 } 3063 3064 // SimpleFSSetDebugLevel implements the SimpleFSInterface. 3065 func (k *SimpleFS) SimpleFSSetDebugLevel( 3066 _ context.Context, level string) error { 3067 k.config.SetVLogLevel(level) 3068 return nil 3069 } 3070 3071 // SimpleFSSettings implements the SimpleFSInterface. 3072 func (k *SimpleFS) SimpleFSSettings(ctx context.Context) (settings keybase1.FSSettings, err error) { 3073 defer func() { 3074 k.log.CDebugf(ctx, "SimpleFSSettings settings=%+v err=%+v", settings, err) 3075 }() 3076 db := k.config.GetSettingsDB() 3077 if db == nil { 3078 return keybase1.FSSettings{}, libkbfs.ErrNoSettingsDB 3079 } 3080 return db.Settings(ctx) 3081 } 3082 3083 // SimpleFSSetNotificationThreshold implements the SimpleFSInterface. 3084 func (k *SimpleFS) SimpleFSSetNotificationThreshold(ctx context.Context, threshold int64) (err error) { 3085 defer func() { 3086 k.log.CDebugf(ctx, "SimpleFSSetNotificationThreshold threshold=%d err=%+v", threshold, err) 3087 }() 3088 db := k.config.GetSettingsDB() 3089 if db == nil { 3090 return libkbfs.ErrNoSettingsDB 3091 } 3092 if err = db.SetNotificationThreshold(ctx, threshold); err != nil { 3093 return err 3094 } 3095 k.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_SETTINGS) 3096 return nil 3097 } 3098 3099 // SimpleFSObfuscatePath implements the SimpleFSInterface. 3100 func (k *SimpleFS) SimpleFSObfuscatePath( 3101 ctx context.Context, path keybase1.Path) (res string, err error) { 3102 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 3103 if err != nil { 3104 return "", err 3105 } 3106 defer func() { 3107 err := libcontext.CleanupCancellationDelayer(ctx) 3108 if err != nil { 3109 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 3110 } 3111 }() 3112 t, tlfName, midPath, finalElem, err := remoteTlfAndPath(path) 3113 if err != nil { 3114 return "", err 3115 } 3116 kbpki, err := k.getKBPKI(ctx) 3117 if err != nil { 3118 return "", err 3119 } 3120 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 3121 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 3122 if err != nil { 3123 return "", err 3124 } 3125 branch, err := k.branchNameFromPath(ctx, tlfHandle, path) 3126 if err != nil { 3127 return "", err 3128 } 3129 fs, err := k.newFS( 3130 ctx, k.config, tlfHandle, branch, "", false) 3131 if err != nil { 3132 return "", err 3133 } 3134 asLibFS, ok := fs.(*libfs.FS) 3135 if !ok { 3136 return "", errors.Errorf("FS was not a KBFS file system: %T", fs) 3137 } 3138 p := fs.Join(midPath, finalElem) 3139 return stdpath.Join( 3140 tlfHandle.GetCanonicalPath(), asLibFS.PathForLogging(p)), nil 3141 } 3142 3143 // SimpleFSDeobfuscatePath implements the SimpleFSInterface. 3144 func (k *SimpleFS) SimpleFSDeobfuscatePath( 3145 ctx context.Context, path keybase1.Path) (res []string, err error) { 3146 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 3147 if err != nil { 3148 return nil, err 3149 } 3150 defer func() { 3151 err := libcontext.CleanupCancellationDelayer(ctx) 3152 if err != nil { 3153 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 3154 } 3155 }() 3156 t, tlfName, midPath, finalElem, err := remoteTlfAndPath(path) 3157 if err != nil { 3158 return nil, err 3159 } 3160 kbpki, err := k.getKBPKI(ctx) 3161 if err != nil { 3162 return nil, err 3163 } 3164 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 3165 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 3166 if err != nil { 3167 return nil, err 3168 } 3169 branch, err := k.branchNameFromPath(ctx, tlfHandle, path) 3170 if err != nil { 3171 return nil, err 3172 } 3173 fs, err := k.newFS( 3174 ctx, k.config, tlfHandle, branch, "", false) 3175 if err != nil { 3176 return nil, err 3177 } 3178 asLibFS, ok := fs.(*libfs.FS) 3179 if !ok { 3180 return nil, errors.Errorf("FS was not a KBFS file system: %T", fs) 3181 } 3182 p := fs.Join(midPath, finalElem) 3183 resWithoutPrefix, err := libfs.Deobfuscate(ctx, asLibFS, p) 3184 if err != nil { 3185 return nil, err 3186 } 3187 for _, r := range resWithoutPrefix { 3188 res = append(res, stdpath.Join(tlfHandle.GetCanonicalPath(), r)) 3189 } 3190 if len(res) == 0 { 3191 return nil, errors.New("Found no matching paths") 3192 } 3193 return res, nil 3194 } 3195 3196 // SimpleFSGetStats implements the SimpleFSInterface. 3197 func (k *SimpleFS) SimpleFSGetStats(ctx context.Context) ( 3198 res keybase1.SimpleFSStats, err error) { 3199 ctx = k.makeContext(ctx) 3200 dbc := k.config.DiskBlockCache() 3201 if dbc == nil { 3202 return keybase1.SimpleFSStats{}, nil 3203 } 3204 3205 res.ProcessStats = runtimestats.GetProcessStats(keybase1.ProcessType_KBFS) 3206 3207 statusMap := dbc.Status(ctx) 3208 if status, ok := statusMap["SyncBlockCache"]; ok { 3209 res.SyncCacheDbStats = status.BlockDBStats 3210 3211 res.RuntimeDbStats = append(res.RuntimeDbStats, 3212 keybase1.DbStats{ 3213 Type: keybase1.DbType_FS_SYNC_BLOCK_CACHE, 3214 MemCompActive: status.MemCompActive, 3215 TableCompActive: status.TableCompActive, 3216 }) 3217 res.RuntimeDbStats = append(res.RuntimeDbStats, 3218 keybase1.DbStats{ 3219 Type: keybase1.DbType_FS_SYNC_BLOCK_CACHE_META, 3220 MemCompActive: status.MetaMemCompActive, 3221 TableCompActive: status.MetaTableCompActive, 3222 }) 3223 } 3224 if status, ok := statusMap["WorkingSetBlockCache"]; ok { 3225 res.BlockCacheDbStats = status.BlockDBStats 3226 res.RuntimeDbStats = append(res.RuntimeDbStats, 3227 keybase1.DbStats{ 3228 Type: keybase1.DbType_FS_BLOCK_CACHE, 3229 MemCompActive: status.MemCompActive, 3230 TableCompActive: status.TableCompActive, 3231 }) 3232 res.RuntimeDbStats = append(res.RuntimeDbStats, 3233 keybase1.DbStats{ 3234 Type: keybase1.DbType_FS_BLOCK_CACHE_META, 3235 MemCompActive: status.MetaMemCompActive, 3236 TableCompActive: status.MetaTableCompActive, 3237 }) 3238 } 3239 return res, nil 3240 } 3241 3242 func (k *SimpleFS) subscriptionManager( 3243 clientID string) libkbfs.SubscriptionManager { 3244 return k.config.SubscriptionManager( 3245 libkbfs.SubscriptionManagerClientID(clientID), true, 3246 k.subscriptionNotifier) 3247 } 3248 3249 // SimpleFSSubscribePath implements the SimpleFSInterface. 3250 func (k *SimpleFS) SimpleFSSubscribePath( 3251 ctx context.Context, arg keybase1.SimpleFSSubscribePathArg) (err error) { 3252 defer func() { 3253 err = k.filterEmptyErr(ctx, arg.KbfsPath, err) 3254 err = translateErr(err) 3255 }() 3256 ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior) 3257 if err != nil { 3258 return err 3259 } 3260 interval := time.Second * time.Duration(arg.DeduplicateIntervalSecond) 3261 return k.subscriptionManager(arg.ClientID).SubscribePath( 3262 ctx, libkbfs.SubscriptionID(arg.SubscriptionID), 3263 arg.KbfsPath, arg.Topic, &interval) 3264 } 3265 3266 // SimpleFSSubscribeNonPath implements the SimpleFSInterface. 3267 func (k *SimpleFS) SimpleFSSubscribeNonPath( 3268 ctx context.Context, arg keybase1.SimpleFSSubscribeNonPathArg) (err error) { 3269 ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior) 3270 if err != nil { 3271 return err 3272 } 3273 interval := time.Second * time.Duration(arg.DeduplicateIntervalSecond) 3274 return k.subscriptionManager(arg.ClientID).SubscribeNonPath( 3275 ctx, libkbfs.SubscriptionID(arg.SubscriptionID), arg.Topic, &interval) 3276 } 3277 3278 // SimpleFSUnsubscribe implements the SimpleFSInterface. 3279 func (k *SimpleFS) SimpleFSUnsubscribe( 3280 ctx context.Context, arg keybase1.SimpleFSUnsubscribeArg) (err error) { 3281 ctx, err = k.makeContextWithIdentifyBehavior(ctx, arg.IdentifyBehavior) 3282 if err != nil { 3283 return err 3284 } 3285 k.subscriptionManager(arg.ClientID).Unsubscribe( 3286 ctx, libkbfs.SubscriptionID(arg.SubscriptionID)) 3287 return nil 3288 } 3289 3290 // SimpleFSStartDownload implements the SimpleFSInterface. 3291 func (k *SimpleFS) SimpleFSStartDownload( 3292 ctx context.Context, arg keybase1.SimpleFSStartDownloadArg) ( 3293 downloadID string, err error) { 3294 return k.getDownloadManager().startDownload(ctx, arg) 3295 } 3296 3297 // SimpleFSGetDownloadStatus implements the SimpleFSInterface. 3298 func (k *SimpleFS) SimpleFSGetDownloadStatus(ctx context.Context) ( 3299 status keybase1.DownloadStatus, err error) { 3300 return k.getDownloadManager().getDownloadStatus(ctx), nil 3301 } 3302 3303 // SimpleFSCancelDownload implements the SimpleFSInterface. 3304 func (k *SimpleFS) SimpleFSCancelDownload( 3305 ctx context.Context, downloadID string) (err error) { 3306 return k.getDownloadManager().cancelDownload(ctx, downloadID) 3307 } 3308 3309 // SimpleFSDismissDownload implements the SimpleFSInterface. 3310 func (k *SimpleFS) SimpleFSDismissDownload( 3311 ctx context.Context, downloadID string) (err error) { 3312 k.getDownloadManager().dismissDownload(ctx, downloadID) 3313 return nil 3314 } 3315 3316 // SimpleFSGetDownloadInfo implements the SimpleFSInterface. 3317 func (k *SimpleFS) SimpleFSGetDownloadInfo( 3318 ctx context.Context, downloadID string) ( 3319 downloadInfo keybase1.DownloadInfo, err error) { 3320 return k.getDownloadManager().getDownloadInfo(downloadID) 3321 } 3322 3323 // SimpleFSConfigureDownload implements the SimpleFSInterface. 3324 func (k *SimpleFS) SimpleFSConfigureDownload( 3325 ctx context.Context, arg keybase1.SimpleFSConfigureDownloadArg) (err error) { 3326 k.getDownloadManager().configureDownload(arg.CacheDirOverride, arg.DownloadDirOverride) 3327 return nil 3328 } 3329 3330 // Copied from net/http/sniff.go: the algorithm uses at most sniffLen bytes to 3331 // make its decision. 3332 const sniffLen = 512 3333 3334 // getContentType detects the content type of the file located at kbfsPath. 3335 // It's adapted from serveContent in net/http/fs.go. The algorithm might change 3336 // in the future, but it's OK as we are using the invariance mechanism and the 3337 // check in libhttpserver happens right before writing the content, using the 3338 // real HTTP headers from the http package. 3339 func (k *SimpleFS) getContentType(ctx context.Context, kbfsPath keybase1.KBFSPath) ( 3340 contentType string, err error) { 3341 contentType = mime.TypeByExtension(filepath.Ext(kbfsPath.Path)) 3342 if len(contentType) > 0 { 3343 return contentType, nil 3344 } 3345 3346 fs, finalElem, err := k.getFS(ctx, keybase1.NewPathWithKbfs(kbfsPath)) 3347 if err != nil { 3348 return "", err 3349 } 3350 f, err := fs.OpenFile(finalElem, os.O_RDONLY, 0644) 3351 if err != nil { 3352 return "", err 3353 } 3354 var buf [sniffLen]byte 3355 n, _ := io.ReadFull(f, buf[:]) 3356 return http.DetectContentType(buf[:n]), nil 3357 } 3358 3359 // SimpleFSGetGUIFileContext implements the SimpleFSInterface. 3360 func (k *SimpleFS) SimpleFSGetGUIFileContext(ctx context.Context, 3361 kbfsPath keybase1.KBFSPath) (resource keybase1.GUIFileContext, err error) { 3362 wrappedPath := keybase1.NewPathWithKbfs(kbfsPath) 3363 ctx, err = k.startSyncOp(ctx, "GetGUIFileContext", "", &wrappedPath, nil) 3364 if err != nil { 3365 return keybase1.GUIFileContext{}, err 3366 } 3367 defer func() { k.doneSyncOp(ctx, err) }() 3368 3369 if len(kbfsPath.Path) == 0 { 3370 return keybase1.GUIFileContext{}, errors.New("empty path") 3371 } 3372 if k.localHTTPServer == nil { 3373 return keybase1.GUIFileContext{}, errors.New("HTTP server is disabled") 3374 } 3375 3376 contentType, err := k.getContentType(ctx, kbfsPath) 3377 if err != nil { 3378 return keybase1.GUIFileContext{}, err 3379 } 3380 viewType, invariance := libhttpserver.GetGUIFileContextFromContentType(contentType) 3381 3382 token, err := k.localHTTPServer.CurrentToken() 3383 if err != nil { 3384 return keybase1.GUIFileContext{}, err 3385 } 3386 address, err := k.localHTTPServer.Address() 3387 if err != nil { 3388 return keybase1.GUIFileContext{}, err 3389 } 3390 3391 u := url.URL{ 3392 Scheme: "http", 3393 Host: address, 3394 Path: path.Join("/files", kbfsPath.Path), 3395 RawQuery: "token=" + token + "&viewTypeInvariance=" + invariance, 3396 } 3397 3398 return keybase1.GUIFileContext{ 3399 ContentType: contentType, 3400 ViewType: viewType, 3401 Url: u.String(), 3402 }, nil 3403 } 3404 3405 const kbfsOpsWaitDuration = 200 * time.Millisecond 3406 const kbfsOpsWaitTimeout = 4 * time.Second 3407 3408 func (k *SimpleFS) waitForKBFSOps(ctx context.Context) error { 3409 for { 3410 if k.config.KBFSOps() != nil { 3411 return nil 3412 } 3413 select { 3414 case <-ctx.Done(): 3415 return ctx.Err() 3416 default: 3417 time.Sleep(kbfsOpsWaitDuration) 3418 } 3419 } 3420 } 3421 3422 // SimpleFSGetFilesTabBadge implements the SimpleFSInterface. 3423 func (k *SimpleFS) SimpleFSGetFilesTabBadge(ctx context.Context) ( 3424 keybase1.FilesTabBadge, error) { 3425 ctx, cancel := context.WithTimeout(ctx, kbfsOpsWaitTimeout) 3426 defer cancel() 3427 if err := k.waitForKBFSOps(ctx); err != nil { 3428 return 0, err 3429 } 3430 return k.config.KBFSOps().GetBadge(ctx) 3431 } 3432 3433 // SimpleFSSetSfmiBannerDismissed implements the SimpleFSInterface. 3434 func (k *SimpleFS) SimpleFSSetSfmiBannerDismissed( 3435 ctx context.Context, dismissed bool) (err error) { 3436 defer func() { 3437 k.log.CDebugf(ctx, "SimpleFSSetSfmiBannerDismissed err=%+v", err) 3438 }() 3439 db := k.config.GetSettingsDB() 3440 if db == nil { 3441 return libkbfs.ErrNoSettingsDB 3442 } 3443 if err = db.SetSfmiBannerDismissed(ctx, dismissed); err != nil { 3444 return err 3445 } 3446 k.config.SubscriptionManagerPublisher().PublishChange(keybase1.SubscriptionTopic_SETTINGS) 3447 return nil 3448 } 3449 3450 // SimpleFSSetSyncOnCellular implements the SimpleFSInterface. 3451 func (k *SimpleFS) SimpleFSSetSyncOnCellular( 3452 ctx context.Context, syncOnCellular bool) (err error) { 3453 defer func() { 3454 k.log.CDebugf(ctx, "SimpleFSSetSyncOnCellular err=%+v", err) 3455 }() 3456 db := k.config.GetSettingsDB() 3457 if db == nil { 3458 return libkbfs.ErrNoSettingsDB 3459 } 3460 if err = db.SetSyncOnCellular(ctx, syncOnCellular); err != nil { 3461 return err 3462 } 3463 k.config.SubscriptionManagerPublisher().PublishChange( 3464 keybase1.SubscriptionTopic_SETTINGS) 3465 return nil 3466 } 3467 3468 // SimpleFSSearch implements the SimpleFSInterface. 3469 func (k *SimpleFS) SimpleFSSearch( 3470 ctx context.Context, arg keybase1.SimpleFSSearchArg) ( 3471 res keybase1.SimpleFSSearchResults, err error) { 3472 if k.getIndexer() == nil { 3473 return keybase1.SimpleFSSearchResults{}, 3474 errors.New("Indexing not enabled") 3475 } 3476 3477 results, nextResult, err := k.getIndexer().Search( 3478 ctx, arg.Query, arg.NumResults, arg.StartingFrom) 3479 if err != nil { 3480 return keybase1.SimpleFSSearchResults{}, err 3481 } 3482 3483 res.Hits = make([]keybase1.SimpleFSSearchHit, len(results)) 3484 for i, result := range results { 3485 res.Hits[i].Path = result.Path 3486 } 3487 res.NextResult = nextResult 3488 return res, nil 3489 } 3490 3491 // SimpleFSResetIndex implements the SimpleFSInterface. 3492 func (k *SimpleFS) SimpleFSResetIndex(ctx context.Context) error { 3493 if k.getIndexer() == nil { 3494 return errors.New("Indexing not enabled") 3495 } 3496 3497 return k.getIndexer().ResetIndex(ctx) 3498 } 3499 3500 // SimpleFSGetIndexProgress implements the SimpleFSInterface. 3501 func (k *SimpleFS) SimpleFSGetIndexProgress( 3502 ctx context.Context) (res keybase1.SimpleFSIndexProgress, err error) { 3503 if k.getIndexer() == nil { 3504 return keybase1.SimpleFSIndexProgress{}, errors.New( 3505 "Indexing not enabled") 3506 } 3507 3508 p := k.getIndexer().Progress() 3509 currProg, overallProg, currTlf, queuedTlfs := p.GetStatus() 3510 res.CurrProgress = currProg 3511 res.OverallProgress = overallProg 3512 3513 if currTlf == tlf.NullID && len(queuedTlfs) == 0 { 3514 return res, nil 3515 } 3516 3517 // All indexing folders should also be synced. 3518 tlfMDs := k.config.KBFSOps().GetAllSyncedTlfMDs(ctx) 3519 kbpki, err := k.getKBPKI(ctx) 3520 if err != nil { 3521 return keybase1.SimpleFSIndexProgress{}, err 3522 } 3523 session, err := idutil.GetCurrentSessionIfPossible(ctx, kbpki, true) 3524 if err != nil { 3525 return keybase1.SimpleFSIndexProgress{}, err 3526 } 3527 3528 if currTlf != tlf.NullID { 3529 md, ok := tlfMDs[currTlf] 3530 if !ok { 3531 return keybase1.SimpleFSIndexProgress{}, errors.Errorf( 3532 "Folder %s is not currently syncing", currTlf) 3533 } 3534 f, err := k.getFolder(ctx, currTlf, md, session) 3535 if err != nil { 3536 return keybase1.SimpleFSIndexProgress{}, err 3537 } 3538 res.CurrFolder = f 3539 } 3540 3541 res.FoldersLeft = make([]keybase1.Folder, 0, len(queuedTlfs)) 3542 for _, id := range queuedTlfs { 3543 md, ok := tlfMDs[id] 3544 if !ok { 3545 return keybase1.SimpleFSIndexProgress{}, errors.Errorf( 3546 "Folder %s is not currently syncing", id) 3547 } 3548 f, err := k.getFolder(ctx, id, md, session) 3549 if err != nil { 3550 return keybase1.SimpleFSIndexProgress{}, err 3551 } 3552 res.FoldersLeft = append(res.FoldersLeft, f) 3553 } 3554 3555 return res, nil 3556 } 3557 3558 // SimpleFSMakeTempDirForUpload implements the SimpleFSInterface. 3559 func (k *SimpleFS) SimpleFSMakeTempDirForUpload( 3560 ctx context.Context) (dirPath string, err error) { 3561 return k.getUploadManager().makeTempDir() 3562 } 3563 3564 // SimpleFSStartUpload implements the SimpleFSInterface. 3565 func (k *SimpleFS) SimpleFSStartUpload(ctx context.Context, 3566 arg keybase1.SimpleFSStartUploadArg) (uploadID string, err error) { 3567 return k.getUploadManager().start(ctx, arg.SourceLocalPath, arg.TargetParentPath) 3568 } 3569 3570 // SimpleFSGetUploadStatus implements the SimpleFSInterface. 3571 func (k *SimpleFS) SimpleFSGetUploadStatus( 3572 ctx context.Context) (status []keybase1.UploadState, err error) { 3573 return k.getUploadManager().getUploads(), nil 3574 } 3575 3576 // SimpleFSCancelUpload implements the SimpleFSInterface. 3577 func (k *SimpleFS) SimpleFSCancelUpload( 3578 ctx context.Context, uploadID string) (err error) { 3579 return k.getUploadManager().cancel(ctx, uploadID) 3580 } 3581 3582 // SimpleFSDismissUpload implements the SimpleFSInterface. 3583 func (k *SimpleFS) SimpleFSDismissUpload( 3584 ctx context.Context, uploadID string) (err error) { 3585 return k.getUploadManager().dismiss(uploadID) 3586 } 3587 3588 // SimpleFSCancelJournalUploads implements the SimpleFSInterface. 3589 func (k *SimpleFS) SimpleFSCancelJournalUploads( 3590 ctx context.Context, path keybase1.KBFSPath) (err error) { 3591 wrappedPath := keybase1.NewPathWithKbfs(path) 3592 ctx, err = populateIdentifyBehaviorIfNeeded(ctx, &wrappedPath, nil) 3593 if err != nil { 3594 return err 3595 } 3596 ctx, err = k.startOpWrapContext(k.makeContext(ctx)) 3597 if err != nil { 3598 return err 3599 } 3600 defer func() { 3601 err := libcontext.CleanupCancellationDelayer(ctx) 3602 if err != nil { 3603 k.log.CDebugf(ctx, "Error cancelling delayer: %+v", err) 3604 } 3605 }() 3606 t, tlfName, _, _, err := remoteTlfAndPath(wrappedPath) 3607 if err != nil { 3608 return err 3609 } 3610 kbpki, err := k.getKBPKI(ctx) 3611 if err != nil { 3612 return err 3613 } 3614 tlfHandle, err := libkbfs.GetHandleFromFolderNameAndType( 3615 ctx, kbpki, k.config.MDOps(), k.config, tlfName, t) 3616 if err != nil { 3617 return err 3618 } 3619 tlfID := tlfHandle.TlfID() 3620 branch, err := k.branchNameFromPath(ctx, tlfHandle, wrappedPath) 3621 if err != nil { 3622 return err 3623 } 3624 return k.config.KBFSOps().CancelUploads( 3625 ctx, data.FolderBranch{ 3626 Tlf: tlfID, 3627 Branch: branch, 3628 }) 3629 } 3630 3631 var cacheDirForTest = "" 3632 3633 func setCacheDirForTest(d string) { 3634 cacheDirForTest = d 3635 } 3636 3637 func unsetCacheDirForTest() { 3638 cacheDirForTest = "" 3639 } 3640 3641 func (k *SimpleFS) getCacheDir() string { 3642 if len(cacheDirForTest) != 0 { 3643 return cacheDirForTest 3644 } 3645 return k.config.KbEnv().GetCacheDir() 3646 } 3647 3648 func generateArchiveJobID() (string, error) { 3649 buf := make([]byte, 8) 3650 err := kbfscrypto.RandRead(buf) 3651 if err != nil { 3652 return "", err 3653 } 3654 return fmt.Sprintf("kbfs-archive-job-%s", 3655 base64.RawURLEncoding.EncodeToString(buf)), nil 3656 } 3657 3658 func parseGitRepoForKBFSPath(gitRepo string) (kbfsPath keybase1.KBFSPath, err error) { 3659 tlfType, tlfName, repoName, err := kbfsgit.ParseRepo(gitRepo) 3660 if err != nil { 3661 return keybase1.KBFSPath{}, err 3662 } 3663 return keybase1.KBFSPath{ 3664 Path: fmt.Sprintf("/%s/%s/.kbfs_git/%s", tlfType.PathString(), tlfName, repoName), 3665 }, nil 3666 } 3667 3668 var gitKBFSPathRE = regexp.MustCompile(`^\/(private|public|team)\/(.+)\/\.kbfs_git\/(` + libgit.RepoNameRE + `)$`) 3669 3670 func parseKbfsPathForGitRepo(kbfsPath keybase1.KBFSPath) (gitRepo string, repoNameWithDotGit string, ok bool) { 3671 match := gitKBFSPathRE.FindStringSubmatch(kbfsPath.Path) 3672 if len(match) != 4 { 3673 return "", "", false 3674 } 3675 return fmt.Sprintf("keybase://%s/%s/%s.git", match[1], match[2], match[3]), match[3] + ".git", true 3676 } 3677 3678 // SimpleFSArchiveStart implements the SimpleFSInterface. 3679 func (k *SimpleFS) SimpleFSArchiveStart(ctx context.Context, 3680 arg keybase1.SimpleFSArchiveStartArg) (jobDesc keybase1.SimpleFSArchiveJobDesc, err error) { 3681 ctx = k.makeContext(ctx) 3682 3683 desc := keybase1.SimpleFSArchiveJobDesc{ 3684 StartTime: keybase1.ToTime(time.Now()), 3685 OverwriteZip: arg.OverwriteZip, 3686 } 3687 3688 desc.JobID, err = generateArchiveJobID() 3689 if err != nil { 3690 return keybase1.SimpleFSArchiveJobDesc{}, err 3691 } 3692 desc.StagingPath = k.getArchiveManager().getStagingPath(ctx, desc.JobID) 3693 3694 var kbfsPath keybase1.KBFSPath 3695 { 3696 typ, err := arg.ArchiveJobStartPath.ArchiveJobStartPathType() 3697 if err != nil { 3698 return keybase1.SimpleFSArchiveJobDesc{}, 3699 fmt.Errorf("getting ArchiveJobStartPathType error: %v", err) 3700 } 3701 switch typ { 3702 case keybase1.ArchiveJobStartPathType_KBFS: 3703 kbfsPath = arg.ArchiveJobStartPath.Kbfs() 3704 p, err := splitPathFromKbfsPath(keybase1.NewPathWithKbfs(kbfsPath)) 3705 if err != nil { 3706 return keybase1.SimpleFSArchiveJobDesc{}, err 3707 } 3708 if len(p) == 0 { 3709 return keybase1.SimpleFSArchiveJobDesc{}, 3710 errors.New("unexpected number of elements from splitPathFromKbfsPath") 3711 } 3712 desc.TargetName = p[len(p)-1] 3713 3714 case keybase1.ArchiveJobStartPathType_GIT: 3715 kbfsPath, err = parseGitRepoForKBFSPath(arg.ArchiveJobStartPath.Git()) 3716 if err != nil { 3717 return keybase1.SimpleFSArchiveJobDesc{}, 3718 fmt.Errorf("parsing git repo error: %v", err) 3719 } 3720 default: 3721 return keybase1.SimpleFSArchiveJobDesc{}, 3722 fmt.Errorf("unexpected ArchiveJobStartPathType: %v", typ) 3723 } 3724 } 3725 3726 // Do this separately rather than in the ArchiveJobStartPathType_GIT branch 3727 // so it also covers the case where user browses into the `.kbfs_git` 3728 // directory and archive. 3729 gitRepo, repoName, ok := parseKbfsPathForGitRepo(kbfsPath) 3730 if ok { 3731 desc.GitRepo = &gitRepo 3732 desc.TargetName = repoName 3733 } 3734 3735 desc.ZipFilePath = arg.OutputPath 3736 if len(desc.ZipFilePath) == 0 { 3737 // No zip file path is given. Assume mobile-like behavior where we 3738 // generate a zip file inside the staging path. A share sheet will 3739 // allow the user to download the zip file, and when user dismisses the 3740 // job, the zip file along with other stuff in the staging path is 3741 // deleted. 3742 desc.ZipFilePath = filepath.Join(desc.StagingPath, desc.TargetName+".zip") 3743 } else if !strings.HasSuffix(desc.ZipFilePath, ".zip") { 3744 desc.ZipFilePath += ".zip" 3745 } 3746 3747 // Pin the job to a specific revision so if the TLF changes during the 3748 // archive we don't end up mixing two different revisions. 3749 { 3750 fb, _, err := k.getFolderBranchFromPath(ctx, keybase1.NewPathWithKbfs(kbfsPath)) 3751 if err != nil { 3752 return keybase1.SimpleFSArchiveJobDesc{}, err 3753 } 3754 if fb == (data.FolderBranch{}) { 3755 return keybase1.SimpleFSArchiveJobDesc{}, nil 3756 } 3757 status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb) 3758 if err != nil { 3759 return keybase1.SimpleFSArchiveJobDesc{}, err 3760 } 3761 desc.KbfsPathWithRevision = keybase1.KBFSArchivedPath{ 3762 Path: kbfsPath.Path, 3763 ArchivedParam: keybase1.NewKBFSArchivedParamWithRevision( 3764 keybase1.KBFSRevision(status.Revision)), 3765 } 3766 } 3767 3768 err = k.getArchiveManager().startJob(ctx, desc) 3769 return desc, err 3770 } 3771 3772 // SimpleFSArchiveCancelOrDismissJob implements the SimpleFSInterface. 3773 func (k *SimpleFS) SimpleFSArchiveCancelOrDismissJob(ctx context.Context, 3774 jobID string) (err error) { 3775 ctx = k.makeContext(ctx) 3776 return k.getArchiveManager().cancelOrDismissJob(ctx, jobID) 3777 } 3778 3779 func (k *SimpleFS) getCurrentTLFRevision( 3780 ctx context.Context, kbfsPath keybase1.KBFSPath) ( 3781 keybase1.KBFSRevision, error) { 3782 fb, _, err := k.getFolderBranchFromPath(ctx, 3783 keybase1.NewPathWithKbfs(kbfsPath)) 3784 if err != nil { 3785 return 0, err 3786 } 3787 if fb == (data.FolderBranch{}) { 3788 return 0, nil 3789 } 3790 status, _, err := k.config.KBFSOps().FolderStatus(ctx, fb) 3791 if err != nil { 3792 return 0, err 3793 } 3794 return keybase1.KBFSRevision(status.Revision), nil 3795 } 3796 3797 // SimpleFSArchiveCheckArchive implements the SimpleFSInterface. 3798 func (k *SimpleFS) SimpleFSArchiveCheckArchive(ctx context.Context, 3799 archiveZipFilePath string) (result keybase1.SimpleFSArchiveCheckArchiveResult, err error) { 3800 ctx = k.makeContext(ctx) 3801 result.Desc, result.PathsWithIssues, err = k.getArchiveManager().checkArchive(ctx, archiveZipFilePath) 3802 if err != nil { 3803 return keybase1.SimpleFSArchiveCheckArchiveResult{}, err 3804 } 3805 result.CurrentTLFRevision, err = k.getCurrentTLFRevision(ctx, 3806 keybase1.KBFSPath{Path: result.Desc.KbfsPathWithRevision.Path}) 3807 if err != nil { 3808 return keybase1.SimpleFSArchiveCheckArchiveResult{}, err 3809 } 3810 return result, nil 3811 } 3812 3813 func (k *SimpleFS) archiveStateToStatus(ctx context.Context, 3814 state keybase1.SimpleFSArchiveState, errorStates map[string]errorState) ( 3815 status keybase1.SimpleFSArchiveStatus, err error) { 3816 status = keybase1.SimpleFSArchiveStatus{ 3817 LastUpdated: state.LastUpdated, 3818 Jobs: make([]keybase1.SimpleFSArchiveJobStatus, 0, len(state.Jobs)), 3819 } 3820 for jobID, stateJob := range state.Jobs { 3821 statusJob := keybase1.SimpleFSArchiveJobStatus{ 3822 Desc: stateJob.Desc.DeepCopy(), 3823 TotalCount: len(stateJob.Manifest), 3824 Phase: stateJob.Phase, 3825 BytesCopied: stateJob.BytesCopied, 3826 BytesZipped: stateJob.BytesZipped, 3827 BytesTotal: stateJob.BytesTotal, 3828 } 3829 for _, item := range stateJob.Manifest { 3830 switch item.State { 3831 case keybase1.SimpleFSFileArchiveState_ToDo: 3832 statusJob.TodoCount++ 3833 case keybase1.SimpleFSFileArchiveState_InProgress: 3834 statusJob.InProgressCount++ 3835 case keybase1.SimpleFSFileArchiveState_Complete: 3836 statusJob.CompleteCount++ 3837 case keybase1.SimpleFSFileArchiveState_Skipped: 3838 statusJob.SkippedCount++ 3839 } 3840 } 3841 if errState, ok := errorStates[jobID]; ok { 3842 statusJob.Error = &keybase1.SimpleFSArchiveJobErrorState{ 3843 Error: errState.err.Error(), 3844 NextRetry: keybase1.ToTime(errState.nextRetry), 3845 } 3846 } 3847 status.Jobs = append(status.Jobs, statusJob) 3848 } 3849 sort.Slice(status.Jobs, func(i, j int) bool { 3850 return status.Jobs[i].Desc.StartTime.Before(status.Jobs[j].Desc.StartTime) 3851 }) 3852 return status, nil 3853 } 3854 3855 // SimpleFSGetArchiveStatus implements the SimpleFSInterface. 3856 func (k *SimpleFS) SimpleFSGetArchiveStatus(ctx context.Context) ( 3857 status keybase1.SimpleFSArchiveStatus, err error) { 3858 ctx = k.makeContext(ctx) 3859 state, errorStates := k.getArchiveManager().getCurrentState(ctx) 3860 return k.archiveStateToStatus(ctx, state, errorStates) 3861 } 3862 3863 // SimpleFSGetArchiveJobFreshness implements the SimpleFSInterface. 3864 func (k *SimpleFS) SimpleFSGetArchiveJobFreshness(ctx context.Context, jobID string) (keybase1.SimpleFSArchiveJobFreshness, error) { 3865 ctx = k.makeContext(ctx) 3866 state, _ := k.getArchiveManager().getCurrentState(ctx) 3867 stateJob, ok := state.Jobs[jobID] 3868 if !ok { 3869 return keybase1.SimpleFSArchiveJobFreshness{}, fmt.Errorf("job not found: %s", jobID) 3870 } 3871 rev, err := k.getCurrentTLFRevision(ctx, 3872 keybase1.KBFSPath{Path: stateJob.Desc.KbfsPathWithRevision.Path}) 3873 if err != nil { 3874 return keybase1.SimpleFSArchiveJobFreshness{}, err 3875 } 3876 return keybase1.SimpleFSArchiveJobFreshness{ 3877 CurrentTLFRevision: rev, 3878 }, nil 3879 } 3880 3881 // SimpleFSArchiveAllFiles implements the SimpleFSInterface. 3882 func (k *SimpleFS) SimpleFSArchiveAllFiles( 3883 ctx context.Context, arg keybase1.SimpleFSArchiveAllFilesArg) ( 3884 res keybase1.SimpleFSArchiveAllFilesResult, err error) { 3885 var startArgs []keybase1.SimpleFSArchiveStartArg 3886 { 3887 fl, err := k.favoriteList(ctx, tlf.Private) 3888 if err != nil { 3889 return keybase1.SimpleFSArchiveAllFilesResult{}, err 3890 } 3891 for _, item := range fl { 3892 startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{ 3893 ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{ 3894 Path: fmt.Sprintf("/private/%s", item.Name), 3895 }), 3896 OutputPath: filepath.Join(arg.OutputDir, "kbfs-private-"+item.Name+".zip"), 3897 OverwriteZip: arg.OverwriteZip, 3898 }) 3899 } 3900 3901 fl, err = k.favoriteList(ctx, tlf.Public) 3902 if err != nil { 3903 return keybase1.SimpleFSArchiveAllFilesResult{}, err 3904 } 3905 for _, item := range fl { 3906 p := fmt.Sprintf("/public/%s", item.Name) 3907 if arg.IncludePublicReadonly || item.Writable { 3908 startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{ 3909 ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{ 3910 Path: p, 3911 }), 3912 OutputPath: filepath.Join(arg.OutputDir, "kbfs-public-"+item.Name+".zip"), 3913 OverwriteZip: arg.OverwriteZip, 3914 }) 3915 } else { 3916 res.SkippedTLFPaths = append(res.SkippedTLFPaths, p) 3917 } 3918 } 3919 3920 fl, err = k.favoriteList(ctx, tlf.SingleTeam) 3921 if err != nil { 3922 return keybase1.SimpleFSArchiveAllFilesResult{}, err 3923 } 3924 for _, item := range fl { 3925 startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{ 3926 ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithKbfs(keybase1.KBFSPath{ 3927 Path: fmt.Sprintf("/team/%s", item.Name), 3928 }), 3929 OutputPath: filepath.Join(arg.OutputDir, "kbfs-team-"+item.Name+".zip"), 3930 OverwriteZip: arg.OverwriteZip, 3931 }) 3932 } 3933 } 3934 3935 res.TlfPathToError = make(map[string]string) 3936 res.TlfPathToJobDesc = make(map[string]keybase1.SimpleFSArchiveJobDesc) 3937 for _, a := range startArgs { 3938 jobDesc, err := k.SimpleFSArchiveStart(ctx, a) 3939 if err != nil { 3940 res.TlfPathToError[a.ArchiveJobStartPath.Kbfs().Path] = err.Error() 3941 } else { 3942 res.TlfPathToJobDesc[a.ArchiveJobStartPath.Kbfs().Path] = jobDesc 3943 } 3944 } 3945 3946 return res, nil 3947 } 3948 3949 func (k *SimpleFS) getAllGitMetadata(ctx context.Context) (res []keybase1.GitRepoResult, err error) { 3950 ks := k.config.KeybaseService() 3951 if ks == nil { 3952 k.log.CWarningf(ctx, "k.getAllGitMetadata: ks is nil") 3953 return nil, errors.New("ks is nil") 3954 } 3955 rc := ks.GetKeybaseDaemonRawClient() 3956 if rc == nil { 3957 k.log.CWarningf(ctx, "k.getAllGitMetadata: rawClient is nil") 3958 return nil, errors.New("rawClient is nil") 3959 } 3960 client := keybase1.GitClient{Cli: rc} 3961 res, err = client.GetAllGitMetadata(ctx) 3962 if err != nil { 3963 k.log.CWarningf(ctx, "k.getAllGitMetadata: client.GetAllGitMetadata error: %v", err) 3964 return nil, err 3965 } 3966 return res, nil 3967 } 3968 3969 // SimpleFSArchiveAllGitRepos implements the SimpleFSInterface. 3970 func (k *SimpleFS) SimpleFSArchiveAllGitRepos( 3971 ctx context.Context, arg keybase1.SimpleFSArchiveAllGitReposArg) ( 3972 res keybase1.SimpleFSArchiveAllGitReposResult, err error) { 3973 allGitMetadataResult, err := k.getAllGitMetadata(ctx) 3974 if err != nil { 3975 return keybase1.SimpleFSArchiveAllGitReposResult{}, err 3976 } 3977 3978 var startArgs []keybase1.SimpleFSArchiveStartArg 3979 for _, gitRepoResult := range allGitMetadataResult { 3980 state, err := gitRepoResult.State() 3981 if err != nil { 3982 return keybase1.SimpleFSArchiveAllGitReposResult{}, 3983 fmt.Errorf("gitRepoResult.State() error: %v", err) 3984 } 3985 switch state { 3986 case keybase1.GitRepoResultState_OK: 3987 gitRepoInfo := gitRepoResult.Ok() 3988 folderTypeStr, err := func() (string, error) { 3989 switch gitRepoInfo.Folder.FolderType { 3990 case keybase1.FolderType_PRIVATE: 3991 return "private", nil 3992 case keybase1.FolderType_TEAM: 3993 return "team", nil 3994 default: 3995 return "", fmt.Errorf("unexpected FolderType: %v", gitRepoInfo.Folder.FolderType) 3996 } 3997 }() 3998 if err != nil { 3999 return keybase1.SimpleFSArchiveAllGitReposResult{}, err 4000 } 4001 startArgs = append(startArgs, keybase1.SimpleFSArchiveStartArg{ 4002 ArchiveJobStartPath: keybase1.NewArchiveJobStartPathWithGit(gitRepoInfo.RepoUrl), 4003 OutputPath: filepath.Join(arg.OutputDir, "git-"+folderTypeStr+"-"+string(gitRepoInfo.LocalMetadata.RepoName)+".git.zip"), 4004 OverwriteZip: arg.OverwriteZip, 4005 }) 4006 case keybase1.GitRepoResultState_ERR: 4007 return keybase1.SimpleFSArchiveAllGitReposResult{}, 4008 fmt.Errorf("getRepoResult result error: %v", gitRepoResult.Err()) 4009 default: 4010 return keybase1.SimpleFSArchiveAllGitReposResult{}, fmt.Errorf("unexpected gitRepoResult.State() %v", state) 4011 4012 } 4013 } 4014 4015 res.GitRepoToError = make(map[string]string) 4016 res.GitRepoToJobDesc = make(map[string]keybase1.SimpleFSArchiveJobDesc) 4017 for _, a := range startArgs { 4018 jobDesc, err := k.SimpleFSArchiveStart(ctx, a) 4019 if err != nil { 4020 res.GitRepoToError[a.ArchiveJobStartPath.Git()] = err.Error() 4021 } else { 4022 res.GitRepoToJobDesc[a.ArchiveJobStartPath.Git()] = jobDesc 4023 } 4024 } 4025 4026 return res, nil 4027 } 4028 4029 func (k *SimpleFS) notifyUIArchiveStateChange(ctx context.Context, 4030 state keybase1.SimpleFSArchiveState, errorStates map[string]errorState) { 4031 ks := k.config.KeybaseService() 4032 if ks == nil { 4033 k.log.CWarningf(ctx, 4034 "k.notifyUIArchiveStateChange: skipping notification because KeybaseService() is nil") 4035 return 4036 } 4037 rc := ks.GetKeybaseDaemonRawClient() 4038 if rc == nil { 4039 k.log.CWarningf(ctx, 4040 "k.notifyUIArchiveStateChange: skipping notification because rawClient is nil") 4041 return 4042 } 4043 client := keybase1.NotifySimpleFSClient{Cli: rc} 4044 4045 status, err := k.archiveStateToStatus(ctx, state, errorStates) 4046 if err != nil { 4047 k.log.CWarningf(ctx, 4048 "k.archiveStateToStatus error: %v", err) 4049 } 4050 err = client.SimpleFSArchiveStatusChanged(ctx, status) 4051 if err != nil { 4052 k.log.CWarningf(ctx, 4053 "sending SimpleFSArchiveStatusChanged notification error: %v", err) 4054 } 4055 } 4056 4057 // Shutdown shuts down SimpleFS. 4058 func (k *SimpleFS) Shutdown(ctx context.Context) error { 4059 k.shutdownLock.Lock() 4060 defer k.shutdownLock.Unlock() 4061 4062 k.archiveManager.shutdown(ctx) 4063 if k.indexer == nil { 4064 return nil 4065 } 4066 return k.indexer.Shutdown(ctx) 4067 }