mirror of
https://github.com/jkl1337/duplicacy.git
synced 2026-06-14 12:18:58 -05:00
Compare commits
11 Commits
3b50b7ec05
...
jkl-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 498a948b34 | |||
| 2679751896 | |||
| 66a938af67 | |||
| 015d2200da | |||
| 96e7c93a2c | |||
| f06779659e | |||
| 16885eaa61 | |||
| bf2565b5c3 | |||
| c07eef5063 | |||
| 2fdedcb9dd | |||
| 7bdd1cabd3 |
@@ -2262,7 +2262,7 @@ func main() {
|
||||
app.Name = "duplicacy"
|
||||
app.HelpName = "duplicacy"
|
||||
app.Usage = "A new generation cloud backup tool based on lock-free deduplication"
|
||||
app.Version = "3.2.2" + " (" + GitCommit + ")"
|
||||
app.Version = "3.2.1" + " (" + GitCommit + ")"
|
||||
|
||||
// Exit with code 2 if an invalid command is provided
|
||||
app.CommandNotFound = func(context *cli.Context, command string) {
|
||||
|
||||
2
go.mod
2
go.mod
@@ -27,7 +27,6 @@ require (
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/net v0.10.0
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sys v0.11.0
|
||||
google.golang.org/api v0.21.0
|
||||
storj.io/uplink v1.12.0
|
||||
)
|
||||
@@ -64,6 +63,7 @@ require (
|
||||
go.opencensus.io v0.22.3 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/tools v0.9.1 // indirect
|
||||
|
||||
@@ -710,23 +710,23 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
var hardLinkTable []hardLinkEntry
|
||||
var hardLinks []*Entry
|
||||
|
||||
restoreHardLink := func(entry *Entry, fullPath string) bool {
|
||||
if entry.IsHardLinkRoot() {
|
||||
restoreHardlink := func(entry *Entry, fullPath string) bool {
|
||||
if entry.IsHardlinkRoot() {
|
||||
hardLinkTable[len(hardLinkTable)-1].willExist = true
|
||||
} else if entry.IsHardLinkChild() {
|
||||
i, err := entry.GetHardLinkId()
|
||||
} else if entry.IsHardlinkedFrom() {
|
||||
i, err := entry.GetHardlinkId()
|
||||
if err != nil {
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Decode error for hard link entry %s: %v", entry.Path, err)
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Decode error for hardlinked entry %s, %v", entry.Path, err)
|
||||
return false
|
||||
}
|
||||
if !hardLinkTable[i].willExist {
|
||||
hardLinkTable[i] = hardLinkEntry{entry, true}
|
||||
} else {
|
||||
sourcePath := joinPath(top, hardLinkTable[i].entry.Path)
|
||||
LOG_INFO("RESTORE_HARDLINK", "Hard linking %s to %s", fullPath, sourcePath)
|
||||
if err := MakeHardlink(sourcePath, fullPath); err != nil {
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Failed to create hard link %s to %s: %v", fullPath, sourcePath, err)
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Failed to create hard link %s to %s %v", fullPath, sourcePath, err)
|
||||
}
|
||||
LOG_TRACE("DOWNLOAD_DONE", "Hard linked %s to %s", entry.Path, hardLinkTable[i].entry.Path)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -735,7 +735,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
|
||||
for remoteEntry := range remoteListingChannel {
|
||||
|
||||
if remoteEntry.IsHardLinkRoot() {
|
||||
if remoteEntry.IsHardlinkRoot() {
|
||||
hardLinkTable = append(hardLinkTable, hardLinkEntry{remoteEntry, false})
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
isRegular, link, err := Readlink(fullPath)
|
||||
if err == nil && link == remoteEntry.Link && !isRegular {
|
||||
remoteEntry.RestoreMetadata(fullPath, nil, setOwner)
|
||||
if remoteEntry.IsHardLinkRoot() {
|
||||
if remoteEntry.IsHardlinkRoot() {
|
||||
hardLinkTable[len(hardLinkTable)-1].willExist = true
|
||||
}
|
||||
continue
|
||||
@@ -798,7 +798,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
os.Remove(fullPath)
|
||||
}
|
||||
|
||||
if restoreHardLink(remoteEntry, fullPath) {
|
||||
if restoreHardlink(remoteEntry, fullPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -807,8 +807,8 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
return 0
|
||||
}
|
||||
remoteEntry.RestoreMetadata(fullPath, nil, setOwner)
|
||||
LOG_TRACE("DOWNLOAD_DONE", "Symlink %s updated", remoteEntry.Path)
|
||||
|
||||
LOG_TRACE("DOWNLOAD_DONE", "Symlink %s updated", remoteEntry.Path)
|
||||
} else if remoteEntry.IsDir() {
|
||||
|
||||
stat, err := os.Stat(fullPath)
|
||||
@@ -833,7 +833,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
if stat, _ := os.Lstat(fullPath); stat != nil {
|
||||
if remoteEntry.IsSameSpecial(stat) {
|
||||
remoteEntry.RestoreMetadata(fullPath, nil, setOwner)
|
||||
if remoteEntry.IsHardLinkRoot() {
|
||||
if remoteEntry.IsHardlinkRoot() {
|
||||
hardLinkTable[len(hardLinkTable)-1].willExist = true
|
||||
}
|
||||
}
|
||||
@@ -845,24 +845,22 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
os.Remove(fullPath)
|
||||
}
|
||||
|
||||
if restoreHardLink(remoteEntry, fullPath) {
|
||||
if restoreHardlink(remoteEntry, fullPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := remoteEntry.RestoreSpecial(fullPath); err != nil {
|
||||
LOG_ERROR("RESTORE_SPECIAL", "Failed to restore special file %s: %v", fullPath, err)
|
||||
LOG_ERROR("RESTORE_SPECIAL", "Unable to restore special file %s: %v", remoteEntry.Path, err)
|
||||
return 0
|
||||
}
|
||||
remoteEntry.RestoreMetadata(fullPath, nil, setOwner)
|
||||
LOG_TRACE("DOWNLOAD_DONE", "Special %s %s restored", remoteEntry.Path, remoteEntry.FmtSpecial())
|
||||
|
||||
} else {
|
||||
if remoteEntry.IsHardLinkRoot() {
|
||||
if remoteEntry.IsHardlinkRoot() {
|
||||
hardLinkTable[len(hardLinkTable)-1].willExist = true
|
||||
} else if remoteEntry.IsHardLinkChild() {
|
||||
i, err := remoteEntry.GetHardLinkId()
|
||||
} else if remoteEntry.IsHardlinkedFrom() {
|
||||
i, err := remoteEntry.GetHardlinkId()
|
||||
if err != nil {
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Decode error for hard link entry %s: %v", remoteEntry.Path, err)
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Decode error for hardlinked entry %s, %v", remoteEntry.Path, err)
|
||||
return 0
|
||||
}
|
||||
if !hardLinkTable[i].willExist {
|
||||
@@ -994,7 +992,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
|
||||
for _, linkEntry := range hardLinks {
|
||||
|
||||
i, _ := linkEntry.GetHardLinkId()
|
||||
i, _ := linkEntry.GetHardlinkId()
|
||||
sourcePath := joinPath(top, hardLinkTable[i].entry.Path)
|
||||
fullPath := joinPath(top, linkEntry.Path)
|
||||
|
||||
@@ -1006,7 +1004,7 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
|
||||
if sourceStat == nil {
|
||||
LOG_WERROR(allowFailures, "RESTORE_HARDLINK",
|
||||
"Target %s for hard link %s is missing", sourcePath, linkEntry.Path)
|
||||
"Target %s for hardlink %s is missing", sourcePath, linkEntry.Path)
|
||||
continue
|
||||
}
|
||||
if !overwrite {
|
||||
@@ -1017,11 +1015,11 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
||||
os.Remove(fullPath)
|
||||
}
|
||||
|
||||
LOG_DEBUG("RESTORE_HARDLINK", "Hard linking %s to %s", fullPath, sourcePath)
|
||||
if err := MakeHardlink(sourcePath, fullPath); err != nil {
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Failed to create hard link %s to %s: %v", fullPath, sourcePath, err)
|
||||
LOG_ERROR("RESTORE_HARDLINK", "Failed to create hard link %s to %s", fullPath, sourcePath)
|
||||
return 0
|
||||
}
|
||||
LOG_TRACE("RESTORE_HARDLINK", "Hard linked %s to %s", linkEntry.Path, hardLinkTable[i].entry.Path)
|
||||
}
|
||||
|
||||
if deleteMode && len(patterns) == 0 {
|
||||
@@ -1199,7 +1197,8 @@ func (manager *BackupManager) UploadSnapshot(chunkOperator *ChunkOperator, top s
|
||||
entry.StartChunk -= delta
|
||||
entry.EndChunk -= delta
|
||||
|
||||
if entry.IsHardLinkRoot() {
|
||||
if entry.IsHardlinkRoot() {
|
||||
LOG_DEBUG("SNAPSHOT_UPLOAD", "Hard link root %s %v %v", entry.Path, entry.StartChunk, entry.EndChunk)
|
||||
hardLinkTable = append(hardLinkTable, hardLinkEntry{entry, entry.StartChunk})
|
||||
}
|
||||
|
||||
@@ -1207,24 +1206,28 @@ func (manager *BackupManager) UploadSnapshot(chunkOperator *ChunkOperator, top s
|
||||
entry.StartChunk -= lastEndChunk
|
||||
lastEndChunk = entry.EndChunk
|
||||
entry.EndChunk = delta
|
||||
} else if entry.IsHardLinkChild() {
|
||||
i, err := entry.GetHardLinkId()
|
||||
} else if entry.IsHardlinkedFrom() && !entry.IsLink() {
|
||||
i, err := entry.GetHardlinkId()
|
||||
if err != nil {
|
||||
LOG_ERROR("SNAPSHOT_UPLOAD", "Decode error for hard link entry %s: %v", entry.Link, err)
|
||||
LOG_ERROR("SNAPSHOT_UPLOAD", "Decode error for hardlinked entry %s, %v", entry.Link, err)
|
||||
return err
|
||||
}
|
||||
|
||||
targetEntry := hardLinkTable[i].entry
|
||||
var startChunk, endChunk int
|
||||
|
||||
if targetEntry.IsFile() && targetEntry.Size > 0 {
|
||||
if targetEntry.Size > 0 {
|
||||
startChunk = hardLinkTable[i].startChunk - lastEndChunk
|
||||
endChunk = targetEntry.EndChunk
|
||||
lastEndChunk = hardLinkTable[i].startChunk + endChunk
|
||||
}
|
||||
entry = entry.HardLinkTo(targetEntry, startChunk, endChunk)
|
||||
|
||||
} else if entry.IsHardLinkRoot() {
|
||||
if targetEntry.Size > 0 {
|
||||
lastEndChunk = hardLinkTable[i].startChunk + endChunk
|
||||
}
|
||||
|
||||
LOG_DEBUG("SNAPSHOT_UPLOAD", "Uploading cloned hardlink for %s to %s (%v %v)", entry.Path, targetEntry.Path, startChunk, endChunk)
|
||||
} else if entry.IsHardlinkRoot() {
|
||||
hardLinkTable = append(hardLinkTable, hardLinkEntry{entry, 0})
|
||||
}
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ type Config struct {
|
||||
FileKey []byte `json:"-"`
|
||||
|
||||
// for erasure coding
|
||||
DataShards int `json:"data-shards"`
|
||||
ParityShards int `json:"parity-shards"`
|
||||
DataShards int `json:'data-shards'`
|
||||
ParityShards int `json:'parity-shards'`
|
||||
|
||||
// for RSA encryption
|
||||
rsaPrivateKey *rsa.PrivateKey
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
entryHardLinkRootChunkMarker = -9
|
||||
entryHardLinkTargetChunkMarker = -10
|
||||
entrySymHardLinkRootChunkMarker = -72
|
||||
entrySymHardLinkTargetChunkMarker = -73
|
||||
)
|
||||
|
||||
// This is the hidden directory in the repository for storing various files.
|
||||
@@ -126,13 +126,6 @@ func (entry *Entry) Copy() *Entry {
|
||||
}
|
||||
|
||||
func (entry *Entry) HardLinkTo(target *Entry, startChunk int, endChunk int) *Entry {
|
||||
endOffset := target.EndOffset
|
||||
|
||||
if !target.IsFile() {
|
||||
startChunk = target.StartChunk
|
||||
endChunk = entry.EndChunk
|
||||
endOffset = entry.EndOffset
|
||||
}
|
||||
return &Entry{
|
||||
Path: entry.Path,
|
||||
Size: target.Size,
|
||||
@@ -147,7 +140,7 @@ func (entry *Entry) HardLinkTo(target *Entry, startChunk int, endChunk int) *Ent
|
||||
StartChunk: startChunk,
|
||||
StartOffset: target.StartOffset,
|
||||
EndChunk: endChunk,
|
||||
EndOffset: endOffset,
|
||||
EndOffset: target.EndOffset,
|
||||
|
||||
Attributes: target.Attributes,
|
||||
}
|
||||
@@ -523,30 +516,34 @@ func (entry *Entry) IsLink() bool {
|
||||
}
|
||||
|
||||
func (entry *Entry) IsSpecial() bool {
|
||||
return entry.Mode&uint32(os.ModeNamedPipe|os.ModeDevice|os.ModeCharDevice|os.ModeSocket) != 0
|
||||
return entry.Mode&uint32(os.ModeNamedPipe|os.ModeDevice|os.ModeCharDevice) != 0
|
||||
}
|
||||
|
||||
func (entry *Entry) IsFileOrSpecial() bool {
|
||||
return entry.Mode&uint32(os.ModeDir|os.ModeSymlink|os.ModeIrregular) == 0
|
||||
}
|
||||
|
||||
func (entry *Entry) IsComplete() bool {
|
||||
return entry.Size >= 0
|
||||
}
|
||||
|
||||
func (entry *Entry) IsHardLinkChild() bool {
|
||||
return (entry.IsFile() && len(entry.Link) > 0 && entry.Link != "/") || (!entry.IsDir() && entry.EndChunk == entryHardLinkTargetChunkMarker)
|
||||
func (entry *Entry) IsHardlinkedFrom() bool {
|
||||
return (entry.IsFileOrSpecial() && len(entry.Link) > 0 && entry.Link != "/") || (entry.IsLink() && entry.StartChunk == entrySymHardLinkTargetChunkMarker)
|
||||
}
|
||||
|
||||
func (entry *Entry) IsHardLinkRoot() bool {
|
||||
return (entry.IsFile() && entry.Link == "/") || (!entry.IsDir() && entry.EndChunk == entryHardLinkRootChunkMarker)
|
||||
func (entry *Entry) IsHardlinkRoot() bool {
|
||||
return (entry.IsFileOrSpecial() && entry.Link == "/") || (entry.IsLink() && entry.StartChunk == entrySymHardLinkRootChunkMarker)
|
||||
}
|
||||
|
||||
func (entry *Entry) GetHardLinkId() (int, error) {
|
||||
if entry.IsFile() {
|
||||
func (entry *Entry) GetHardlinkId() (int, error) {
|
||||
if entry.IsLink() {
|
||||
if entry.StartChunk != entrySymHardLinkTargetChunkMarker {
|
||||
return 0, errors.New("Symlink entry not marked as hardlinked")
|
||||
}
|
||||
return entry.StartOffset, nil
|
||||
} else {
|
||||
i, err := strconv.ParseUint(entry.Link, 16, 64)
|
||||
return int(i), err
|
||||
} else {
|
||||
if entry.EndChunk != entryHardLinkTargetChunkMarker {
|
||||
return 0, errors.New("Entry not marked as hard link child")
|
||||
}
|
||||
return entry.EndOffset, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,6 +807,9 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string
|
||||
if f.Name() == DUPLICACY_DIRECTORY {
|
||||
continue
|
||||
}
|
||||
if f.Mode()&os.ModeSocket != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
entry := CreateEntryFromFileInfo(f, normalizedPath)
|
||||
if len(patterns) > 0 && !MatchPath(entry.Path, patterns) {
|
||||
@@ -823,23 +823,21 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string
|
||||
k := listEntryLinkKey{dev: uint64(stat.Dev), ino: uint64(stat.Ino)}
|
||||
if linkIndex, seen := listingState.linkTable[k]; seen {
|
||||
if linkIndex == -1 {
|
||||
LOG_DEBUG("LIST_EXCLUDE", "%s is excluded by attribute (hard link)", entry.Path)
|
||||
LOG_DEBUG("LIST_EXCLUDE", "%s is excluded by attribute (hardlink)", entry.Path)
|
||||
continue
|
||||
}
|
||||
entry.Size = 0
|
||||
if entry.IsFile() {
|
||||
if entry.IsLink() {
|
||||
entry.StartChunk = entrySymHardLinkTargetChunkMarker
|
||||
entry.StartOffset = linkIndex
|
||||
} else {
|
||||
entry.Link = strconv.FormatInt(int64(linkIndex), 16)
|
||||
} else {
|
||||
entry.EndChunk = entryHardLinkTargetChunkMarker
|
||||
entry.EndOffset = linkIndex
|
||||
}
|
||||
listingChannel <- entry
|
||||
continue
|
||||
} else {
|
||||
if entry.IsFile() {
|
||||
entry.Link = "/"
|
||||
if entry.IsLink() {
|
||||
entry.StartChunk = entrySymHardLinkRootChunkMarker
|
||||
} else {
|
||||
entry.EndChunk = entryHardLinkRootChunkMarker
|
||||
entry.Link = "/"
|
||||
}
|
||||
listingState.linkTable[k] = -1
|
||||
linkKey = &k
|
||||
|
||||
@@ -172,8 +172,6 @@ func TestEntryOrder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
listingState := NewListingState()
|
||||
|
||||
directories := make([]*Entry, 0, 4)
|
||||
directories = append(directories, CreateEntry("", 0, 0, 0))
|
||||
|
||||
@@ -184,7 +182,7 @@ func TestEntryOrder(t *testing.T) {
|
||||
for len(directories) > 0 {
|
||||
directory := directories[len(directories)-1]
|
||||
directories = directories[:len(directories)-1]
|
||||
subdirectories, _, err := ListEntries(testDir, directory.Path, nil, "", false, listingState, entryChannel)
|
||||
subdirectories, _, err := ListEntries(testDir, directory.Path, nil, "", false, entryChannel)
|
||||
if err != nil {
|
||||
t.Errorf("ListEntries(%s, %s) returned an error: %s", testDir, directory.Path, err)
|
||||
}
|
||||
@@ -290,8 +288,6 @@ func TestEntryExcludeByAttribute(t *testing.T) {
|
||||
|
||||
for _, excludeByAttribute := range [2]bool{true, false} {
|
||||
t.Logf("testing excludeByAttribute: %t", excludeByAttribute)
|
||||
|
||||
listingState := NewListingState()
|
||||
directories := make([]*Entry, 0, 4)
|
||||
directories = append(directories, CreateEntry("", 0, 0, 0))
|
||||
|
||||
@@ -302,7 +298,7 @@ func TestEntryExcludeByAttribute(t *testing.T) {
|
||||
for len(directories) > 0 {
|
||||
directory := directories[len(directories)-1]
|
||||
directories = directories[:len(directories)-1]
|
||||
subdirectories, _, err := ListEntries(testDir, directory.Path, nil, "", excludeByAttribute, listingState, entryChannel)
|
||||
subdirectories, _, err := ListEntries(testDir, directory.Path, nil, "", excludeByAttribute, entryChannel)
|
||||
if err != nil {
|
||||
t.Errorf("ListEntries(%s, %s) returned an error: %s", testDir, directory.Path, err)
|
||||
}
|
||||
|
||||
@@ -2568,7 +2568,7 @@ func (manager *SnapshotManager) CheckSnapshot(snapshot *Snapshot) (err error) {
|
||||
}
|
||||
|
||||
if entry.EndChunk < entry.StartChunk {
|
||||
err = fmt.Errorf("The file %s starts at chunk %d and ends at chunk %d",
|
||||
fmt.Errorf("The file %s starts at chunk %d and ends at chunk %d",
|
||||
entry.Path, entry.StartChunk, entry.EndChunk)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -756,8 +756,6 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor
|
||||
LOG_ERROR("STORAGE_CREATE", "Failed to load the Storj storage at %s: %v", storageURL, err)
|
||||
return nil
|
||||
}
|
||||
SavePassword(preference, "storj_key", apiKey)
|
||||
SavePassword(preference, "storj_passphrase", passphrase)
|
||||
return storjStorage
|
||||
} else if matched[1] == "smb" {
|
||||
server := matched[3]
|
||||
|
||||
@@ -92,18 +92,3 @@ func (entry *Entry) RestoreEarlyDirFlags(path string) error {
|
||||
func (entry *Entry) RestoreEarlyFileFlags(f *os.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entry *Entry) RestoreSpecial(fullPath string) error {
|
||||
mode := entry.Mode & uint32(fileModeMask)
|
||||
|
||||
if entry.Mode&uint32(os.ModeNamedPipe) != 0 {
|
||||
mode |= syscall.S_IFIFO
|
||||
} else if entry.Mode&uint32(os.ModeCharDevice) != 0 {
|
||||
mode |= syscall.S_IFCHR
|
||||
} else if entry.Mode&uint32(os.ModeDevice) != 0 {
|
||||
mode |= syscall.S_IFBLK
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return syscall.Mknod(fullPath, mode, int(entry.GetRdev()))
|
||||
}
|
||||
|
||||
@@ -219,23 +219,6 @@ func (entry *Entry) restoreLateFileFlags(f *os.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entry *Entry) RestoreSpecial(fullPath string) error {
|
||||
mode := entry.Mode & uint32(fileModeMask)
|
||||
|
||||
if entry.Mode&uint32(os.ModeNamedPipe) != 0 {
|
||||
mode |= syscall.S_IFIFO
|
||||
} else if entry.Mode&uint32(os.ModeCharDevice) != 0 {
|
||||
mode |= syscall.S_IFCHR
|
||||
} else if entry.Mode&uint32(os.ModeDevice) != 0 {
|
||||
mode |= syscall.S_IFBLK
|
||||
} else if entry.Mode&uint32(os.ModeSocket) != 0 {
|
||||
mode |= syscall.S_IFSOCK
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return syscall.Mknod(fullPath, mode, int(entry.GetRdev()))
|
||||
}
|
||||
|
||||
func excludedByAttribute(attributes map[string][]byte) bool {
|
||||
_, ok := attributes["user.duplicacy_exclude"]
|
||||
return ok
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package duplicacy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
@@ -66,6 +65,21 @@ func (entry *Entry) GetRdev() uint64 {
|
||||
return uint64(entry.StartChunk) | uint64(entry.StartOffset)<<32
|
||||
}
|
||||
|
||||
func (entry *Entry) RestoreSpecial(fullPath string) error {
|
||||
mode := entry.Mode & uint32(fileModeMask)
|
||||
|
||||
if entry.Mode&uint32(os.ModeNamedPipe) != 0 {
|
||||
mode |= syscall.S_IFIFO
|
||||
} else if entry.Mode&uint32(os.ModeCharDevice) != 0 {
|
||||
mode |= syscall.S_IFCHR
|
||||
} else if entry.Mode&uint32(os.ModeDevice) != 0 {
|
||||
mode |= syscall.S_IFBLK
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return syscall.Mknod(fullPath, mode, int(entry.GetRdev()))
|
||||
}
|
||||
|
||||
func (entry *Entry) IsSameSpecial(fileInfo os.FileInfo) bool {
|
||||
stat := fileInfo.Sys().(*syscall.Stat_t)
|
||||
if stat == nil {
|
||||
@@ -74,26 +88,6 @@ func (entry *Entry) IsSameSpecial(fileInfo os.FileInfo) bool {
|
||||
return (uint32(fileInfo.Mode()) == entry.Mode) && (uint64(stat.Rdev) == entry.GetRdev())
|
||||
}
|
||||
|
||||
func (entry *Entry) FmtSpecial() string {
|
||||
var c string
|
||||
mode := entry.Mode & uint32(os.ModeType)
|
||||
|
||||
if mode&uint32(os.ModeNamedPipe) != 0 {
|
||||
c = "p"
|
||||
} else if mode&uint32(os.ModeCharDevice) != 0 {
|
||||
c = "c"
|
||||
} else if mode&uint32(os.ModeDevice) != 0 {
|
||||
c = "b"
|
||||
} else if mode&uint32(os.ModeSocket) != 0 {
|
||||
c = "s"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
rdev := entry.GetRdev()
|
||||
return fmt.Sprintf("%s (%d, %d)", c, unix.Major(rdev), unix.Minor(rdev))
|
||||
}
|
||||
|
||||
func MakeHardlink(source string, target string) error {
|
||||
return unix.Linkat(unix.AT_FDCWD, source, unix.AT_FDCWD, target, 0)
|
||||
}
|
||||
|
||||
@@ -125,10 +125,6 @@ func (entry *Entry) RestoreSpecial(fullPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entry *Entry) FmtSpecial() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func MakeHardlink(source string, target string) error {
|
||||
return os.Link(source, target)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user