golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/conf/migration_windows.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package conf 7 8 import ( 9 "errors" 10 "io" 11 "log" 12 "os" 13 "path/filepath" 14 "strings" 15 "sync" 16 "time" 17 18 "golang.org/x/sys/windows" 19 ) 20 21 var ( 22 migrating sync.Mutex 23 lastMigrationTimer *time.Timer 24 ) 25 26 type MigrationCallback func(name, oldPath, newPath string) 27 28 func MigrateUnencryptedConfigs(migrated MigrationCallback) { migrateUnencryptedConfigs(3, migrated) } 29 30 func migrateUnencryptedConfigs(sharingBase int, migrated MigrationCallback) { 31 if migrated == nil { 32 migrated = func(_, _, _ string) {} 33 } 34 migrating.Lock() 35 defer migrating.Unlock() 36 configFileDir, err := tunnelConfigurationsDirectory() 37 if err != nil { 38 return 39 } 40 files, err := os.ReadDir(configFileDir) 41 if err != nil { 42 return 43 } 44 ignoreSharingViolations := false 45 for _, file := range files { 46 path := filepath.Join(configFileDir, file.Name()) 47 name := filepath.Base(file.Name()) 48 if len(name) <= len(configFileUnencryptedSuffix) || !strings.HasSuffix(name, configFileUnencryptedSuffix) { 49 continue 50 } 51 if !file.Type().IsRegular() { 52 continue 53 } 54 info, err := file.Info() 55 if err != nil { 56 continue 57 } 58 if info.Mode().Perm()&0o444 == 0 { 59 continue 60 } 61 62 var bytes []byte 63 var config *Config 64 var newPath string 65 // We don't use os.ReadFile, because we actually want RDWR, so that we can take advantage 66 // of Windows file locking for ensuring the file is finished being written. 67 f, err := os.OpenFile(path, os.O_RDWR, 0) 68 if err != nil { 69 if errors.Is(err, windows.ERROR_SHARING_VIOLATION) { 70 if ignoreSharingViolations { 71 continue 72 } else if sharingBase > 0 { 73 if lastMigrationTimer != nil { 74 lastMigrationTimer.Stop() 75 } 76 lastMigrationTimer = time.AfterFunc(time.Second/time.Duration(sharingBase*sharingBase), func() { migrateUnencryptedConfigs(sharingBase-1, migrated) }) 77 ignoreSharingViolations = true 78 continue 79 } 80 } 81 goto error 82 } 83 bytes, err = io.ReadAll(f) 84 f.Close() 85 if err != nil { 86 goto error 87 } 88 config, err = FromWgQuickWithUnknownEncoding(string(bytes), strings.TrimSuffix(name, configFileUnencryptedSuffix)) 89 if err != nil { 90 goto error 91 } 92 err = config.Save(false) 93 if err != nil { 94 goto error 95 } 96 err = os.Remove(path) 97 if err != nil { 98 goto error 99 } 100 newPath, err = config.Path() 101 if err != nil { 102 goto error 103 } 104 migrated(config.Name, path, newPath) 105 continue 106 error: 107 log.Printf("Unable to ingest and encrypt %#q: %v", path, err) 108 } 109 }