aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/conf/migration_windows.go
blob: 091efa32e13000e97a3774de9456c0fae10ec9c4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
 */

package conf

import (
	"errors"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"golang.org/x/sys/windows"
)

var migrating sync.Mutex
var lastMigrationTimer *time.Timer

func MigrateUnencryptedConfigs() { migrateUnencryptedConfigs(3) }

func migrateUnencryptedConfigs(sharingBase int) {
	migrating.Lock()
	defer migrating.Unlock()
	configFileDir, err := tunnelConfigurationsDirectory()
	if err != nil {
		return
	}
	files, err := os.ReadDir(configFileDir)
	if err != nil {
		return
	}
	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
		}
		if !file.Type().IsRegular() {
			continue
		}
		info, err := file.Info()
		if err != nil {
			continue
		}
		if info.Mode().Perm()&0444 == 0 {
			continue
		}

		var bytes []byte
		var config *Config
		// 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) })
					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 {
			log.Printf("Unable to remove old path %#q: %v", path, err)
		}
		continue
	error:
		log.Printf("Unable to ingest and encrypt %#q: %v", path, err)
	}
}