Skip to content

Commit 812976f

Browse files
committed
fix: Potential file system race condition
1 parent 0cdee3d commit 812976f

1 file changed

Lines changed: 56 additions & 24 deletions

File tree

builder/source/cache.ts

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,72 @@ const CachePath = Path.resolve(SafeInitCwd({ Cwd: Process.cwd(), InitCwd: Proces
99
const CacheDomainsPath = Path.join(CachePath, 'domains.json')
1010

1111
export function CreateCache(Domains: Set<string>) {
12-
if (!Fs.existsSync(CachePath)) {
13-
Fs.mkdirSync(CachePath)
14-
} else if (!Fs.statSync(CachePath).isDirectory()) {
12+
Fs.mkdirSync(CachePath, { recursive: true })
13+
14+
if (!Fs.statSync(CachePath).isDirectory()) {
1515
throw new Error('.buildcache exists and is not a directory!')
1616
}
17-
if (Fs.existsSync(CacheDomainsPath)) {
18-
throw new Error('Cache already exists!')
17+
18+
try {
19+
Fs.writeFileSync(
20+
CacheDomainsPath,
21+
JSON.stringify([...Domains], null, 2),
22+
{ encoding: 'utf-8', flag: 'wx' },
23+
)
24+
} catch (Err) {
25+
if ((Err as NodeJS.ErrnoException).code === 'EEXIST') {
26+
throw new Error('Cache already exists!')
27+
}
28+
throw Err
1929
}
20-
Fs.writeFileSync(CacheDomainsPath, JSON.stringify([...Domains], null, 2), { encoding: 'utf-8' })
2130
}
2231

2332
export async function LoadCache(): Promise<Set<string>> {
24-
if (!Fs.existsSync(CacheDomainsPath)) {
25-
throw new Error('Cache does not exist!')
26-
}
27-
const DomainsRaw = Fs.readFileSync(CacheDomainsPath, { encoding: 'utf-8' })
28-
const DomainsArray: string[] = JSON.parse(DomainsRaw)
29-
await Zod.array(Zod.string().refine((Value) => {
30-
try {
31-
new URLPattern(`https://${Value}/`)
32-
return true
33-
} catch {
34-
return false
33+
let DomainsRaw: string
34+
35+
try {
36+
DomainsRaw = Fs.readFileSync(CacheDomainsPath, { encoding: 'utf-8' })
37+
} catch (error: unknown) {
38+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
39+
throw new Error('Cache does not exist!')
3540
}
36-
})).parseAsync(DomainsArray)
37-
return new Set(DomainsArray)
41+
throw error
42+
}
43+
44+
let DomainsArray: unknown
45+
try {
46+
DomainsArray = JSON.parse(DomainsRaw)
47+
} catch {
48+
throw new Error('Cache is corrupted!')
49+
}
50+
51+
const ParsedDomains = await Zod.array(
52+
Zod.string().refine((Value) => {
53+
try {
54+
new URLPattern(`https://${Value}/`)
55+
return true
56+
} catch {
57+
return false
58+
}
59+
}),
60+
).parseAsync(DomainsArray)
61+
62+
return new Set(ParsedDomains)
3863
}
3964

4065
export async function LoadDomainsFromCache(): Promise<Set<string>> {
41-
if (!Fs.existsSync(CacheDomainsPath)) {
42-
const Domains = await FetchAdShieldDomains()
43-
CreateCache(Domains)
44-
return Domains
45-
} else {
66+
try {
4667
return await LoadCache()
68+
} catch {
69+
const Domains = await FetchAdShieldDomains()
70+
try {
71+
CreateCache(Domains)
72+
return Domains
73+
} catch (Err: unknown) {
74+
if ((Err as NodeJS.ErrnoException).code === 'EEXIST') {
75+
return await LoadCache()
76+
}
77+
throw Err
78+
}
4779
}
4880
}

0 commit comments

Comments
 (0)