github.com/cyverse/go-irodsclient@v0.13.2/fs/fs.go (about) 1 package fs 2 3 import ( 4 "path" 5 "sync" 6 "time" 7 8 "github.com/cyverse/go-irodsclient/irods/connection" 9 irods_fs "github.com/cyverse/go-irodsclient/irods/fs" 10 "github.com/cyverse/go-irodsclient/irods/metrics" 11 "github.com/cyverse/go-irodsclient/irods/session" 12 "github.com/cyverse/go-irodsclient/irods/types" 13 "github.com/cyverse/go-irodsclient/irods/util" 14 "github.com/rs/xid" 15 "golang.org/x/xerrors" 16 ) 17 18 // FileSystem provides a file-system like interface 19 type FileSystem struct { 20 id string 21 account *types.IRODSAccount 22 config *FileSystemConfig 23 ioSession *session.IRODSSession 24 metaSession *session.IRODSSession 25 cache *FileSystemCache 26 cachePropagation *FileSystemCachePropagation 27 cacheEventHandlerMap *FilesystemCacheEventHandlerMap 28 fileHandleMap *FileHandleMap 29 } 30 31 // NewFileSystem creates a new FileSystem 32 func NewFileSystem(account *types.IRODSAccount, config *FileSystemConfig) (*FileSystem, error) { 33 ioSessionConfig := session.NewIRODSSessionConfig(config.ApplicationName, config.ConnectionErrorTimeout, config.ConnectionInitNumber, config.ConnectionLifespan, config.OperationTimeout, config.ConnectionIdleTimeout, config.ConnectionMax, config.TCPBufferSize, config.StartNewTransaction) 34 ioSession, err := session.NewIRODSSession(account, ioSessionConfig) 35 if err != nil { 36 return nil, err 37 } 38 39 metaSessionConfig := session.NewIRODSSessionConfig(config.ApplicationName, config.ConnectionErrorTimeout, config.ConnectionInitNumber, config.ConnectionLifespan, config.OperationTimeout, config.ConnectionIdleTimeout, FileSystemConnectionMetaDefault, config.TCPBufferSize, config.StartNewTransaction) 40 metaSession, err := session.NewIRODSSession(account, metaSessionConfig) 41 if err != nil { 42 return nil, err 43 } 44 45 ioTransactionFailureHandler := func(commitFail bool, poormansRollbackFail bool) { 46 metaSession.SetCommitFail(commitFail) 47 metaSession.SetPoormansRollbackFail(poormansRollbackFail) 48 } 49 50 metaTransactionFailureHandler := func(commitFail bool, poormansRollbackFail bool) { 51 ioSession.SetCommitFail(commitFail) 52 ioSession.SetPoormansRollbackFail(poormansRollbackFail) 53 } 54 55 ioSession.SetTransactionFailureHandler(ioTransactionFailureHandler) 56 metaSession.SetTransactionFailureHandler(metaTransactionFailureHandler) 57 58 cache := NewFileSystemCache(config.CacheTimeout, config.CacheCleanupTime, config.CacheTimeoutSettings, config.InvalidateParentEntryCacheImmediately) 59 60 fs := &FileSystem{ 61 id: xid.New().String(), // generate a new ID 62 account: account, 63 config: config, 64 ioSession: ioSession, 65 metaSession: metaSession, 66 cache: cache, 67 cacheEventHandlerMap: NewFilesystemCacheEventHandlerMap(), 68 fileHandleMap: NewFileHandleMap(), 69 } 70 71 cachePropagation := NewFileSystemCachePropagation(fs) 72 fs.cachePropagation = cachePropagation 73 74 return fs, nil 75 } 76 77 // NewFileSystemWithDefault creates a new FileSystem with default configurations 78 func NewFileSystemWithDefault(account *types.IRODSAccount, applicationName string) (*FileSystem, error) { 79 config := NewFileSystemConfigWithDefault(applicationName) 80 ioSessionConfig := session.NewIRODSSessionConfig(config.ApplicationName, config.ConnectionErrorTimeout, config.ConnectionInitNumber, config.ConnectionLifespan, config.OperationTimeout, config.ConnectionIdleTimeout, config.ConnectionMax, config.TCPBufferSize, config.StartNewTransaction) 81 ioSession, err := session.NewIRODSSession(account, ioSessionConfig) 82 if err != nil { 83 return nil, err 84 } 85 86 metaSessionConfig := session.NewIRODSSessionConfig(config.ApplicationName, config.ConnectionErrorTimeout, config.ConnectionInitNumber, config.ConnectionLifespan, config.OperationTimeout, config.ConnectionIdleTimeout, FileSystemConnectionMetaDefault, config.TCPBufferSize, config.StartNewTransaction) 87 metaSession, err := session.NewIRODSSession(account, metaSessionConfig) 88 if err != nil { 89 return nil, err 90 } 91 92 cache := NewFileSystemCache(config.CacheTimeout, config.CacheCleanupTime, config.CacheTimeoutSettings, config.InvalidateParentEntryCacheImmediately) 93 94 fs := &FileSystem{ 95 id: xid.New().String(), // generate a new ID 96 account: account, 97 config: config, 98 ioSession: ioSession, 99 metaSession: metaSession, 100 cache: cache, 101 cacheEventHandlerMap: NewFilesystemCacheEventHandlerMap(), 102 fileHandleMap: NewFileHandleMap(), 103 } 104 105 cachePropagation := NewFileSystemCachePropagation(fs) 106 fs.cachePropagation = cachePropagation 107 108 return fs, nil 109 } 110 111 // NewFileSystemWithSessionConfig creates a new FileSystem with custom session configurations 112 func NewFileSystemWithSessionConfig(account *types.IRODSAccount, sessConfig *session.IRODSSessionConfig) (*FileSystem, error) { 113 config := NewFileSystemConfigWithDefault(sessConfig.ApplicationName) 114 ioSession, err := session.NewIRODSSession(account, sessConfig) 115 if err != nil { 116 return nil, err 117 } 118 119 metaSessionConfig := session.NewIRODSSessionConfig(config.ApplicationName, config.ConnectionErrorTimeout, config.ConnectionInitNumber, config.ConnectionLifespan, config.OperationTimeout, config.ConnectionIdleTimeout, FileSystemConnectionMetaDefault, config.TCPBufferSize, config.StartNewTransaction) 120 metaSession, err := session.NewIRODSSession(account, metaSessionConfig) 121 if err != nil { 122 return nil, err 123 } 124 125 cache := NewFileSystemCache(config.CacheTimeout, config.CacheCleanupTime, config.CacheTimeoutSettings, config.InvalidateParentEntryCacheImmediately) 126 127 fs := &FileSystem{ 128 id: xid.New().String(), // generate a new ID 129 account: account, 130 config: config, 131 ioSession: ioSession, 132 metaSession: metaSession, 133 cache: cache, 134 cacheEventHandlerMap: NewFilesystemCacheEventHandlerMap(), 135 fileHandleMap: NewFileHandleMap(), 136 } 137 138 cachePropagation := NewFileSystemCachePropagation(fs) 139 fs.cachePropagation = cachePropagation 140 141 return fs, nil 142 } 143 144 // Release releases all resources 145 func (fs *FileSystem) Release() { 146 handles := fs.fileHandleMap.PopAll() 147 for _, handle := range handles { 148 handle.Close() 149 } 150 151 fs.cacheEventHandlerMap.Release() 152 fs.cachePropagation.Release() 153 154 fs.ioSession.Release() 155 fs.metaSession.Release() 156 } 157 158 // GetID returns file system instance ID 159 func (fs *FileSystem) GetID() string { 160 return fs.id 161 } 162 163 // GetIOConnection returns irods connection for IO 164 func (fs *FileSystem) GetIOConnection() (*connection.IRODSConnection, error) { 165 return fs.ioSession.AcquireConnection() 166 } 167 168 // ReturnIOConnection returns irods connection for IO back to session 169 func (fs *FileSystem) ReturnIOConnection(conn *connection.IRODSConnection) { 170 fs.ioSession.ReturnConnection(conn) 171 } 172 173 // GetMetadataConnection returns irods connection for metadata operations 174 func (fs *FileSystem) GetMetadataConnection() (*connection.IRODSConnection, error) { 175 return fs.metaSession.AcquireConnection() 176 } 177 178 // ReturnMetadataConnection returns irods connection for metadata operations back to session 179 func (fs *FileSystem) ReturnMetadataConnection(conn *connection.IRODSConnection) { 180 fs.metaSession.ReturnConnection(conn) 181 } 182 183 // ConnectionTotal counts current established connections 184 func (fs *FileSystem) ConnectionTotal() int { 185 return fs.ioSession.ConnectionTotal() + fs.metaSession.ConnectionTotal() 186 } 187 188 // GetServerVersion returns server version info 189 func (fs *FileSystem) GetServerVersion() (*types.IRODSVersion, error) { 190 conn, err := fs.metaSession.AcquireConnection() 191 if err != nil { 192 return nil, err 193 } 194 defer fs.metaSession.ReturnConnection(conn) 195 196 return conn.GetVersion(), nil 197 } 198 199 // SupportParallelUpload returns if the server supports parallel upload 200 func (fs *FileSystem) SupportParallelUpload() bool { 201 return fs.metaSession.SupportParallelUpload() 202 } 203 204 // GetMetrics returns metrics 205 func (fs *FileSystem) GetMetrics() *metrics.IRODSMetrics { 206 ioMetrics := fs.ioSession.GetMetrics() 207 metaMetrics := fs.metaSession.GetMetrics() 208 209 newMetrics := &metrics.IRODSMetrics{} 210 newMetrics.Sum(ioMetrics) 211 newMetrics.Sum(metaMetrics) 212 return newMetrics 213 } 214 215 // Stat returns file status 216 func (fs *FileSystem) Stat(p string) (*Entry, error) { 217 irodsPath := util.GetCorrectIRODSPath(p) 218 219 // check if a negative cache for the given path exists 220 if fs.cache.HasNegativeEntryCache(irodsPath) { 221 // has a negative cache - fail fast 222 return nil, xerrors.Errorf("failed to find the data object or the collection for path %s: %w", irodsPath, types.NewFileNotFoundError(irodsPath)) 223 } 224 225 // check if a cached Entry for the given path exists 226 cachedEntry := fs.cache.GetEntryCache(irodsPath) 227 if cachedEntry != nil { 228 return cachedEntry, nil 229 } 230 231 // check if a cached dir Entry for the given path exists 232 parentPath := path.Dir(irodsPath) 233 cachedDirEntryPaths := fs.cache.GetDirCache(parentPath) 234 dirEntryExist := false 235 if cachedDirEntryPaths != nil { 236 for _, cachedDirEntryPath := range cachedDirEntryPaths { 237 if cachedDirEntryPath == irodsPath { 238 dirEntryExist = true 239 break 240 } 241 } 242 243 if !dirEntryExist { 244 // dir entry not exist - fail fast 245 fs.cache.AddNegativeEntryCache(irodsPath) 246 return nil, xerrors.Errorf("failed to find the data object or the collection for path %s: %w", irodsPath, types.NewFileNotFoundError(irodsPath)) 247 } 248 } 249 250 // if cache does not exist, 251 // check dir first 252 dirStat, err := fs.getCollectionNoCache(irodsPath) 253 if err != nil { 254 if !types.IsFileNotFoundError(err) { 255 return nil, err 256 } 257 } else { 258 return dirStat, nil 259 } 260 261 // if it's not dir, check file 262 fileStat, err := fs.getDataObjectNoCache(irodsPath) 263 if err != nil { 264 if !types.IsFileNotFoundError(err) { 265 return nil, err 266 } 267 } else { 268 return fileStat, nil 269 } 270 271 // not a collection, not a data object 272 fs.cache.AddNegativeEntryCache(irodsPath) 273 return nil, xerrors.Errorf("failed to find the data object or the collection for path %s: %w", irodsPath, types.NewFileNotFoundError(irodsPath)) 274 } 275 276 // StatDir returns status of a directory 277 func (fs *FileSystem) StatDir(path string) (*Entry, error) { 278 irodsPath := util.GetCorrectIRODSPath(path) 279 280 return fs.getCollection(irodsPath) 281 } 282 283 // StatFile returns status of a file 284 func (fs *FileSystem) StatFile(path string) (*Entry, error) { 285 irodsPath := util.GetCorrectIRODSPath(path) 286 287 return fs.getDataObject(irodsPath) 288 } 289 290 // Exists checks file/directory existence 291 func (fs *FileSystem) Exists(path string) bool { 292 entry, err := fs.Stat(path) 293 if err != nil { 294 return false 295 } 296 return entry.ID > 0 297 } 298 299 // ExistsDir checks directory existence 300 func (fs *FileSystem) ExistsDir(path string) bool { 301 entry, err := fs.StatDir(path) 302 if err != nil { 303 return false 304 } 305 return entry.ID > 0 306 } 307 308 // ExistsFile checks file existence 309 func (fs *FileSystem) ExistsFile(path string) bool { 310 entry, err := fs.StatFile(path) 311 if err != nil { 312 return false 313 } 314 return entry.ID > 0 315 } 316 317 // List lists all file system entries under the given path 318 func (fs *FileSystem) List(path string) ([]*Entry, error) { 319 irodsPath := util.GetCorrectIRODSPath(path) 320 321 collectionEntry, err := fs.getCollection(irodsPath) 322 if err != nil { 323 return nil, err 324 } 325 326 collection := fs.getCollectionFromEntry(collectionEntry) 327 328 return fs.listEntries(collection) 329 } 330 331 // RemoveDir deletes a directory 332 func (fs *FileSystem) RemoveDir(path string, recurse bool, force bool) error { 333 irodsPath := util.GetCorrectIRODSPath(path) 334 335 conn, err := fs.metaSession.AcquireConnection() 336 if err != nil { 337 return err 338 } 339 defer fs.metaSession.ReturnConnection(conn) 340 341 err = irods_fs.DeleteCollection(conn, irodsPath, recurse, force) 342 if err != nil { 343 return err 344 } 345 346 fs.invalidateCacheForDirRemove(irodsPath, recurse) 347 fs.cachePropagation.PropagateDirRemove(irodsPath) 348 return nil 349 } 350 351 // RemoveFile deletes a file 352 func (fs *FileSystem) RemoveFile(path string, force bool) error { 353 irodsPath := util.GetCorrectIRODSPath(path) 354 355 conn, err := fs.metaSession.AcquireConnection() 356 if err != nil { 357 return err 358 } 359 defer fs.metaSession.ReturnConnection(conn) 360 361 // if file handle is opened, wg 362 wg := sync.WaitGroup{} 363 wg.Add(1) 364 365 eventHandlerID := fs.fileHandleMap.AddCloseEventHandler(irodsPath, func(path, id string, empty bool) { 366 if empty { 367 wg.Done() 368 } 369 }) 370 371 defer fs.fileHandleMap.RemoveCloseEventHandler(eventHandlerID) 372 373 if util.WaitTimeout(&wg, fs.config.OperationTimeout) { 374 // timed out 375 return xerrors.Errorf("failed to remove file, there are files still opened") 376 } 377 378 // wait done 379 err = irods_fs.DeleteDataObject(conn, irodsPath, force) 380 if err != nil { 381 return err 382 } 383 384 fs.invalidateCacheForFileRemove(irodsPath) 385 fs.cachePropagation.PropagateFileRemove(irodsPath) 386 return nil 387 } 388 389 // RenameDir renames a dir 390 func (fs *FileSystem) RenameDir(srcPath string, destPath string) error { 391 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 392 irodsDestPath := util.GetCorrectIRODSPath(destPath) 393 394 destDirPath := irodsDestPath 395 if fs.ExistsDir(irodsDestPath) { 396 // make full file name for dest 397 srcFileName := util.GetIRODSPathFileName(irodsSrcPath) 398 destDirPath = util.MakeIRODSPath(irodsDestPath, srcFileName) 399 } 400 401 return fs.RenameDirToDir(irodsSrcPath, destDirPath) 402 } 403 404 // RenameDirToDir renames a dir 405 func (fs *FileSystem) RenameDirToDir(srcPath string, destPath string) error { 406 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 407 irodsDestPath := util.GetCorrectIRODSPath(destPath) 408 409 conn, err := fs.metaSession.AcquireConnection() 410 if err != nil { 411 return err 412 } 413 defer fs.metaSession.ReturnConnection(conn) 414 415 // preprocess 416 handles, err := fs.preprocessRenameFileHandleForDir(irodsSrcPath) 417 if err != nil { 418 return err 419 } 420 421 err = irods_fs.MoveCollection(conn, irodsSrcPath, irodsDestPath) 422 if err != nil { 423 return err 424 } 425 426 fs.invalidateCacheForDirRemove(irodsSrcPath, true) 427 fs.cachePropagation.PropagateDirRemove(irodsSrcPath) 428 fs.invalidateCacheForDirCreate(irodsDestPath) 429 fs.cachePropagation.PropagateDirCreate(irodsDestPath) 430 431 // postprocess 432 err = fs.postprocessRenameFileHandleForDir(handles, conn, irodsSrcPath, irodsDestPath) 433 if err != nil { 434 return err 435 } 436 return nil 437 } 438 439 // RenameFile renames a file 440 func (fs *FileSystem) RenameFile(srcPath string, destPath string) error { 441 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 442 irodsDestPath := util.GetCorrectIRODSPath(destPath) 443 444 destFilePath := irodsDestPath 445 if fs.ExistsDir(irodsDestPath) { 446 // make full file name for dest 447 srcFileName := util.GetIRODSPathFileName(irodsSrcPath) 448 destFilePath = util.MakeIRODSPath(irodsDestPath, srcFileName) 449 } 450 451 return fs.RenameFileToFile(irodsSrcPath, destFilePath) 452 } 453 454 // RenameFileToFile renames a file 455 func (fs *FileSystem) RenameFileToFile(srcPath string, destPath string) error { 456 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 457 irodsDestPath := util.GetCorrectIRODSPath(destPath) 458 459 conn, err := fs.metaSession.AcquireConnection() 460 if err != nil { 461 return err 462 } 463 defer fs.metaSession.ReturnConnection(conn) 464 465 // preprocess 466 handles, err := fs.preprocessRenameFileHandle(irodsSrcPath) 467 if err != nil { 468 return err 469 } 470 471 // rename 472 err = irods_fs.MoveDataObject(conn, irodsSrcPath, irodsDestPath) 473 if err != nil { 474 return err 475 } 476 477 fs.invalidateCacheForFileRemove(irodsSrcPath) 478 fs.cachePropagation.PropagateFileRemove(irodsSrcPath) 479 fs.invalidateCacheForFileCreate(irodsDestPath) 480 fs.cachePropagation.PropagateFileCreate(irodsDestPath) 481 482 // postprocess 483 err = fs.postprocessRenameFileHandle(handles, conn, irodsDestPath) 484 if err != nil { 485 return err 486 } 487 488 return nil 489 } 490 491 func (fs *FileSystem) preprocessRenameFileHandle(srcPath string) ([]*FileHandle, error) { 492 handles := fs.fileHandleMap.PopByPath(srcPath) 493 handlesLocked := []*FileHandle{} 494 495 errs := []error{} 496 for _, handle := range handles { 497 // lock handles 498 handle.Lock() 499 500 err := handle.preprocessRename() 501 if err != nil { 502 errs = append(errs, err) 503 // unlock handle 504 handle.Unlock() 505 } else { 506 handlesLocked = append(handlesLocked, handle) 507 } 508 } 509 510 if len(errs) > 0 { 511 return handlesLocked, errs[0] 512 } 513 return handlesLocked, nil 514 } 515 516 func (fs *FileSystem) preprocessRenameFileHandleForDir(srcPath string) ([]*FileHandle, error) { 517 paths := fs.fileHandleMap.ListPathsInDir(srcPath) 518 519 errs := []error{} 520 handles := []*FileHandle{} 521 for _, path := range paths { 522 handlesForPath, err := fs.preprocessRenameFileHandle(path) 523 if err != nil { 524 errs = append(errs, err) 525 } else { 526 handles = append(handles, handlesForPath...) 527 } 528 } 529 530 if len(errs) > 0 { 531 return handles, errs[0] 532 } 533 return handles, nil 534 } 535 536 func (fs *FileSystem) postprocessRenameFileHandle(handles []*FileHandle, conn *connection.IRODSConnection, destPath string) error { 537 newEntry, err := fs.getDataObjectWithConnection(conn, destPath) 538 if err != nil { 539 return err 540 } 541 542 errs := []error{} 543 for _, handle := range handles { 544 err := handle.postprocessRename(destPath, newEntry) 545 if err != nil { 546 errs = append(errs, err) 547 } 548 549 handle.Unlock() 550 fs.fileHandleMap.Add(handle) 551 } 552 553 if len(errs) > 0 { 554 return errs[0] 555 } 556 return nil 557 } 558 559 func (fs *FileSystem) postprocessRenameFileHandleForDir(handles []*FileHandle, conn *connection.IRODSConnection, srcPath string, destPath string) error { 560 errs := []error{} 561 562 // map (original path => new Entry) 563 entryMap := map[string]*Entry{} 564 for _, handle := range handles { 565 if _, ok := entryMap[handle.entry.Path]; !ok { 566 // mapping not exist 567 // make full destPath 568 relPath, err := util.GetRelativeIRODSPath(srcPath, handle.entry.Path) 569 if err != nil { 570 errs = append(errs, err) 571 } else { 572 destFullPath := path.Join(destPath, relPath) 573 newEntry, err := fs.getDataObjectWithConnection(conn, destFullPath) 574 if err != nil { 575 errs = append(errs, err) 576 } else { 577 entryMap[handle.entry.Path] = newEntry 578 } 579 } 580 } 581 } 582 583 if len(errs) > 0 { 584 for _, handle := range handles { 585 handle.Unlock() 586 } 587 return errs[0] 588 } 589 590 for _, handle := range handles { 591 newEntry := entryMap[handle.entry.Path] 592 err := handle.postprocessRename(newEntry.Path, newEntry) 593 if err != nil { 594 errs = append(errs, err) 595 } 596 597 handle.Unlock() 598 fs.fileHandleMap.Add(handle) 599 } 600 601 if len(errs) > 0 { 602 return errs[0] 603 } 604 return nil 605 } 606 607 // MakeDir creates a directory 608 func (fs *FileSystem) MakeDir(path string, recurse bool) error { 609 irodsPath := util.GetCorrectIRODSPath(path) 610 611 conn, err := fs.metaSession.AcquireConnection() 612 if err != nil { 613 return err 614 } 615 defer fs.metaSession.ReturnConnection(conn) 616 617 dirEntry, err := fs.StatDir(path) 618 if err == nil { 619 if dirEntry.ID > 0 { 620 // already exists 621 if recurse { 622 return nil 623 } 624 return types.NewFileAlreadyExistError(path) 625 } 626 } 627 628 err = irods_fs.CreateCollection(conn, irodsPath, recurse) 629 if err != nil { 630 return err 631 } 632 633 fs.invalidateCacheForDirCreate(irodsPath) 634 fs.cachePropagation.PropagateDirCreate(irodsPath) 635 fs.cache.AddDirCache(irodsPath, []string{}) 636 return nil 637 } 638 639 // CopyFile copies a file 640 func (fs *FileSystem) CopyFile(srcPath string, destPath string, force bool) error { 641 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 642 irodsDestPath := util.GetCorrectIRODSPath(destPath) 643 644 destFilePath := irodsDestPath 645 if fs.ExistsDir(irodsDestPath) { 646 // make full file name for dest 647 srcFileName := util.GetIRODSPathFileName(irodsSrcPath) 648 destFilePath = util.MakeIRODSPath(irodsDestPath, srcFileName) 649 } 650 651 return fs.CopyFileToFile(irodsSrcPath, destFilePath, force) 652 } 653 654 // CopyFileToFile copies a file 655 func (fs *FileSystem) CopyFileToFile(srcPath string, destPath string, force bool) error { 656 irodsSrcPath := util.GetCorrectIRODSPath(srcPath) 657 irodsDestPath := util.GetCorrectIRODSPath(destPath) 658 659 conn, err := fs.metaSession.AcquireConnection() 660 if err != nil { 661 return err 662 } 663 defer fs.metaSession.ReturnConnection(conn) 664 665 err = irods_fs.CopyDataObject(conn, irodsSrcPath, irodsDestPath, force) 666 if err != nil { 667 return err 668 } 669 670 fs.invalidateCacheForFileCreate(irodsDestPath) 671 fs.cachePropagation.PropagateFileCreate(irodsDestPath) 672 return nil 673 } 674 675 // TruncateFile truncates a file 676 func (fs *FileSystem) TruncateFile(path string, size int64) error { 677 irodsPath := util.GetCorrectIRODSPath(path) 678 679 if size < 0 { 680 size = 0 681 } 682 683 conn, err := fs.metaSession.AcquireConnection() 684 if err != nil { 685 return err 686 } 687 defer fs.metaSession.ReturnConnection(conn) 688 689 err = irods_fs.TruncateDataObject(conn, irodsPath, size) 690 if err != nil { 691 return err 692 } 693 694 fs.invalidateCacheForFileUpdate(irodsPath) 695 fs.cachePropagation.PropagateFileUpdate(irodsPath) 696 return nil 697 } 698 699 // ReplicateFile replicates a file 700 func (fs *FileSystem) ReplicateFile(path string, resource string, update bool) error { 701 irodsPath := util.GetCorrectIRODSPath(path) 702 703 conn, err := fs.metaSession.AcquireConnection() 704 if err != nil { 705 return err 706 } 707 defer fs.metaSession.ReturnConnection(conn) 708 709 err = irods_fs.ReplicateDataObject(conn, irodsPath, resource, update, false) 710 if err != nil { 711 return err 712 } 713 714 fs.invalidateCacheForFileUpdate(irodsPath) 715 fs.cachePropagation.PropagateFileUpdate(irodsPath) 716 return nil 717 } 718 719 // OpenFile opens an existing file for read/write 720 func (fs *FileSystem) OpenFile(path string, resource string, mode string) (*FileHandle, error) { 721 irodsPath := util.GetCorrectIRODSPath(path) 722 723 conn, err := fs.ioSession.AcquireConnection() 724 if err != nil { 725 return nil, err 726 } 727 728 handle, offset, err := irods_fs.OpenDataObject(conn, irodsPath, resource, mode) 729 if err != nil { 730 fs.ioSession.ReturnConnection(conn) 731 return nil, err 732 } 733 734 var entry *Entry = nil 735 openMode := types.FileOpenMode(mode) 736 if openMode.IsOpeningExisting() { 737 // file may exists 738 entryExisting, err := fs.getDataObjectWithConnection(conn, irodsPath) 739 if err == nil { 740 entry = entryExisting 741 } 742 } 743 744 if entry == nil { 745 // create a new 746 entry = &Entry{ 747 ID: 0, 748 Type: FileEntry, 749 Name: util.GetIRODSPathFileName(irodsPath), 750 Path: irodsPath, 751 Owner: fs.account.ClientUser, 752 Size: 0, 753 CreateTime: time.Now(), 754 ModifyTime: time.Now(), 755 CheckSumAlgorithm: "", 756 CheckSum: "", 757 } 758 } 759 760 // do not return connection here 761 fileHandle := &FileHandle{ 762 id: xid.New().String(), 763 filesystem: fs, 764 connection: conn, 765 irodsFileHandle: handle, 766 entry: entry, 767 offset: offset, 768 openMode: types.FileOpenMode(mode), 769 } 770 771 fs.fileHandleMap.Add(fileHandle) 772 return fileHandle, nil 773 } 774 775 // CreateFile opens a new file for write 776 func (fs *FileSystem) CreateFile(path string, resource string, mode string) (*FileHandle, error) { 777 irodsPath := util.GetCorrectIRODSPath(path) 778 779 conn, err := fs.ioSession.AcquireConnection() 780 if err != nil { 781 return nil, err 782 } 783 784 // create 785 handle, err := irods_fs.CreateDataObject(conn, irodsPath, resource, mode, true) 786 if err != nil { 787 fs.ioSession.ReturnConnection(conn) 788 return nil, err 789 } 790 791 // close - this is required to let other processes see the file existence 792 err = irods_fs.CloseDataObject(conn, handle) 793 if err != nil { 794 fs.ioSession.ReturnConnection(conn) 795 return nil, err 796 } 797 798 entry, err := fs.getDataObjectWithConnectionNoCache(conn, irodsPath) 799 if err != nil { 800 fs.ioSession.ReturnConnection(conn) 801 return nil, err 802 } 803 804 // re-open 805 handle, offset, err := irods_fs.OpenDataObject(conn, irodsPath, resource, mode) 806 if err != nil { 807 fs.ioSession.ReturnConnection(conn) 808 return nil, err 809 } 810 811 // do not return connection here 812 fileHandle := &FileHandle{ 813 id: xid.New().String(), 814 filesystem: fs, 815 connection: conn, 816 irodsFileHandle: handle, 817 entry: entry, 818 offset: offset, 819 openMode: types.FileOpenMode(mode), 820 } 821 822 fs.fileHandleMap.Add(fileHandle) 823 fs.invalidateCacheForFileCreate(irodsPath) 824 fs.cachePropagation.PropagateFileCreate(irodsPath) 825 826 return fileHandle, nil 827 } 828 829 // getCollectionNoCache returns collection entry 830 func (fs *FileSystem) getCollectionNoCache(path string) (*Entry, error) { 831 // retrieve it and add it to cache 832 conn, err := fs.metaSession.AcquireConnection() 833 if err != nil { 834 return nil, err 835 } 836 defer fs.metaSession.ReturnConnection(conn) 837 838 collection, err := irods_fs.GetCollection(conn, path) 839 if err != nil { 840 return nil, err 841 } 842 843 if collection.ID > 0 { 844 entry := fs.getEntryFromCollection(collection) 845 846 // cache it 847 fs.cache.RemoveNegativeEntryCache(path) 848 fs.cache.AddEntryCache(entry) 849 return entry, nil 850 } 851 852 return nil, xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 853 } 854 855 // getCollection returns collection entry 856 func (fs *FileSystem) getCollection(path string) (*Entry, error) { 857 if fs.cache.HasNegativeEntryCache(path) { 858 return nil, xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 859 } 860 861 // check cache first 862 cachedEntry := fs.cache.GetEntryCache(path) 863 if cachedEntry != nil && cachedEntry.Type == DirectoryEntry { 864 return cachedEntry, nil 865 } 866 867 // otherwise, retrieve it and add it to cache 868 return fs.getCollectionNoCache(path) 869 } 870 871 // getCollectionFromEntry returns collection from entry 872 func (fs *FileSystem) getCollectionFromEntry(entry *Entry) *types.IRODSCollection { 873 return &types.IRODSCollection{ 874 ID: entry.ID, 875 Path: entry.Path, 876 Name: entry.Name, 877 Owner: entry.Owner, 878 CreateTime: entry.CreateTime, 879 ModifyTime: entry.ModifyTime, 880 } 881 } 882 883 func (fs *FileSystem) getEntryFromCollection(collection *types.IRODSCollection) *Entry { 884 return &Entry{ 885 ID: collection.ID, 886 Type: DirectoryEntry, 887 Name: collection.Name, 888 Path: collection.Path, 889 Owner: collection.Owner, 890 Size: 0, 891 DataType: "", 892 CreateTime: collection.CreateTime, 893 ModifyTime: collection.ModifyTime, 894 CheckSumAlgorithm: "", 895 CheckSum: "", 896 } 897 } 898 899 func (fs *FileSystem) getEntryFromDataObject(dataobject *types.IRODSDataObject) *Entry { 900 checksum := dataobject.Replicas[0].Checksum 901 checksumAlgorithm := "" 902 checksumString := "" 903 904 if checksum != nil { 905 checksumAlgorithm = checksum.GetChecksumAlgorithm() 906 checksumString = checksum.GetChecksumString() 907 } 908 909 return &Entry{ 910 ID: dataobject.ID, 911 Type: FileEntry, 912 Name: dataobject.Name, 913 Path: dataobject.Path, 914 Owner: dataobject.Replicas[0].Owner, 915 Size: dataobject.Size, 916 DataType: dataobject.DataType, 917 CreateTime: dataobject.Replicas[0].CreateTime, 918 ModifyTime: dataobject.Replicas[0].ModifyTime, 919 CheckSumAlgorithm: checksumAlgorithm, 920 CheckSum: checksumString, 921 } 922 } 923 924 // listEntries lists entries in a collection 925 func (fs *FileSystem) listEntries(collection *types.IRODSCollection) ([]*Entry, error) { 926 // check cache first 927 cachedEntries := []*Entry{} 928 useCached := false 929 930 cachedDirEntryPaths := fs.cache.GetDirCache(collection.Path) 931 if cachedDirEntryPaths != nil { 932 useCached = true 933 for _, cachedDirEntryPath := range cachedDirEntryPaths { 934 cachedEntry := fs.cache.GetEntryCache(cachedDirEntryPath) 935 if cachedEntry != nil { 936 cachedEntries = append(cachedEntries, cachedEntry) 937 } else { 938 useCached = false 939 } 940 } 941 } 942 943 if useCached { 944 // remove from nagative entry cache 945 for _, cachedEntry := range cachedEntries { 946 fs.cache.RemoveNegativeEntryCache(cachedEntry.Path) 947 } 948 return cachedEntries, nil 949 } 950 951 // otherwise, retrieve it and add it to cache 952 conn, err := fs.metaSession.AcquireConnection() 953 if err != nil { 954 return nil, err 955 } 956 defer fs.metaSession.ReturnConnection(conn) 957 958 collections, err := irods_fs.ListSubCollections(conn, collection.Path) 959 if err != nil { 960 return nil, err 961 } 962 963 entries := []*Entry{} 964 965 for _, coll := range collections { 966 entry := fs.getEntryFromCollection(coll) 967 entries = append(entries, entry) 968 969 // cache it 970 fs.cache.RemoveNegativeEntryCache(entry.Path) 971 fs.cache.AddEntryCache(entry) 972 } 973 974 dataobjects, err := irods_fs.ListDataObjectsMasterReplica(conn, collection) 975 if err != nil { 976 return nil, err 977 } 978 979 for _, dataobject := range dataobjects { 980 if len(dataobject.Replicas) == 0 { 981 continue 982 } 983 984 entry := fs.getEntryFromDataObject(dataobject) 985 entries = append(entries, entry) 986 987 // cache it 988 fs.cache.RemoveNegativeEntryCache(entry.Path) 989 fs.cache.AddEntryCache(entry) 990 } 991 992 // cache dir entries 993 dirEntryPaths := []string{} 994 for _, entry := range entries { 995 dirEntryPaths = append(dirEntryPaths, entry.Path) 996 } 997 fs.cache.AddDirCache(collection.Path, dirEntryPaths) 998 999 return entries, nil 1000 } 1001 1002 // getDataObjectWithConnectionNoCache returns an entry for data object 1003 func (fs *FileSystem) getDataObjectWithConnectionNoCache(conn *connection.IRODSConnection, path string) (*Entry, error) { 1004 // retrieve it and add it to cache 1005 collectionEntry, err := fs.getCollection(util.GetIRODSPathDirname(path)) 1006 if err != nil { 1007 return nil, err 1008 } 1009 1010 collection := fs.getCollectionFromEntry(collectionEntry) 1011 1012 dataobject, err := irods_fs.GetDataObjectMasterReplica(conn, collection, util.GetIRODSPathFileName(path)) 1013 if err != nil { 1014 return nil, err 1015 } 1016 1017 if dataobject.ID > 0 { 1018 entry := fs.getEntryFromDataObject(dataobject) 1019 1020 // cache it 1021 fs.cache.RemoveNegativeEntryCache(path) 1022 fs.cache.AddEntryCache(entry) 1023 return entry, nil 1024 } 1025 1026 return nil, xerrors.Errorf("failed to find the data object for path %s: %w", path, types.NewFileNotFoundError(path)) 1027 } 1028 1029 // getDataObjectWithConnection returns an entry for data object 1030 func (fs *FileSystem) getDataObjectWithConnection(conn *connection.IRODSConnection, path string) (*Entry, error) { 1031 if fs.cache.HasNegativeEntryCache(path) { 1032 return nil, xerrors.Errorf("failed to find the data object for path %s: %w", path, types.NewFileNotFoundError(path)) 1033 } 1034 1035 // check cache first 1036 cachedEntry := fs.cache.GetEntryCache(path) 1037 if cachedEntry != nil && cachedEntry.Type == FileEntry { 1038 return cachedEntry, nil 1039 } 1040 1041 // otherwise, retrieve it and add it to cache 1042 return fs.getDataObjectWithConnectionNoCache(conn, path) 1043 } 1044 1045 // getDataObjectNoCache returns an entry for data object 1046 func (fs *FileSystem) getDataObjectNoCache(path string) (*Entry, error) { 1047 // retrieve it and add it to cache 1048 collectionEntry, err := fs.getCollection(util.GetIRODSPathDirname(path)) 1049 if err != nil { 1050 return nil, err 1051 } 1052 1053 collection := fs.getCollectionFromEntry(collectionEntry) 1054 1055 conn, err := fs.metaSession.AcquireConnection() 1056 if err != nil { 1057 return nil, err 1058 } 1059 defer fs.metaSession.ReturnConnection(conn) 1060 1061 dataobject, err := irods_fs.GetDataObjectMasterReplica(conn, collection, util.GetIRODSPathFileName(path)) 1062 if err != nil { 1063 return nil, err 1064 } 1065 1066 if dataobject.ID > 0 { 1067 entry := fs.getEntryFromDataObject(dataobject) 1068 1069 // cache it 1070 fs.cache.RemoveNegativeEntryCache(path) 1071 fs.cache.AddEntryCache(entry) 1072 return entry, nil 1073 } 1074 1075 return nil, xerrors.Errorf("failed to find the data object for path %s: %w", path, types.NewFileNotFoundError(path)) 1076 } 1077 1078 // getDataObject returns an entry for data object 1079 func (fs *FileSystem) getDataObject(path string) (*Entry, error) { 1080 if fs.cache.HasNegativeEntryCache(path) { 1081 return nil, xerrors.Errorf("failed to find the data object for path %s: %w", path, types.NewFileNotFoundError(path)) 1082 } 1083 1084 // check cache first 1085 cachedEntry := fs.cache.GetEntryCache(path) 1086 if cachedEntry != nil && cachedEntry.Type == FileEntry { 1087 return cachedEntry, nil 1088 } 1089 1090 // otherwise, retrieve it and add it to cache 1091 return fs.getDataObjectNoCache(path) 1092 }