|
@@ -23,6 +23,13 @@ var mutatedUtilityVMFiles = map[string]bool{
|
|
|
`EFI\Microsoft\Boot\BCD.LOG2`: true,
|
|
|
}
|
|
|
|
|
|
+const (
|
|
|
+ filesPath = `Files`
|
|
|
+ hivesPath = `Hives`
|
|
|
+ utilityVMPath = `UtilityVM`
|
|
|
+ utilityVMFilesPath = `UtilityVM\Files`
|
|
|
+)
|
|
|
+
|
|
|
func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
|
|
|
return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition)
|
|
|
}
|
|
@@ -44,6 +51,10 @@ func makeLongAbsPath(path string) (string, error) {
|
|
|
return `\\?\` + path, nil
|
|
|
}
|
|
|
|
|
|
+func hasPathPrefix(p, prefix string) bool {
|
|
|
+ return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\'
|
|
|
+}
|
|
|
+
|
|
|
type fileEntry struct {
|
|
|
path string
|
|
|
fi os.FileInfo
|
|
@@ -83,7 +94,7 @@ func readTombstones(path string) (map[string]([]string), error) {
|
|
|
|
|
|
ts := make(map[string]([]string))
|
|
|
for s.Scan() {
|
|
|
- t := filepath.Join("Files", s.Text()[1:]) // skip leading `\`
|
|
|
+ t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\`
|
|
|
dir := filepath.Dir(t)
|
|
|
ts[dir] = append(ts[dir], t)
|
|
|
}
|
|
@@ -212,7 +223,7 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if fe.fi.IsDir() && strings.HasPrefix(path, `Files\`) {
|
|
|
+ if fe.fi.IsDir() && hasPathPrefix(path, filesPath) {
|
|
|
fe.path += ".$wcidirs$"
|
|
|
}
|
|
|
|
|
@@ -231,14 +242,14 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if !strings.HasPrefix(path, `Files\`) {
|
|
|
+ if !hasPathPrefix(path, filesPath) {
|
|
|
size = fe.fi.Size()
|
|
|
r.backupReader = winio.NewBackupFileReader(f, false)
|
|
|
- if path == "Hives" || path == "Files" {
|
|
|
+ if path == hivesPath || path == filesPath {
|
|
|
// The Hives directory has a non-deterministic file time because of the
|
|
|
// nature of the import process. Use the times from System_Delta.
|
|
|
var g *os.File
|
|
|
- g, err = os.Open(filepath.Join(r.root, `Hives\System_Delta`))
|
|
|
+ g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`))
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
@@ -357,7 +368,7 @@ func (w *legacyLayerWriter) init() error {
|
|
|
|
|
|
func (w *legacyLayerWriter) initUtilityVM() error {
|
|
|
if !w.HasUtilityVM {
|
|
|
- err := os.Mkdir(filepath.Join(w.destRoot, `UtilityVM`), 0)
|
|
|
+ err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -365,7 +376,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
|
|
|
// clone the utility VM from the parent layer into this layer. Use hard
|
|
|
// links to avoid unnecessary copying, since most of the files are
|
|
|
// immutable.
|
|
|
- err = cloneTree(filepath.Join(w.parentRoots[0], `UtilityVM\Files`), filepath.Join(w.destRoot, `UtilityVM\Files`), mutatedUtilityVMFiles)
|
|
|
+ err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles)
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
|
|
|
}
|
|
@@ -490,15 +501,15 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if name == `UtilityVM` {
|
|
|
+ if name == utilityVMPath {
|
|
|
return w.initUtilityVM()
|
|
|
}
|
|
|
|
|
|
- if strings.HasPrefix(name, `UtilityVM\`) {
|
|
|
+ if hasPathPrefix(name, utilityVMPath) {
|
|
|
if !w.HasUtilityVM {
|
|
|
return errors.New("missing UtilityVM directory")
|
|
|
}
|
|
|
- if !strings.HasPrefix(name, `UtilityVM\Files\`) && name != `UtilityVM\Files` {
|
|
|
+ if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
|
|
|
return errors.New("invalid UtilityVM layer")
|
|
|
}
|
|
|
path := filepath.Join(w.destRoot, name)
|
|
@@ -585,7 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if strings.HasPrefix(name, `Hives\`) {
|
|
|
+ if hasPathPrefix(name, hivesPath) {
|
|
|
w.backupWriter = winio.NewBackupFileWriter(f, false)
|
|
|
} else {
|
|
|
// The file attributes are written before the stream.
|
|
@@ -608,22 +619,19 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- var requiredPrefix string
|
|
|
var roots []string
|
|
|
- if prefix := `Files\`; strings.HasPrefix(name, prefix) {
|
|
|
- requiredPrefix = prefix
|
|
|
+ if hasPathPrefix(target, filesPath) {
|
|
|
// Look for cross-layer hard link targets in the parent layers, since
|
|
|
// nothing is in the destination path yet.
|
|
|
roots = w.parentRoots
|
|
|
- } else if prefix := `UtilityVM\Files\`; strings.HasPrefix(name, prefix) {
|
|
|
- requiredPrefix = prefix
|
|
|
+ } else if hasPathPrefix(target, utilityVMFilesPath) {
|
|
|
// Since the utility VM is fully cloned into the destination path
|
|
|
// already, look for cross-layer hard link targets directly in the
|
|
|
// destination path.
|
|
|
roots = []string{w.destRoot}
|
|
|
}
|
|
|
|
|
|
- if requiredPrefix == "" || !strings.HasPrefix(target, requiredPrefix) {
|
|
|
+ if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
|
|
|
return errors.New("invalid hard link in layer")
|
|
|
}
|
|
|
|
|
@@ -657,9 +665,9 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
|
|
}
|
|
|
|
|
|
func (w *legacyLayerWriter) Remove(name string) error {
|
|
|
- if strings.HasPrefix(name, `Files\`) {
|
|
|
- w.tombstones = append(w.tombstones, name[len(`Files\`):])
|
|
|
- } else if strings.HasPrefix(name, `UtilityVM\Files\`) {
|
|
|
+ if hasPathPrefix(name, filesPath) {
|
|
|
+ w.tombstones = append(w.tombstones, name[len(filesPath)+1:])
|
|
|
+ } else if hasPathPrefix(name, utilityVMFilesPath) {
|
|
|
err := w.initUtilityVM()
|
|
|
if err != nil {
|
|
|
return err
|