mirror of
https://github.com/jkl1337/duplicacy.git
synced 2026-01-02 03:34:39 -06:00
Support backup and restore of special files on POSIX style systems
Special files are device nodes and named pipes. The necessity of the former is clear, the latter is debatable. In order to preserve backward compatibility, the device number is encoded in the StartChunk/StartOffset fields of the entry.
This commit is contained in:
@@ -797,6 +797,21 @@ func (manager *BackupManager) Restore(top string, revision int, inPlace bool, qu
|
|||||||
}
|
}
|
||||||
remoteEntry.RestoreEarlyDirFlags(fullPath)
|
remoteEntry.RestoreEarlyDirFlags(fullPath)
|
||||||
directoryEntries = append(directoryEntries, remoteEntry)
|
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 {
|
} else {
|
||||||
if remoteEntry.IsHardlinkRoot() {
|
if remoteEntry.IsHardlinkRoot() {
|
||||||
hardLinkTable[len(hardLinkTable)-1] = hardLinkEntry{remoteEntry, true}
|
hardLinkTable[len(hardLinkTable)-1] = hardLinkEntry{remoteEntry, true}
|
||||||
|
|||||||
@@ -509,6 +509,10 @@ func (entry *Entry) IsLink() bool {
|
|||||||
return entry.Mode&uint32(os.ModeSymlink) != 0
|
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 {
|
func (entry *Entry) IsComplete() bool {
|
||||||
return entry.Size >= 0
|
return entry.Size >= 0
|
||||||
}
|
}
|
||||||
@@ -815,13 +819,16 @@ func ListEntries(top string, path string, patterns []string, nobackupFile string
|
|||||||
}
|
}
|
||||||
entry = newEntry
|
entry = newEntry
|
||||||
}
|
}
|
||||||
}
|
} else if entry.Mode & uint32(os.ModeSocket) != 0 {
|
||||||
|
// no reason to issue a warning for what should always be a transient file anyways
|
||||||
if f.Mode()&(os.ModeNamedPipe|os.ModeSocket|os.ModeDevice) != 0 {
|
continue
|
||||||
LOG_WARN("LIST_SKIP", "Skipped non-regular file %s", entry.Path)
|
} 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)
|
skippedFiles = append(skippedFiles, entry.Path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var linkKey *listEntryLinkKey
|
var linkKey *listEntryLinkKey
|
||||||
|
|
||||||
|
|||||||
@@ -111,12 +111,12 @@ func (entryList *EntryList)createOnDiskFile() error {
|
|||||||
// Add an entry to the entry list
|
// Add an entry to the entry list
|
||||||
func (entryList *EntryList)AddEntry(entry *Entry) error {
|
func (entryList *EntryList)AddEntry(entry *Entry) error {
|
||||||
|
|
||||||
if !entry.IsDir() && !entry.IsLink() {
|
if entry.IsFile() {
|
||||||
entryList.NumberOfEntries++
|
entryList.NumberOfEntries++
|
||||||
}
|
}
|
||||||
|
|
||||||
if !entry.IsComplete() {
|
if !entry.IsComplete() {
|
||||||
if entry.IsDir() || entry.IsLink() {
|
if !entry.IsFile() {
|
||||||
entry.Size = 0
|
entry.Size = 0
|
||||||
} else {
|
} else {
|
||||||
modifiedEntry := ModifiedEntry {
|
modifiedEntry := ModifiedEntry {
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ func ioctl(f *os.File, request uintptr, attrp *uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) ReadFileFlags(f *os.File) error {
|
func (entry *Entry) ReadFileFlags(f *os.File) error {
|
||||||
|
if entry.IsSpecial() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var flags uint32
|
var flags uint32
|
||||||
if err := ioctl(f, linux_FS_IOC_GETFLAGS, &flags); err != nil {
|
if err := ioctl(f, linux_FS_IOC_GETFLAGS, &flags); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func SetOwner(fullPath string, entry *Entry, fileInfo *os.FileInfo) bool {
|
|||||||
|
|
||||||
func (entry *Entry) ReadAttributes(top string) {
|
func (entry *Entry) ReadAttributes(top string) {
|
||||||
fullPath := filepath.Join(top, entry.Path)
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,37 @@ func (entry *Entry) SetAttributesToFile(fullPath string) {
|
|||||||
f.Close()
|
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 {
|
func joinPath(components ...string) string {
|
||||||
return path.Join(components...)
|
return path.Join(components...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
func joinPath(components ...string) string {
|
||||||
|
|
||||||
combinedPath := `\\?\` + filepath.Join(components...)
|
combinedPath := `\\?\` + filepath.Join(components...)
|
||||||
|
|||||||
Reference in New Issue
Block a user