feat: add sys attr copy and one to one copy

This commit is contained in:
Samuel N Cui
2026-03-10 21:08:00 +08:00
parent af05f05ce1
commit 4a3bab1fbb
10 changed files with 181 additions and 8 deletions

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@
go.sum
output/
test.sh
upload_test.sh

View File

@@ -3,7 +3,6 @@ package acp
import (
"context"
"fmt"
"os"
)
func (c *Copyer) cleanupJob(ctx context.Context, copyed <-chan *baseJob) {
@@ -13,9 +12,10 @@ func (c *Copyer) cleanupJob(ctx context.Context, copyed <-chan *baseJob) {
if !ok {
return
}
for _, name := range job.successTargets {
if err := os.Chtimes(name, job.modTime, job.modTime); err != nil {
c.reportError(job.path, name, fmt.Errorf("change info, chtimes fail, %w", err))
if err := copyAttrs(name, job); err != nil {
c.reportError(job.path, name, fmt.Errorf("change info, copy attrs fail, %w", err))
}
}

View File

@@ -5,6 +5,7 @@ import (
"flag"
"os"
"os/signal"
"strings"
"github.com/klauspost/cpuid/v2"
"github.com/samuelncui/acp"
@@ -63,7 +64,47 @@ func main() {
}()
opts := make([]acp.Option, 0, 8)
opts = append(opts, acp.WildcardJob(acp.Source(sources...), acp.Target(targetPaths...)))
useAccurate := func() bool {
if *noTarget {
return false
}
if len(sources) > 1 {
return false
}
if len(targetPaths) > 1 {
return false
}
dst, src := targetPaths[0], sources[0]
if strings.HasSuffix(dst, "/") {
return false
}
dstStat, err := os.Stat(dst)
if err == nil {
return !dstStat.IsDir()
}
if !os.IsNotExist(err) {
logrus.Fatalf("stat dst path fail, %s", err)
panic(err)
}
srcStat, err := os.Stat(src)
if err == nil {
return srcStat.Mode().IsRegular()
}
logrus.Fatalf("stat src path fail, %s", err)
panic(err)
}()
if useAccurate {
opts = append(opts, acp.AccurateJob(sources[0], []string{targetPaths[0]}))
} else {
opts = append(opts, acp.WildcardJob(acp.Source(sources...), acp.Target(targetPaths...)))
}
// if *continueReport != "" {
// f, err := os.Open(*continueReport)
// if err != nil {
@@ -106,8 +147,8 @@ func main() {
report := getter()
r, err := os.Create(*reportPath)
if err != nil {
logrus.Warnf("open report fail, path= '%s', err= %w", *reportPath, err)
logrus.Infof("report: %s", report)
logrus.Warnf("open report fail, path= '%s', err= %s", *reportPath, err)
logrus.Infof("report= %q", report.ToJSONString(false))
return
}
defer r.Close()

13
copy.go
View File

@@ -139,6 +139,12 @@ func (c *Copyer) write(ctx context.Context, job *writeJob, ch chan<- *baseJob, c
job.fail(target, fmt.Errorf("open dst file fail, %w", err))
continue
}
if !job.copyer.toDevice.linear {
if err := truncate(file, job.size); err != nil {
job.fail(target, fmt.Errorf("truncate dst file fail, %w", err))
continue
}
}
ch := make(chan []byte, 4)
chans = append(chans, ch)
@@ -150,7 +156,7 @@ func (c *Copyer) write(ctx context.Context, job *writeJob, ch chan<- *baseJob, c
var rerr error
defer func() {
if rerr == nil {
job.succes(target)
job.success(target)
return
}
@@ -183,8 +189,13 @@ func (c *Copyer) write(ctx context.Context, job *writeJob, ch chan<- *baseJob, c
}
}
if err := file.Sync(); err != nil {
rerr = fmt.Errorf("sync dst file fail, %w", err)
return
}
if readErr != nil {
rerr = readErr
return
}
})
}

View File

@@ -98,6 +98,7 @@ func (c *Copyer) walk(ctx context.Context) ([]*baseJob, error) {
size: stat.Size(),
mode: stat.Mode(),
modTime: stat.ModTime(),
sys: stat.Sys(),
targets: targets,
})
@@ -154,6 +155,7 @@ func (c *Copyer) walk(ctx context.Context) ([]*baseJob, error) {
size: stat.Size(),
mode: stat.Mode(),
modTime: stat.ModTime(),
sys: stat.Sys(),
targets: j.dsts,
})

3
job.go
View File

@@ -42,6 +42,7 @@ type baseJob struct {
size int64 // length in bytes for regular files; system-dependent for others
mode fs.FileMode // file mode bits
modTime time.Time // modification time
sys any
lock sync.Mutex
writeTime time.Time
@@ -73,7 +74,7 @@ func (j *baseJob) setHash(h []byte) {
j.copyer.submit(&EventUpdateJob{j.report()})
}
func (j *baseJob) succes(path string) {
func (j *baseJob) success(path string) {
j.lock.Lock()
defer j.lock.Unlock()

34
syscall_darwin.go Normal file
View File

@@ -0,0 +1,34 @@
//go:build darwin
// +build darwin
package acp
import (
"fmt"
"os"
"syscall"
)
func truncate(file *os.File, size int64) error {
if err := file.Truncate(size); err != nil {
return err
}
return nil
}
func copyAttrs(name string, j *baseJob) error {
if err := os.Chmod(name, j.mode); err != nil {
return fmt.Errorf("chmod fail, %w", err)
}
if os.Geteuid() == 0 {
if stat, ok := j.sys.(*syscall.Stat_t); ok {
if err := os.Chown(name, int(stat.Uid), int(stat.Gid)); err != nil {
return fmt.Errorf("chown fail, %w", err)
}
}
}
if err := os.Chtimes(name, j.modTime, j.modTime); err != nil {
return fmt.Errorf("chtimes fail, %w", err)
}
return nil
}

34
syscall_linux.go Normal file
View File

@@ -0,0 +1,34 @@
//go:build linux
// +build linux
package acp
import (
"fmt"
"os"
"syscall"
)
func truncate(file *os.File, size int64) error {
if err := syscall.Fallocate(int(file.Fd()), 0, 0, size); err != nil {
return err
}
return nil
}
func copyAttrs(name string, j *baseJob) error {
if err := os.Chmod(name, j.mode); err != nil {
return fmt.Errorf("chmod fail, %w", err)
}
if os.Geteuid() == 0 {
if stat, ok := j.sys.(*syscall.Stat_t); ok {
if err := os.Chown(name, int(stat.Uid), int(stat.Gid)); err != nil {
return fmt.Errorf("chown fail, %w", err)
}
}
}
if err := os.Chtimes(name, j.modTime, j.modTime); err != nil {
return fmt.Errorf("chtimes fail, %w", err)
}
return nil
}

23
syscall_other.go Normal file
View File

@@ -0,0 +1,23 @@
//go:build !linux && !darwin && !windows
// +build !linux,!darwin,!windows
package acp
import (
"fmt"
"os"
)
func truncate(_ *os.File, _ int64) error {
return nil
}
func copyAttrs(name string, j *baseJob) error {
if err := os.Chmod(name, j.mode); err != nil {
return fmt.Errorf("chmod fail, %w", err)
}
if err := os.Chtimes(name, j.modTime, j.modTime); err != nil {
return fmt.Errorf("chtimes fail, %w", err)
}
return nil
}

26
syscall_windows.go Normal file
View File

@@ -0,0 +1,26 @@
//go:build windows
// +build windows
package acp
import (
"fmt"
"os"
)
func truncate(file *os.File, size int64) error {
if err := file.Truncate(size); err != nil {
return err
}
return nil
}
func copyAttrs(name string, j *baseJob) error {
if err := os.Chmod(name, j.mode); err != nil {
return fmt.Errorf("chmod fail, %w", err)
}
if err := os.Chtimes(name, j.modTime, j.modTime); err != nil {
return fmt.Errorf("chtimes fail, %w", err)
}
return nil
}