diff --git a/cmd/syncEnumerator.go b/cmd/syncEnumerator.go index 9124fce958..2ef38b505a 100644 --- a/cmd/syncEnumerator.go +++ b/cmd/syncEnumerator.go @@ -533,7 +533,7 @@ func GetSyncEnumeratorWithDestComparator( } destCleanerFunc := newFpoAwareProcessor(fpo, destinationCleaner.removeImmediately) - if UseSyncOrchestrator && (cca.fromTo == common.EFromTo.S3Blob() || cca.fromTo == common.EFromTo.BlobBlob() || cca.fromTo == common.EFromTo.BlobFSBlob()) { + if UseSyncOrchestrator && (cca.fromTo == common.EFromTo.S3Blob() || cca.fromTo == common.EFromTo.BlobBlob() || cca.fromTo == common.EFromTo.BlobFSBlob() || cca.fromTo == common.EFromTo.FileFile()) { // newFpoAwareProcessor sets the destCleanerFunc to nil for a non folder aware source destination pair like S3->Blob. // But in case of SyncOrchestrator, S3->Blob sync does recursive deletion for a prefix. // This requires a valid deletion processor to be passed to the comparator. diff --git a/cmd/syncOrchestrator.go b/cmd/syncOrchestrator.go index 56011e1191..f38db7b740 100644 --- a/cmd/syncOrchestrator.go +++ b/cmd/syncOrchestrator.go @@ -69,6 +69,8 @@ var ( "BlobNotFound", "PathNotFound", "ResourceNotFound", + "ShareNotFound", + "ShareBeingDeleted", } orchestratorOptions *SyncOrchestratorOptions @@ -200,6 +202,26 @@ func validateLocalRoot(path string) error { return nil } +// validateFileShareRoot validates an Azure Files share root URL +func validateFileShareRoot(sourcePath string) error { + // Parse as a URL to validate format + parsedURL, err := url.Parse(sourcePath) + if err != nil { + return err + } + + // Basic validation - should be a valid URL with file.core.windows.net host + if parsedURL.Host == "" { + return fmt.Errorf("invalid Azure Files URL: missing host") + } + + if !strings.Contains(parsedURL.Host, "file.core.windows.net") { + return fmt.Errorf("invalid Azure Files URL: expected file.core.windows.net host") + } + + return nil +} + // validateS3Root returns the root object for the sync orchestrator based on the S3 source path. // It parses the S3 URL and determines the entity type (file or folder) based on the URL structure. // @@ -269,6 +291,8 @@ func validateAndGetRootObject(path string, fromTo common.FromTo) (minimalStoredO err = validateBlobRoot(path) case common.ELocation.BlobFS(): err = validateBlobFSRoot(path) + case common.ELocation.File(): + err = validateFileShareRoot(path) default: err = fmt.Errorf("sync orchestrator is not supported for %s source", fromTo.From().String()) } @@ -545,7 +569,7 @@ func newSyncTraverser(enumerator *syncEnumerator, dir string, comparator objectP func validate(cca *cookedSyncCmdArgs, orchestratorOptions *SyncOrchestratorOptions) error { switch cca.fromTo { - case common.EFromTo.LocalBlob(), common.EFromTo.LocalBlobFS(), common.EFromTo.LocalFile(), common.EFromTo.S3Blob(), common.EFromTo.BlobBlob(), common.EFromTo.BlobBlobFS(), common.EFromTo.BlobFSBlob(), common.EFromTo.BlobFSBlobFS(): + case common.EFromTo.LocalBlob(), common.EFromTo.LocalBlobFS(), common.EFromTo.LocalFile(), common.EFromTo.S3Blob(), common.EFromTo.BlobBlob(), common.EFromTo.BlobBlobFS(), common.EFromTo.BlobFSBlob(), common.EFromTo.BlobFSBlobFS(), common.EFromTo.FileFile(): // sync orchestrator is supported for these types default: return fmt.Errorf( @@ -557,7 +581,8 @@ func validate(cca *cookedSyncCmdArgs, orchestratorOptions *SyncOrchestratorOptio "\t- Blob->Blob\n" + "\t- Blob->BlobFS\n" + "\t- BlobFS->Blob\n" + - "\t- BlobFS->BlobFS", + "\t- BlobFS->BlobFS\n" + + "\t- File->File", ) } diff --git a/cmd/syncOrchestratorModels.go b/cmd/syncOrchestratorModels.go index c6bff9711e..f804fe1a77 100644 --- a/cmd/syncOrchestratorModels.go +++ b/cmd/syncOrchestratorModels.go @@ -77,8 +77,8 @@ func (s *SyncOrchestratorOptions) validate(from common.Location) error { return errors.New("sync orchestrator options should only be used when UseSyncOrchestrator is true") } - if from != common.ELocation.Local() && from != common.ELocation.S3() && from != common.ELocation.Blob() && from != common.ELocation.BlobFS() { - return errors.New("sync optimizations using timestamps should only be used for local to remote syncs") + if from != common.ELocation.Local() && from != common.ELocation.S3() && from != common.ELocation.Blob() && from != common.ELocation.BlobFS() && from != common.ELocation.File() { + return errors.New("sync optimizations using timestamps should only be used for supported source locations (Local, S3, Blob, BlobFS, File)") } if s.maxDirectoryDirectChildCount == 0 { diff --git a/cmd/syncThrottler.go b/cmd/syncThrottler.go index e2ba1caa8c..21514bbb3e 100644 --- a/cmd/syncThrottler.go +++ b/cmd/syncThrottler.go @@ -197,6 +197,8 @@ func GetSafeParallelismLimit(maxActiveFiles, maxChildCount int64, fromTo common. return min(limit, 48) case common.EFromTo.S3Blob(): return min(limit, 64) + case common.EFromTo.FileFile(): + return min(limit, 48) default: return min(limit, 48) } diff --git a/cmd/zc_newobjectadapters.go b/cmd/zc_newobjectadapters.go index f9e96038f5..55134650fb 100644 --- a/cmd/zc_newobjectadapters.go +++ b/cmd/zc_newobjectadapters.go @@ -349,18 +349,30 @@ type shareDirectoryFilePropertiesAdapter struct { } func (a shareDirectoryFilePropertiesAdapter) Metadata() common.Metadata { + if a.FileProperty == nil { + return nil + } return nil } func (a shareDirectoryFilePropertiesAdapter) LastModified() time.Time { + if a.FileProperty == nil { + return time.Time{} + } return common.IffNotNil(a.FileProperty.LastModified, time.Time{}) } func (a shareDirectoryFilePropertiesAdapter) FileLastWriteTime() time.Time { + if a.FileProperty == nil { + return time.Time{} + } return common.IffNotNil(a.FileProperty.LastWriteTime, time.Time{}) } func (a shareDirectoryFilePropertiesAdapter) FileChangeTime() time.Time { + if a.FileProperty == nil { + return time.Time{} + } return common.IffNotNil(a.FileProperty.ChangeTime, time.Time{}) } @@ -389,6 +401,9 @@ func (a shareDirectoryFilePropertiesAdapter) ContentMD5() []byte { } func (a shareDirectoryFilePropertiesAdapter) ContentLength() int64 { + if a.FileProperty == nil { + return 0 + } return common.IffNotNil(a.FileProperty.ContentLength, 0) }