nimbus-eth2/beacon_chain/filepath.nim

130 lines
4.2 KiB
Nim

# beacon_chain
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
import chronicles
import stew/io2
import spec/keystore
when defined(windows):
import stew/[windows/acl]
type
ByteChar = byte | char
const
INCOMPLETE_ERROR =
when defined(windows):
IoErrorCode(996) # ERROR_IO_INCOMPLETE
else:
IoErrorCode(28) # ENOSPC
proc openLockedFile*(keystorePath: string): IoResult[FileLockHandle] =
let
flags = {OpenFlags.Read, OpenFlags.Write, OpenFlags.Exclusive}
handle = ? openFile(keystorePath, flags)
var success = false
defer:
if not(success):
discard closeFile(handle)
let lock = ? lockFile(handle, LockType.Exclusive)
success = true
ok(FileLockHandle(ioHandle: lock, opened: true))
proc getData*(lockHandle: FileLockHandle,
maxBufferSize: int): IoResult[string] =
let filesize = ? getFileSize(lockHandle.ioHandle.handle)
let length = min(filesize, maxBufferSize)
var buffer = newString(length)
let bytesRead = ? readFile(lockHandle.ioHandle.handle, buffer)
if uint64(bytesRead) != uint64(len(buffer)):
err(INCOMPLETE_ERROR)
else:
ok(buffer)
proc closeLockedFile*(lockHandle: FileLockHandle): IoResult[void] =
if lockHandle.opened:
var success = false
defer:
lockHandle.opened = false
if not(success):
discard lockHandle.ioHandle.handle.closeFile()
? lockHandle.ioHandle.unlockFile()
success = true
? lockHandle.ioHandle.handle.closeFile()
ok()
proc secureCreatePath*(path: string): IoResult[void] =
when defined(windows):
let sres = createFoldersUserOnlySecurityDescriptor()
if sres.isErr():
error "Could not allocate security descriptor", path = path,
errorMsg = ioErrorMsg(sres.error), errorCode = $sres.error
err(sres.error)
else:
var sd = sres.get()
createPath(path, 0o700, secDescriptor = sd.getDescriptor())
else:
createPath(path, 0o700)
proc secureWriteFile*[T: ByteChar](path: string,
data: openArray[T]): IoResult[void] =
when defined(windows):
let sres = createFilesUserOnlySecurityDescriptor()
if sres.isErr():
error "Could not allocate security descriptor", path = path,
errorMsg = ioErrorMsg(sres.error), errorCode = $sres.error
err(sres.error())
else:
var sd = sres.get()
let res = writeFile(path, data, 0o600, sd.getDescriptor())
if res.isErr():
# writeFile() will not attempt to remove file on failure
discard removeFile(path)
err(res.error())
else:
ok()
else:
let res = writeFile(path, data, 0o600)
if res.isErr():
# writeFile() will not attempt to remove file on failure
discard removeFile(path)
err(res.error())
else:
ok()
proc secureWriteLockedFile*[T: ByteChar](path: string,
data: openArray[T]
): IoResult[FileLockHandle] =
let handle =
block:
let flags = {OpenFlags.Write, OpenFlags.Truncate, OpenFlags.Create,
OpenFlags.Exclusive}
when defined(windows):
var sd = ? createFilesUserOnlySecurityDescriptor()
? openFile(path, flags, 0o600, sd.getDescriptor())
else:
? openFile(path, flags, 0o600)
var success = false
defer:
if not(success):
discard closeFile(handle)
# We will try to remove file, if something goes wrong.
discard removeFile(path)
let bytesWrote = ? writeFile(handle, data)
if uint64(bytesWrote) != uint64(len(data)):
# Data was partially written, and `write` did not return any errors, so
# lets return INCOMPLETE_ERROR.
return err(INCOMPLETE_ERROR)
let res = ? lockFile(handle, LockType.Exclusive)
success = true
ok(FileLockHandle(ioHandle: res, opened: true))