diff options
Diffstat (limited to 'conf/migration_windows.go')
-rw-r--r-- | conf/migration_windows.go | 108 |
1 files changed, 83 insertions, 25 deletions
diff --git a/conf/migration_windows.go b/conf/migration_windows.go index 72b298b6..ed288f3c 100644 --- a/conf/migration_windows.go +++ b/conf/migration_windows.go @@ -1,51 +1,109 @@ /* SPDX-License-Identifier: MIT * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. */ package conf import ( + "errors" + "io" "log" + "os" "path/filepath" "strings" + "sync" + "time" "golang.org/x/sys/windows" ) -func maybeMigrate(c string) { - if disableAutoMigration { - return - } +var ( + migrating sync.Mutex + lastMigrationTimer *time.Timer +) - vol := filepath.VolumeName(c) - withoutVol := strings.TrimPrefix(c, vol) - oldRoot := filepath.Join(vol, "\\windows.old") - oldC := filepath.Join(oldRoot, withoutVol) +type MigrationCallback func(name, oldPath, newPath string) - sd, err := windows.GetNamedSecurityInfo(oldRoot, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION) - if err == windows.ERROR_PATH_NOT_FOUND || err == windows.ERROR_FILE_NOT_FOUND { - return +func MigrateUnencryptedConfigs(migrated MigrationCallback) { migrateUnencryptedConfigs(3, migrated) } + +func migrateUnencryptedConfigs(sharingBase int, migrated MigrationCallback) { + if migrated == nil { + migrated = func(_, _, _ string) {} } + migrating.Lock() + defer migrating.Unlock() + configFileDir, err := tunnelConfigurationsDirectory() if err != nil { - log.Printf("Not migrating configuration from ‘%s’ due to GetNamedSecurityInfo error: %v", oldRoot, err) return } - owner, defaulted, err := sd.Owner() + files, err := os.ReadDir(configFileDir) if err != nil { - log.Printf("Not migrating configuration from ‘%s’ due to GetSecurityDescriptorOwner error: %v", oldRoot, err) return } - if defaulted || (!owner.IsWellKnown(windows.WinLocalSystemSid) && !owner.IsWellKnown(windows.WinBuiltinAdministratorsSid)) { - log.Printf("Not migrating configuration from ‘%s’, as it is not explicitly owned by SYSTEM or Built-in Administrators, but rather ‘%v’", oldRoot, owner) - return - } - err = windows.MoveFileEx(windows.StringToUTF16Ptr(oldC), windows.StringToUTF16Ptr(c), windows.MOVEFILE_COPY_ALLOWED) - if err != nil { - if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_ALREADY_EXISTS { - log.Printf("Not migrating configuration from ‘%s’ due to error when moving files: %v", oldRoot, err) + ignoreSharingViolations := false + for _, file := range files { + path := filepath.Join(configFileDir, file.Name()) + name := filepath.Base(file.Name()) + if len(name) <= len(configFileUnencryptedSuffix) || !strings.HasSuffix(name, configFileUnencryptedSuffix) { + continue } - return + if !file.Type().IsRegular() { + continue + } + info, err := file.Info() + if err != nil { + continue + } + if info.Mode().Perm()&0o444 == 0 { + continue + } + + var bytes []byte + var config *Config + var newPath string + // We don't use os.ReadFile, because we actually want RDWR, so that we can take advantage + // of Windows file locking for ensuring the file is finished being written. + f, err := os.OpenFile(path, os.O_RDWR, 0) + if err != nil { + if errors.Is(err, windows.ERROR_SHARING_VIOLATION) { + if ignoreSharingViolations { + continue + } else if sharingBase > 0 { + if lastMigrationTimer != nil { + lastMigrationTimer.Stop() + } + lastMigrationTimer = time.AfterFunc(time.Second/time.Duration(sharingBase*sharingBase), func() { migrateUnencryptedConfigs(sharingBase-1, migrated) }) + ignoreSharingViolations = true + continue + } + } + goto error + } + bytes, err = io.ReadAll(f) + f.Close() + if err != nil { + goto error + } + config, err = FromWgQuickWithUnknownEncoding(string(bytes), strings.TrimSuffix(name, configFileUnencryptedSuffix)) + if err != nil { + goto error + } + err = config.Save(false) + if err != nil { + goto error + } + err = os.Remove(path) + if err != nil { + goto error + } + newPath, err = config.Path() + if err != nil { + goto error + } + migrated(config.Name, path, newPath) + continue + error: + log.Printf("Unable to ingest and encrypt %#q: %v", path, err) } - log.Printf("Migrated configuration from ‘%s’", oldRoot) } |