diff --git a/src/duplicacy_backupmanager.go b/src/duplicacy_backupmanager.go index 8d8b38d..e44550d 100644 --- a/src/duplicacy_backupmanager.go +++ b/src/duplicacy_backupmanager.go @@ -797,6 +797,21 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu } remoteEntry.RestoreEarlyDirFlags(fullPath) directoryEntries = append(directoryEntries, remoteEntry) + } else if remoteEntry.IsSpecial() { + if stat, _ := os.Lstat(fullPath); stat != nil { + if !overwrite { + LOG_WERROR(allowFailures, "DOWNLOAD_OVERWRITE", + "File %s already exists. Please specify the -overwrite option to overwrite", remoteEntry.Path) + continue + } + os.Remove(fullPath) + } + + if err := remoteEntry.RestoreSpecial(fullPath); err != nil { + LOG_ERROR("RESTORE_SPECIAL", "Unable to restore special file %s: %v", remoteEntry.Path, err) + return 0 + } + remoteEntry.RestoreMetadata(fullPath, nil, setOwner) } else { if remoteEntry.IsHardlinkRoot() { hardLinkTable[len(hardLinkTable)-1] = hardLinkEntry{remoteEntry, true} diff --git a/src/duplicacy_entry.go b/src/duplicacy_entry.go index b33b53b..6e00ba1 100644 --- a/src/duplicacy_entry.go +++ b/src/duplicacy_entry.go @@ -509,6 +509,10 @@ func (entry *Entry) IsLink() bool { return entry.Mode&uint32(os.ModeSymlink) != 0 } +func (entry *Entry) IsSpecial() bool { + return entry.Mode&uint32(os.ModeNamedPipe|os.ModeDevice|os.ModeCharDevice) != 0 +} + func (entry *Entry) IsComplete() bool { return entry.Size >= 0 } @@ -815,12 +819,15 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string } entry = newEntry } - } - - if f.Mode()&(os.ModeNamedPipe|os.ModeSocket|os.ModeDevice) != 0 { - LOG_WARN("LIST_SKIP", "Skipped non-regular file %s", entry.Path) - skippedFiles = append(skippedFiles, entry.Path) + } else if entry.Mode & uint32(os.ModeSocket) != 0 { + // no reason to issue a warning for what should always be a transient file anyways continue + } else if entry.IsSpecial() { + if !entry.ReadSpecial(f) { + LOG_WARN("LIST_DEV", "Failed to save device node %s", entry.Path) + skippedFiles = append(skippedFiles, entry.Path) + continue + } } var linkKey *listEntryLinkKey diff --git a/src/duplicacy_entrylist.go b/src/duplicacy_entrylist.go index 6f21a5f..0e5ef97 100644 --- a/src/duplicacy_entrylist.go +++ b/src/duplicacy_entrylist.go @@ -111,12 +111,12 @@ func (entryList *EntryList)createOnDiskFile() error { // Add an entry to the entry list func (entryList *EntryList)AddEntry(entry *Entry) error { - if !entry.IsDir() && !entry.IsLink() { + if entry.IsFile() { entryList.NumberOfEntries++ } if !entry.IsComplete() { - if entry.IsDir() || entry.IsLink() { + if !entry.IsFile() { entry.Size = 0 } else { modifiedEntry := ModifiedEntry { diff --git a/src/duplicacy_utils_linux.go b/src/duplicacy_utils_linux.go index ec46fe8..ac6dff1 100644 --- a/src/duplicacy_utils_linux.go +++ b/src/duplicacy_utils_linux.go @@ -48,6 +48,9 @@ func ioctl(f *os.File, request uintptr, attrp *uint32) error { } func (entry *Entry) ReadFileFlags(f *os.File) error { + if entry.IsSpecial() { + return nil + } var flags uint32 if err := ioctl(f, linux_FS_IOC_GETFLAGS, &flags); err != nil { return err diff --git a/src/duplicacy_utils_others.go b/src/duplicacy_utils_others.go index ebb5ff2..c202e65 100644 --- a/src/duplicacy_utils_others.go +++ b/src/duplicacy_utils_others.go @@ -49,7 +49,7 @@ func SetOwner(fullPath string, entry *Entry, fileInfo *os.FileInfo) bool { func (entry *Entry) ReadAttributes(top string) { fullPath := filepath.Join(top, entry.Path) - f, err := os.OpenFile(fullPath, os.O_RDONLY|syscall.O_NOFOLLOW, 0) + f, err := os.OpenFile(fullPath, os.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_NONBLOCK, 0) if err != nil { return } @@ -101,6 +101,37 @@ func (entry *Entry) SetAttributesToFile(fullPath string) { f.Close() } +func (entry *Entry) ReadSpecial(fileInfo os.FileInfo) bool { + if fileInfo.Mode() & (os.ModeDevice | os.ModeCharDevice) == 0 { + return true + } + stat, ok := fileInfo.Sys().(*syscall.Stat_t) + if !ok || stat == nil { + return false + } + entry.Size = 0 + rdev := uint64(stat.Rdev) + entry.StartChunk = int(rdev & 0xFFFFFFFF) + entry.StartOffset = int(rdev >> 32) + return true +} + +func (entry *Entry) RestoreSpecial(fullPath string) error { + if entry.Mode & uint32(os.ModeDevice | os.ModeCharDevice) != 0 { + mode := entry.Mode & uint32(fileModeMask) + if entry.Mode & uint32(os.ModeCharDevice) != 0 { + mode |= syscall.S_IFCHR + } else { + mode |= syscall.S_IFBLK + } + rdev := uint64(entry.StartChunk) | uint64(entry.StartOffset) << 32 + return syscall.Mknod(fullPath, mode, int(rdev)) + } else if entry.Mode & uint32(os.ModeNamedPipe) != 0 { + return syscall.Mkfifo(fullPath, uint32(entry.Mode)) + } + return nil +} + func joinPath(components ...string) string { return path.Join(components...) } diff --git a/src/duplicacy_utils_windows.go b/src/duplicacy_utils_windows.go index f394d2e..3846ca6 100644 --- a/src/duplicacy_utils_windows.go +++ b/src/duplicacy_utils_windows.go @@ -117,6 +117,14 @@ func (entry *Entry) SetAttributesToFile(fullPath string) { } +func (entry *Entry) ReadDeviceNode(fileInfo os.FileInfo) bool { + return nil +} + +func (entry *Entry) RestoreSpecial(fullPath string) error { + return nil +} + func joinPath(components ...string) string { combinedPath := `\\?\` + filepath.Join(components...)