1use super::{Exposure, Paths, ServiceSettings, os};
5use crate::{Error, config, service};
6use std::{
7 fs::File,
8 io::{self, Write},
9 path::{Path, PathBuf},
10};
11
12type Writer<'a> = &'a dyn Fn(&mut File) -> Result<(), Error>;
13type Create = fn(&Path) -> Result<(), io::Error>;
14
15#[derive(Clone, Copy, PartialEq, Eq)]
19enum Adopt {
20 AdminOnly,
21 Owned,
22}
23
24impl Adopt {
25 const STATE: Self = match cfg!(windows) {
26 true => Self::AdminOnly,
27 false => Self::Owned,
28 };
29}
30
31pub fn ensure_defaults(paths: &Paths) -> Result<Vec<PathBuf>, Error> {
34 let mut created = Vec::new();
35 let directories: [(&Path, Create, Adopt); 3] = [
36 (
37 &paths.settings_dir,
38 os::create_settings_directory,
39 Adopt::AdminOnly,
40 ),
41 (&paths.state_dir, os::create_state_directory, Adopt::STATE),
42 (&paths.data, os::create_data_directory, Adopt::Owned),
43 ];
44 for (path, create, adopt) in directories {
45 if absent_directory(path, adopt)? {
46 create(path)?;
47 created.push(path.to_path_buf());
48 }
49 }
50 let config = serde_json::to_vec_pretty(&config::service_archive_config()?)?;
51 let settings = serde_json::to_vec_pretty(&ServiceSettings::defaults(paths))?;
52 let files: [(&Path, Exposure, Adopt, Writer); 4] = [
53 (&paths.config, Exposure::Public, Adopt::AdminOnly, &|file| {
54 Ok(file.write_all(&config)?)
55 }),
56 (
57 &paths.settings,
58 Exposure::Public,
59 Adopt::AdminOnly,
60 &|file| Ok(file.write_all(&settings)?),
61 ),
62 (
63 &paths.peer_key,
64 Exposure::Private,
65 Adopt::Owned,
66 &write_peer_key,
67 ),
68 (
69 &paths.token,
70 Exposure::Readers,
71 Adopt::STATE,
72 &service::write_token,
73 ),
74 ];
75 for (path, exposure, adopt, write) in files {
76 if fill(path, exposure, adopt, write)? {
77 created.push(path.to_path_buf());
78 }
79 }
80 Ok(created)
81}
82
83pub fn write_peer_key(file: &mut File) -> Result<(), Error> {
85 let mut bytes = peer_key_bytes()?;
86 let written = file.write_all(&bytes);
87 bytes.fill(0);
88 Ok(written?)
89}
90
91pub fn create_peer_key(path: &Path) -> Result<(), Error> {
93 let mut bytes = peer_key_bytes()?;
94 let written = urma_io::write_new(path, &bytes);
95 bytes.fill(0);
96 written.map_err(|error| Error::Invalid(error.to_string()))
97}
98
99fn peer_key_bytes() -> Result<Vec<u8>, Error> {
100 libp2p::identity::Keypair::generate_ed25519()
101 .to_protobuf_encoding()
102 .map_err(|error| Error::Invalid(error.to_string()))
103}
104
105fn adoptable(path: &Path, adopt: Adopt) -> Result<(), Error> {
106 match adopt {
107 Adopt::Owned => Ok(()),
108 Adopt::AdminOnly => match urma_io::check_admin(path) {
109 Ok(()) => Ok(()),
110 Err(error) => Err(Error::Invalid(format!(
111 "refusing to adopt {}: it must be owned by and writable only by administrators \
112 ({error}); remove it or let `urma-node service install` reset it",
113 path.display()
114 ))),
115 },
116 }
117}
118
119fn absent_directory(path: &Path, adopt: Adopt) -> Result<bool, Error> {
120 match std::fs::symlink_metadata(path) {
121 Ok(metadata) if metadata.is_dir() => {
122 adoptable(path, adopt)?;
123 Ok(false)
124 }
125 Ok(metadata) => Err(Error::Invalid(format!(
126 "{} exists but is a {:?}, not a directory",
127 path.display(),
128 metadata.file_type()
129 ))),
130 Err(cause) if cause.kind() == io::ErrorKind::NotFound => Ok(true),
131 Err(cause) => Err(cause.into()),
132 }
133}
134
135fn fill(path: &Path, exposure: Exposure, adopt: Adopt, write: Writer) -> Result<bool, Error> {
136 let mut file = match os::create_file(path, exposure) {
137 Ok(file) => file,
138 Err(cause) if cause.kind() == io::ErrorKind::AlreadyExists => {
139 if !std::fs::symlink_metadata(path)?.is_file() {
140 return Err(Error::Invalid(format!(
141 "{} exists but is not a regular file: {cause}",
142 path.display()
143 )));
144 }
145 adoptable(path, adopt)?;
146 return Ok(false);
147 }
148 Err(cause) => return Err(cause.into()),
149 };
150 let outcome = write(&mut file).and_then(|()| Ok(file.sync_all()?));
151 drop(file);
152 match outcome {
153 Ok(()) => Ok(true),
154 Err(cause) => {
155 std::fs::remove_file(path)?;
156 Err(cause)
157 }
158 }
159}