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)
|
||||
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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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...)
|
||||
|
||||
Reference in New Issue
Block a user