Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a8b15c5
Upload AzCopy binaries to Github (#3014)
wonwuakpa-msft Jul 23, 2025
88f0293
Fixed Mariner arm64 docker issue (#3034)
dphulkar-msft Jul 24, 2025
cde6e58
AzCopy as a library Phase 1 - jobs commands (#3106)
gapra-msft Jul 25, 2025
f26646d
Add entraID support for FileBlob S2S copies (#3053)
wonwuakpa-msft Jul 25, 2025
475f015
Warns the user if multiple AzCopy processes are running (#3128)
wonwuakpa-msft Jul 28, 2025
c6e1fcb
NFS Phase 1 changes (#3130)
dphulkar-msft Jul 28, 2025
d555ee7
Updated changelog and version (#3133)
gapra-msft Jul 29, 2025
ae864a6
Changing azcopy binary name in pipeline to resolve failure (#3134)
dphulkar-msft Jul 29, 2025
4f7f875
Fix issues with release pipeline (#3135)
gapra-msft Jul 30, 2025
82c2406
Missed adding FileNFS location type while oauth authentication (#3139)
dphulkar-msft Aug 1, 2025
2d0ac54
ESRP and PMC migration to using MI in AME (#3140)
gapra-msft Aug 4, 2025
28c6d8b
Pipeline yaml fix (#3152)
gapra-msft Aug 6, 2025
ada831d
Merge branch 'main' into users/shnayak/nfs-main-merge
shnayak-msft Aug 21, 2025
efb997c
Changes required post NFS GA main merge
shnayak-msft Aug 21, 2025
8cb6dc0
Resetting transfer lists in JobSummary based on a flag
shnayak-msft Aug 21, 2025
3665fb5
BugFix: Adding new FromTo enum values to sync orchestrator checks
shnayak-msft Aug 26, 2025
9f9f579
BugFix: Adding FileNFS to the folder tracker optimization supported s…
shnayak-msft Aug 29, 2025
5426686
S3 Traverser: Reusing S3Client in traverser as fix for sync orchestra…
shnayak-msft Sep 3, 2025
2df646e
Merge branch 'users/shnayak/nfs-main-merge' into mover/stage2
shnayak-msft Sep 3, 2025
579aaf2
BugFix: Readonly attribute is not being set for objects in azure file…
shnayak-msft Sep 14, 2025
255ab11
LocalTraverser: Removing extra os.Stat call during directory walk
shnayak-msft Sep 14, 2025
4f8de98
BugFix: Channel back pressure bugs
shnayak-msft Sep 14, 2025
350b399
LocalTraverser: Fetching extended properties only for sync flow
shnayak-msft Sep 12, 2025
fcfa51a
SyncComparator: Using micro second precision for time comparison for …
shnayak-msft Sep 12, 2025
e454937
Increasing slice size for file system directory reader from 1024 to 1…
shnayak-msft Sep 14, 2025
0e14225
Adding symlink and special file skipped counters
shnayak-msft Sep 19, 2025
024c760
Unifying httpClient into the common library, adding httpTrace to trac…
shnayak-msft Sep 19, 2025
19b5231
Skipping character auto encoding for NFS file share target for mover …
shnayak-msft Sep 22, 2025
f211e06
Fix for issue seen with read-only source files on Azure Files targets
bewurman-msft Sep 23, 2025
d91c641
MoverDependencies: Add init and close utils for scanning logger
shnayak-msft Oct 3, 2025
99b148f
SyncOrchestrator: Encoding destination URLs before creating target tr…
shnayak-msft Oct 3, 2025
5ced058
LocalTraverser: Sending skipped symlinks and unsupported special file…
shnayak-msft Oct 3, 2025
9e16f36
SyncComparator: Skipping entity type check for hardlinks, adding lowe…
shnayak-msft Oct 3, 2025
82ef523
Updating enumeration increment counter to account for all entity types
shnayak-msft Oct 3, 2025
dd3a13c
Disabling memory and goroutine throttling
shnayak-msft Oct 3, 2025
420eb66
Adding a relevant error message for pre-epoch timestamps comparison f…
shnayak-msft Oct 6, 2025
c795365
Bug fixes in symlink handling for local traversal
shnayak-msft Oct 8, 2025
63eaeb1
Skipping early failure in case ShareSizeLimitReached error for mover …
shnayak-msft Oct 10, 2025
d8b2194
Adding few defensive checks in sync throttler to handle invalid scena…
shnayak-msft Oct 14, 2025
5ac90c7
Merge branch 'mover/stage2' of https://github.com/Azure/azure-storage…
sgv-msft Feb 27, 2026
9c352de
Removed unused enum package in rpc-models.go
sgv-msft Feb 27, 2026
e8b9553
Updated ResurrectJob parameters in ResumeJobOrder function
sgv-msft Feb 27, 2026
84d4082
Fixing merge conflicts in syncEnumerator.go
sgv-msft Feb 28, 2026
c9a9d2d
Changes in moverDependencies.go and syncEnumerator.go found during do…
sgv-msft Feb 28, 2026
873c375
Merge remote-tracking branch 'origin/mover/c2c-stage' into users/sanv…
sgv-msft Apr 20, 2026
5a6ba72
AzCopy UT failure fixes.
sgv-msft Apr 21, 2026
69dfe56
Merge branch 'mover/c2c-stage' of https://github.com/Azure/azure-stor…
sgv-msft May 19, 2026
439526d
Merge from mover/c2c-stage2 after FSX merge
sgv-msft May 19, 2026
904455e
Merge branch 'mover/c2c-stage' of https://github.com/Azure/azure-stor…
sgv-msft May 26, 2026
7ddd197
Remove instructions .md to run UTs
sgv-msft May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ vendor/
## Ignore the executables
azcopy*

## Do not ignore azcopy as a library code
!azcopy/

## Dep tool
vendor/

Expand Down Expand Up @@ -336,3 +339,6 @@ venv*
# go files
*.mod
*.sum

# ignore the latest_version.txt
/latest_version.txt
23 changes: 23 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@

# Change Log

## Version 10.30.0
### Breaking changes
1. For transfers involving Azure Files (NFS or SMB), AzCopy will not auto create file shares.
2. AzCopy binaries and latest version information will now be distributed from Github releases instead of the static website. ([#3014](https://github.com/Azure/azure-storage-azcopy/pull/3014))

### New Features
1. Azure Files NFS Support via REST.
Support for transferring data between local Linux systems and Azure Files NFS using REST. To use this feature, please explicitly specify the `--from-to` flag.
- Transfer from local Linux to Azure Files NFS. (`--from-to=LocalFileNFS`)
- Transfer from Azure Files NFS to local Linux. (`--from-to=FileNFSLocal`)
- Transfer between Azure Files NFS shares. (`--from-to=FileNFSFileNFS`)
2. Added support to retry on copy source error code and status code for service to service copies. ([#3105](https://github.com/Azure/azure-storage-azcopy/pull/3105))
3. Added support for service to service copies from Azure Files to Blob Storage using EntraID. ([#3053](https://github.com/Azure/azure-storage-azcopy/pull/3053))

### Bug Fixes
1. Fixed a bug where when copying a file that has already been deleted with `--trailing-dot=Disable` resulted in the wrong error instead of a 404. ([#3092](https://github.com/Azure/azure-storage-azcopy/pull/3092))

### Supportability
1. Removed the warning message when failing to create a container. This message can be misleading when there is insufficient permissions to create a container and the container already exists. ([#3045](https://github.com/Azure/azure-storage-azcopy/pull/3045))
2. Improved the error message returned when block size is larger than bandwidth limit. ([#3051](https://github.com/Azure/azure-storage-azcopy/pull/3051))
3. Warn user if transfer is going to exceed 10M objects. ([#3111](https://github.com/Azure/azure-storage-azcopy/pull/3111))
4. Warn user if multiple AzCopy processes are running. ([#3128](https://github.com/Azure/azure-storage-azcopy/pull/3128))

## Version 10.30.0-preview.2

### Dependency updates
Expand Down
65 changes: 65 additions & 0 deletions azcopy/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright © 2025 Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package azcopy

import (
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/Azure/azure-storage-azcopy/v10/jobsAdmin"
"github.com/Azure/azure-storage-azcopy/v10/ste"
"log"
"runtime"
)

type Client struct {
CurrentJobID common.JobID // TODO (gapra): In future this should only be set when there is a current job running. On complete, this should be cleared. It can also behave as something we can check to see if a current job is running
}

type ClientOptions struct {
CapMbps float64
}

func NewClient(opts ClientOptions) (Client, error) {
c := Client{}
common.InitializeFolders()
configureGoMaxProcs()
// Perform os specific initialization
azcopyMaxFileAndSocketHandles, err := processOSSpecificInitialization()
if err != nil {
log.Fatalf("initialization failed: %v", err)
}
// startup of the STE happens here, so that the startup can access the values of command line parameters that are defined for "root" command
concurrencySettings := ste.NewConcurrencySettings(azcopyMaxFileAndSocketHandles)
err = jobsAdmin.MainSTE(concurrencySettings, opts.CapMbps)
if err != nil {
return c, err
}
return c, nil
}

// Ensure we always have more than 1 OS thread running goroutines, since there are issues with having just 1.
// (E.g. version check doesn't happen at login time, if have only one go proc. Not sure why that happens if have only one
// proc. Is presumably due to the high CPU usage we see on login if only 1 CPU, even tho can't see any busy-wait in that code)
func configureGoMaxProcs() {
isOnlyOne := runtime.GOMAXPROCS(0) == 1
if isOnlyOne {
runtime.GOMAXPROCS(2)
}
}
13 changes: 1 addition & 12 deletions cmd/root_unix.go → azcopy/client_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd
package azcopy

import (
"math"
"path"
"syscall"

"github.com/Azure/azure-storage-azcopy/v10/common"
)

// processOSSpecificInitialization changes the soft limit for file descriptor for process
Expand Down Expand Up @@ -67,11 +64,3 @@ func processOSSpecificInitialization() (int, error) {
return int(set.Cur), nil
}
}

// getAzCopyAppPath returns the path of Azcopy folder in local appdata.
// Azcopy folder in local appdata contains all the files created by azcopy locally.
func getAzCopyAppPath() string {
localAppData := common.GetEnvironmentVariable(common.EEnvironmentVariable.UserDir())
azcopyAppDataFolder := path.Join(localAppData, ".azcopy")
return azcopyAppDataFolder
}
18 changes: 3 additions & 15 deletions cmd/root_windows.go → azcopy/client_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd
package azcopy

import (
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/minio/minio-go/v7"
"math"
"net/http"
"path"
"strings"

"github.com/minio/minio-go/v7"

"github.com/Azure/azure-storage-azcopy/v10/common"
)

// processOSSpecificInitialization changes the soft limit for filedescriptor for process
Expand All @@ -44,14 +40,6 @@ func processOSSpecificInitialization() (int, error) {
return effectivelyUnlimited, nil
}

// getAzCopyAppPath returns the path of Azcopy in local appdata.
func getAzCopyAppPath() string {
userProfile := common.GetEnvironmentVariable(common.EEnvironmentVariable.UserDir())
azcopyAppDataFolder := strings.ReplaceAll(path.Join(userProfile, ".azcopy"), "/", `\`)

return azcopyAppDataFolder
}

func init() {
//Catch everything that uses http.DefaultTransport with ieproxy.GetProxyFunc()
http.DefaultTransport.(*http.Transport).Proxy = common.GlobalProxyLookup
Expand Down
65 changes: 65 additions & 0 deletions azcopy/jobsClean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright © 2025 Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package azcopy

import (
"fmt"
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/Azure/azure-storage-azcopy/v10/jobsAdmin"
)

// TODO (gapra) : Consider adding an onDelete callback to CleanJobsOptions? That way we can print to console as we clean jobs.
type CleanJobsOptions struct {
WithStatus *common.JobStatus // Default: All
}

type CleanJobsResult struct {
Count int // Number of files cleaned
Jobs []common.JobID // List of job IDs cleaned if WithStatus is not All, otherwise nil
}

// CleanJobs removes jobs with a specified status.
// If WithStatus is All, it cleans all jobs and returns the count of jobs cleaned.
// If WithStatus is not All, it cleans jobs with that status and returns the count of jobs cleaned and list of job IDs cleaned.
func (c Client) CleanJobs(opts CleanJobsOptions) (result CleanJobsResult, err error) {
result = CleanJobsResult{}
status := common.IffNil(opts.WithStatus, common.EJobStatus.All())

if status == common.EJobStatus.All() {
result.Count, err = jobsAdmin.DeleteAllJobFilesExceptCurrent(c.CurrentJobID)
} else {
resp := jobsAdmin.ListJobs(status)
if resp.ErrorMessage != "" {
return result, fmt.Errorf("failed to list jobs due to error: %s", resp.ErrorMessage)
}
result.Jobs = []common.JobID{}
for _, job := range resp.JobIDDetails {
result.Jobs = append(result.Jobs, job.JobId)
count, err := jobsAdmin.RemoveSingleJobFiles(job.JobId)
if err != nil {
return result, fmt.Errorf("failed to remove job %s due to error: %w", job.JobId, err)
} else {
result.Count += count
}
}
}
return result, err
}
79 changes: 79 additions & 0 deletions azcopy/jobsList.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright © 2025 Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package azcopy

import (
"fmt"
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/Azure/azure-storage-azcopy/v10/jobsAdmin"
"sort"
"time"
)

type ListJobsOptions struct {
WithStatus *common.JobStatus // Default: All
}

type JobDetail struct {
JobID common.JobID
StartTime time.Time
Status common.JobStatus
Command string
}

type ListJobsResponse struct {
Details []JobDetail
}

func (c Client) ListJobs(opts ListJobsOptions) (result ListJobsResponse, err error) {
status := common.IffNil(opts.WithStatus, common.EJobStatus.All())

resp := jobsAdmin.ListJobs(status)
if resp.ErrorMessage != "" {
return ListJobsResponse{}, fmt.Errorf("failed to list jobs due to error: %s", resp.ErrorMessage)
}

var details []JobDetail
for _, job := range resp.JobIDDetails {
details = append(details, JobDetail{
JobID: job.JobId,
StartTime: time.Unix(0, job.StartTime),
Status: job.JobStatus,
Command: job.CommandString,
})
}

// before displaying the jobs, sort them accordingly so that they are displayed in a consistent way
sortJobs(details)

return ListJobsResponse{
Details: details,
}, nil
}

func sortJobs(jobsDetails []JobDetail) {
// sort the jobs so that the latest one is shown first
sort.Slice(jobsDetails, func(i, j int) bool {
// this function essentially asks whether i should be placed before j
// we say yes if the job i is more recent
return jobsDetails[i].StartTime.After(jobsDetails[j].StartTime)
})
}
43 changes: 43 additions & 0 deletions azcopy/jobsList_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package azcopy

import (
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/stretchr/testify/assert"
"testing"
"time"
)

func TestSortJobs(t *testing.T) {
a := assert.New(t)
// setup
job2 := JobDetail{
JobID: common.NewJobID(),
StartTime: time.Now(),
Command: "dummy2",
}

// sleep for a bit so that the time stamp is different
time.Sleep(time.Millisecond)
job1 := JobDetail{
JobID: common.NewJobID(),
StartTime: time.Now(),
Command: "dummy1",
}

// sleep for a bit so that the time stamp is different
time.Sleep(time.Millisecond)
job0 := JobDetail{
JobID: common.NewJobID(),
StartTime: time.Now(),
Command: "dummy0",
}
jobsList := []JobDetail{job2, job1, job0}

// act
sortJobs(jobsList)

// verify
a.Equal(job0, jobsList[0])
a.Equal(job1, jobsList[1])
a.Equal(job2, jobsList[2])
}
Loading
Loading