1use crate::{config_handle, editor::Action, input::Event};
31use ninep::{
32 fs::{FileMeta, IoUnit, Mode, Perm, Stat},
33 server::{socket_path, ClientId, ReadOutcome, Serve9p, Server},
34 Result,
35};
36use std::{
37 collections::HashMap,
38 env,
39 fs::create_dir_all,
40 mem::take,
41 path::Path,
42 process::Command,
43 sync::mpsc::{channel, Receiver, Sender},
44 thread::{spawn, JoinHandle},
45 time::SystemTime,
46};
47use tracing::{error, trace};
48
49mod buffer;
50mod event;
51mod log;
52mod message;
53
54pub(crate) use event::InputFilter;
55pub(crate) use log::LogEvent;
56pub(crate) use message::{Message, Req};
57
58use buffer::{BufferNodes, QidCheck};
59use log::spawn_log_listener;
60
61const DEFAULT_SOCKET_NAME: &str = "ad";
62const MOUNT_DIR: &str = ".ad/mnt";
63const IO_UNIT: u32 = 8168;
64
65const MOUNT_ROOT_QID: u64 = 0;
68const CONTROL_FILE_QID: u64 = 1;
70const CONTROL_FILE: &str = "ctl";
71const LOG_FILE_QID: u64 = 2;
73const LOG_FILE: &str = "log";
74const MINIBUFFER_QID: u64 = 3;
76const MINIBUFFER: &str = "minibuffer";
77const BUFFERS_QID: u64 = 4;
79const BUFFERS_DIR: &str = "buffers";
80const INDEX_BUFFER_QID: u64 = 5;
82const INDEX_BUFFER: &str = "index";
83const CURRENT_BUFFER_QID: u64 = 6;
85const CURRENT_BUFFER: &str = "current";
86
87const QID_OFFSET: u64 = 9;
100
101const TOP_LEVEL_QIDS: [u64; 7] = [
102 MOUNT_ROOT_QID,
103 CONTROL_FILE_QID,
104 MINIBUFFER_QID,
105 LOG_FILE_QID,
106 BUFFERS_QID,
107 INDEX_BUFFER_QID,
108 CURRENT_BUFFER_QID,
109];
110
111const E_UNKNOWN_FILE: &str = "unknown file";
112const E_NOT_ALLOWED: &str = "not allowed";
113
114enum InternalRead {
115 Immediate(Vec<u8>),
116 Blocked(Receiver<Vec<u8>>),
117 Unknown,
118}
119
120#[derive(Debug, Default)]
121struct Cids {
122 cids: Vec<ClientId>,
123 read_locked: Option<ClientId>,
124}
125
126#[derive(Debug)]
128pub struct FsHandle(JoinHandle<()>);
129
130impl FsHandle {
131 pub fn join(self) {
133 _ = self.0.join();
134 }
135}
136
137#[derive(Debug)]
138enum MiniBufferContent {
139 Buffering(Vec<u8>),
140 Data(Vec<u8>),
141 Pending(Sender<Sender<Vec<u8>>>, Receiver<Vec<u8>>),
142}
143
144#[derive(Debug)]
146pub(crate) struct AdFs {
147 tx: Sender<Event>,
148 buffer_nodes: BufferNodes,
149 minibuffer_content: MiniBufferContent,
150 minibuffer_prompt: Option<String>,
151 open_cids: HashMap<u64, Cids>,
153 mount_dir_stat: Stat,
155 control_file_stat: Stat,
156 minibuffer_stat: Stat,
157 log_file_stat: Stat,
158 mount_path: String,
159 auto_mount: bool,
160}
161
162impl Drop for AdFs {
163 fn drop(&mut self) {
164 if self.auto_mount {
165 let res = Command::new("fusermount")
166 .args(["-u", &self.mount_path])
167 .spawn();
168
169 if let Ok(mut child) = res {
170 _ = child.wait();
171 }
172 }
173 }
174}
175
176impl AdFs {
177 pub fn new(tx: Sender<Event>, brx: Receiver<LogEvent>) -> Self {
179 let home = env::var("HOME").expect("$HOME to be set");
180 let mount_path = format!("{home}/{MOUNT_DIR}");
181
182 if !Path::new(&mount_path).exists() {
183 create_dir_all(&mount_path).expect("to be able to create our mount point");
184 }
185
186 let (log_tx, log_rx) = channel();
187 let (listener_tx, listener_rx) = channel();
188 spawn_log_listener(brx, listener_tx, log_rx);
189
190 let buffer_nodes = BufferNodes::new(tx.clone(), listener_rx, log_tx);
191 let auto_mount = config_handle!().auto_mount;
192
193 Self {
194 tx,
195 buffer_nodes,
196 open_cids: HashMap::new(),
197 minibuffer_content: MiniBufferContent::Data(Vec::new()),
198 minibuffer_prompt: None,
199 mount_dir_stat: empty_dir_stat(MOUNT_ROOT_QID, "/"),
200 control_file_stat: empty_file_stat(CONTROL_FILE_QID, CONTROL_FILE),
201 minibuffer_stat: empty_file_stat(MINIBUFFER_QID, MINIBUFFER),
202 log_file_stat: empty_file_stat(LOG_FILE_QID, LOG_FILE),
203 mount_path,
204 auto_mount,
205 }
206 }
207
208 pub fn run_threaded(self) -> FsHandle {
210 let auto_mount = self.auto_mount;
211 let mount_path = self.mount_path.clone();
212 let socket_path = socket_path(DEFAULT_SOCKET_NAME);
213
214 let s = Server::new(self);
215 let handle = FsHandle(s.serve_socket(DEFAULT_SOCKET_NAME.to_string()));
216
217 if auto_mount {
218 let res = Command::new("9pfuse")
219 .args([socket_path, mount_path])
220 .spawn();
221
222 if let Ok(mut child) = res {
223 _ = child.wait();
224 }
225 }
226
227 handle
228 }
229
230 fn add_open_cid(&mut self, qid: u64, cid: ClientId) {
231 self.open_cids.entry(qid).or_default().cids.push(cid);
232 }
233
234 fn remove_open_cid(&mut self, qid: u64, cid: ClientId) {
235 self.open_cids.entry(qid).and_modify(|cids| {
236 cids.cids.retain(|&id| id != cid);
237 if cids.read_locked == Some(cid) {
238 cids.read_locked = None;
239 }
240 });
241 }
242
243 fn lock_qid_for_reading(&mut self, qid: u64, cid: ClientId) -> Result<()> {
244 trace!("locking qid for reading qid={qid} cid={cid:?}");
245 match self.open_cids.get_mut(&qid) {
246 Some(cids) => cids.read_locked = Some(cid),
247 None => return Err(E_UNKNOWN_FILE.to_string()),
248 }
249
250 Ok(())
251 }
252
253 fn readlocked_cid(&self, qid: u64) -> Option<ClientId> {
254 self.open_cids.get(&qid).and_then(|cids| cids.read_locked)
255 }
256
257 fn minibuffer_write(&mut self, lines: String) -> Result<usize> {
261 let n_bytes = lines.len();
262 match &mut self.minibuffer_content {
263 MiniBufferContent::Buffering(buffer) => buffer.extend_from_slice(lines.as_bytes()),
264 _ => self.minibuffer_content = MiniBufferContent::Buffering(lines.into_bytes()),
265 }
266
267 Ok(n_bytes)
268 }
269
270 fn set_active_buffer(&mut self, s: String) -> Result<usize> {
271 let id: usize = match s.trim().parse() {
272 Ok(n) => n,
273 Err(_) => {
274 trace!("invalid buffer id submitted to buffers/current: {s}");
275 return Ok(0);
276 }
277 };
278
279 if let Err(e) = self.tx.send(Event::Action(Action::FocusBuffer { id })) {
280 error!("unable to send event to main loop: {e}");
281 return Ok(0);
282 }
283
284 Ok(s.len())
285 }
286
287 fn minibuffer_read(&mut self, offset: usize, count: usize) -> ReadOutcome {
288 match &mut self.minibuffer_content {
289 MiniBufferContent::Buffering(lines_bytes) => {
290 let lines = match String::from_utf8(take(lines_bytes)) {
291 Ok(s) => s,
292 Err(e) => {
293 error!("invalid minibuffer data: {e}");
294 self.minibuffer_content = MiniBufferContent::Buffering(Vec::new());
295 return ReadOutcome::Immediate(Vec::new());
296 }
297 };
298 let prompt = self.minibuffer_prompt.take();
299
300 let (data_tx, data_rx) = channel();
301 let (fsys_tx, fsys_rx) = channel();
302 let (sub_tx, sub_rx) = channel();
303
304 self.minibuffer_stat.n_bytes = 0;
305 self.minibuffer_stat.last_modified = SystemTime::now();
306 spawn_minibuffer_listener(data_rx, fsys_tx, sub_rx);
307
308 let (tx, rx) = channel();
309 _ = sub_tx.send(tx);
310 self.minibuffer_content = MiniBufferContent::Pending(sub_tx, fsys_rx);
311
312 match Message::send(
313 Req::MinibufferSelect {
314 prompt,
315 lines,
316 tx: data_tx,
317 },
318 &self.tx,
319 ) {
320 Ok(_) => ReadOutcome::Blocked(rx),
321 Err(e) => {
322 error!("unable to open minibuffer: {e}");
323 self.minibuffer_content = MiniBufferContent::Buffering(Vec::new());
324 ReadOutcome::Immediate(Vec::new())
325 }
326 }
327 }
328
329 MiniBufferContent::Data(data) => {
330 ReadOutcome::Immediate(apply_offset(data, offset, count))
331 }
332
333 MiniBufferContent::Pending(sub_tx, fsys_rx) => match fsys_rx.try_recv() {
334 Ok(data) => {
335 self.minibuffer_stat.n_bytes = data.len() as u64;
336 self.minibuffer_content = MiniBufferContent::Data(data.clone());
337 ReadOutcome::Immediate(apply_offset(&data, offset, count))
338 }
339 _ => {
340 let (tx, rx) = channel();
341 _ = sub_tx.send(tx);
342 ReadOutcome::Blocked(rx)
343 }
344 },
345 }
346 }
347}
348
349fn spawn_minibuffer_listener(
351 data_rx: Receiver<String>,
352 fsys_tx: Sender<Vec<u8>>,
353 sub_rx: Receiver<Sender<Vec<u8>>>,
354) {
355 spawn(move || {
356 let data = match data_rx.recv() {
357 Ok(s) => s.into_bytes(),
358 Err(e) => {
359 error!("unable to read minibuffer output: {e}");
360 Vec::new()
361 }
362 };
363
364 _ = fsys_tx.send(data.clone());
366
367 for tx in sub_rx.try_iter() {
369 _ = tx.send(data.clone());
370 }
371 });
372}
373
374impl Serve9p for AdFs {
375 fn stat(&mut self, cid: ClientId, qid: u64, uname: &str) -> Result<Stat> {
376 trace!(?cid, %qid, %uname, "handling stat request");
377 self.buffer_nodes.update();
378
379 match qid {
380 MOUNT_ROOT_QID => Ok(self.mount_dir_stat.clone()),
381 CONTROL_FILE_QID => Ok(self.control_file_stat.clone()),
382 MINIBUFFER_QID => Ok(self.minibuffer_stat.clone()),
383 LOG_FILE_QID => Ok(self.log_file_stat.clone()),
384 BUFFERS_QID => Ok(self.buffer_nodes.stat().clone()),
385 qid => match self.buffer_nodes.get_stat_for_qid(qid) {
386 Some(stat) => Ok(stat.clone()),
387 None => Err(E_UNKNOWN_FILE.to_string()),
388 },
389 }
390 }
391
392 fn write_stat(&mut self, cid: ClientId, qid: u64, stat: Stat, uname: &str) -> Result<()> {
393 trace!(?cid, %qid, %uname, "handling write stat request");
394 self.buffer_nodes.update();
395
396 if stat.n_bytes == 0 {
397 trace!(%qid, %uname, "stat n_bytes=0, truncating file");
398 match qid {
399 MOUNT_ROOT_QID | CONTROL_FILE_QID | MINIBUFFER_QID | LOG_FILE_QID => (),
400 qid => self.buffer_nodes.truncate(qid),
401 }
402 }
403
404 Ok(())
405 }
406
407 fn walk(
408 &mut self,
409 cid: ClientId,
410 parent_qid: u64,
411 child: &str,
412 uname: &str,
413 ) -> Result<FileMeta> {
414 trace!(?cid, %parent_qid, %child, %uname, "handling walk request");
415 self.buffer_nodes.update();
416
417 match parent_qid {
418 MOUNT_ROOT_QID => match child {
419 CONTROL_FILE => Ok(self.control_file_stat.fm.clone()),
420 MINIBUFFER => Ok(self.minibuffer_stat.fm.clone()),
421 LOG_FILE => Ok(self.log_file_stat.fm.clone()),
422 BUFFERS_DIR => Ok(self.buffer_nodes.stat().fm.clone()),
423 _ => match self.buffer_nodes.lookup_file_stat(parent_qid, child) {
424 Some(stat) => Ok(stat.fm.clone()),
425 None => Err(format!("{E_UNKNOWN_FILE}: {parent_qid} {child}")),
426 },
427 },
428
429 qid if qid == BUFFERS_QID || self.buffer_nodes.is_known_buffer_qid(qid) => {
430 match self.buffer_nodes.lookup_file_stat(qid, child) {
431 Some(stat) => Ok(stat.fm.clone()),
432 None => Err(format!("{E_UNKNOWN_FILE}: {parent_qid} {child}")),
433 }
434 }
435
436 _ => Err(format!("{E_UNKNOWN_FILE}: {parent_qid} {child}")),
437 }
438 }
439
440 fn open(&mut self, cid: ClientId, qid: u64, mode: Mode, uname: &str) -> Result<IoUnit> {
441 trace!(?cid, %qid, %uname, ?mode, "handling open request");
442 self.buffer_nodes.update();
443
444 if qid == LOG_FILE_QID {
445 self.buffer_nodes.log.add_client(cid);
446 } else if !TOP_LEVEL_QIDS.contains(&qid) {
447 if let QidCheck::Unknown = self.buffer_nodes.check_if_known_qid(qid) {
448 return Err(format!("{E_UNKNOWN_FILE}: {qid}"));
449 }
450 }
451
452 self.add_open_cid(qid, cid);
453
454 Ok(IO_UNIT)
455 }
456
457 fn clunk(&mut self, cid: ClientId, qid: u64) {
458 trace!(?cid, %qid, "handling clunk request");
459
460 if qid == LOG_FILE_QID {
461 self.buffer_nodes.log.remove_client(cid);
462 } else if let QidCheck::EventFile { buf_qid } = self.buffer_nodes.check_if_known_qid(qid) {
463 if self.readlocked_cid(qid) == Some(cid) {
464 self.buffer_nodes.clear_input_filter(buf_qid);
465 }
466 }
467 self.remove_open_cid(qid, cid); }
469
470 fn read(
471 &mut self,
472 cid: ClientId,
473 qid: u64,
474 offset: usize,
475 count: usize,
476 uname: &str,
477 ) -> Result<ReadOutcome> {
478 trace!(?cid, %qid, %offset, %count, %uname, "handling read request");
479 self.buffer_nodes.update();
480
481 if qid == CONTROL_FILE_QID {
482 return Ok(ReadOutcome::Immediate(Vec::new()));
483 } else if qid == MINIBUFFER_QID {
484 return Ok(self.minibuffer_read(offset, count));
485 } else if qid == LOG_FILE_QID {
486 return Ok(self.buffer_nodes.log.events_since_last_read(cid));
487 }
488
489 if let QidCheck::EventFile { buf_qid } = self.buffer_nodes.check_if_known_qid(qid) {
490 match self.readlocked_cid(qid) {
491 Some(id) if id == cid => (),
492 Some(_) => return Ok(ReadOutcome::Immediate(Vec::new())),
493 None => {
494 trace!("attaching filter qid={qid} cid={cid:?}");
495 self.buffer_nodes.attach_input_filter(buf_qid)?;
496 self.lock_qid_for_reading(qid, cid)?;
497 }
498 }
499 }
500
501 match self.buffer_nodes.get_file_content(qid, offset, count) {
502 InternalRead::Unknown => Err(format!("{E_UNKNOWN_FILE}: {qid}")),
503 InternalRead::Immediate(content) => Ok(ReadOutcome::Immediate(content)),
504 InternalRead::Blocked(tx) => Ok(ReadOutcome::Blocked(tx)),
505 }
506 }
507
508 fn read_dir(&mut self, cid: ClientId, qid: u64, uname: &str) -> Result<Vec<Stat>> {
509 trace!(?cid, %qid, %uname, "handling read dir request");
510 self.buffer_nodes.update();
511
512 match qid {
513 MOUNT_ROOT_QID => Ok(vec![
514 self.log_file_stat.clone(),
515 self.minibuffer_stat.clone(),
516 self.control_file_stat.clone(),
517 self.buffer_nodes.stat().clone(),
518 ]),
519 BUFFERS_QID => Ok(self.buffer_nodes.top_level_stats()),
520 qid => self
521 .buffer_nodes
522 .buffer_level_stats(qid)
523 .ok_or_else(|| E_UNKNOWN_FILE.to_string()),
524 }
525 }
526
527 fn write(
528 &mut self,
529 cid: ClientId,
530 qid: u64,
531 offset: usize,
532 data: Vec<u8>,
533 uname: &str,
534 ) -> Result<usize> {
535 trace!(?cid, %qid, %offset, n_bytes=%data.len(), %uname, "handling write request");
536 self.buffer_nodes.update();
537
538 let n_bytes = data.len();
539 let s = match String::from_utf8(data.to_vec()) {
540 Ok(s) => s,
541 Err(e) => return Err(format!("Invalid data: {e}")),
542 };
543
544 match qid {
545 CONTROL_FILE_QID => match s.strip_prefix("minibuffer-prompt ") {
546 Some(prompt) => {
547 self.minibuffer_prompt = Some(prompt.to_string());
548 Ok(n_bytes)
549 }
550 None => {
551 self.control_file_stat.last_modified = SystemTime::now();
552 match Message::send(Req::ControlMessage { msg: s }, &self.tx) {
553 Ok(_) => Ok(n_bytes),
554 Err(e) => Err(format!("unable to execute control message: {e}")),
555 }
556 }
557 },
558
559 MINIBUFFER_QID => self.minibuffer_write(s),
560 CURRENT_BUFFER_QID => self.set_active_buffer(s),
561
562 LOG_FILE_QID | INDEX_BUFFER_QID => Err(E_NOT_ALLOWED.to_string()),
563
564 qid => self.buffer_nodes.write(qid, s, offset),
565 }
566 }
567
568 fn remove(&mut self, cid: ClientId, qid: u64, uname: &str) -> Result<()> {
570 trace!(?cid, %qid, %uname, "handling remove request");
571 Err("remove not allowed".to_string())
572 }
573
574 fn create(
575 &mut self,
576 cid: ClientId,
577 parent: u64,
578 name: &str,
579 perm: Perm,
580 mode: Mode,
581 uname: &str,
582 ) -> Result<(FileMeta, IoUnit)> {
583 trace!(?cid, %parent, %name, ?perm, ?mode, %uname, "handling create request");
584 Err("create not allowed".to_string())
585 }
586}
587
588fn apply_offset(data: &[u8], offset: usize, count: usize) -> Vec<u8> {
589 data.iter()
590 .skip(offset)
591 .take(count)
592 .copied()
593 .collect::<Vec<u8>>()
594}
595
596fn empty_dir_stat(qid: u64, name: &str) -> Stat {
597 Stat {
598 fm: FileMeta::dir(name, qid),
599 perms: Perm::OWNER_READ | Perm::OWNER_EXEC,
600 n_bytes: 0,
601 last_accesses: SystemTime::now(),
602 last_modified: SystemTime::now(),
603 owner: "ad".into(),
604 group: "ad".into(),
605 last_modified_by: "ad".into(),
606 }
607}
608
609fn empty_file_stat(qid: u64, name: &str) -> Stat {
610 Stat {
611 fm: FileMeta::file(name, qid),
612 perms: Perm::OWNER_READ | Perm::OWNER_WRITE,
613 n_bytes: 0,
614 last_accesses: SystemTime::now(),
615 last_modified: SystemTime::now(),
616 owner: "ad".into(),
617 group: "ad".into(),
618 last_modified_by: "ad".into(),
619 }
620}