1use std::{
2 borrow::Cow,
3 convert::TryInto,
4 env,
5 fs::{self, File},
6 io::{self, Read, Write},
7 path::{Path, PathBuf},
8};
9
10use fd_lock::RwLock;
11use tempfile::NamedTempFile;
12use walkdir::WalkDir;
13
14use crate::SPECIAL_NAMES;
15use crate::{pgp, InternalError};
16use crate::{Error, Result, Tag};
17
18const PATH_PREFIX_LEN: usize = 2;
19
20const TRACE: bool = false;
21
22pub enum MergeResult<'a> {
26 Keep,
28
29 DataRef(&'a [u8]),
34
35 Data(Vec<u8>),
40}
41
42impl<'a> From<&'a [u8]> for MergeResult<'a> {
43 fn from(data: &'a [u8]) -> Self {
44 MergeResult::DataRef(data)
45 }
46}
47
48impl From<Vec<u8>> for MergeResult<'_> {
49 fn from(data: Vec<u8>) -> Self {
50 MergeResult::Data(data)
51 }
52}
53
54struct CertDTag([Tag; 256]);
58
59impl CertDTag {
60 fn null() -> Self {
61 Self([Tag(676149182_1608123167); 256])
62 }
63
64 fn compress(&self) -> Tag {
65 let mut composite: u64 = 0;
68 for (i, tag) in self.0.iter().enumerate() {
69 let mut tag = tag.0;
70
71 tag = tag.rotate_right(i as u32);
72
73 composite ^= tag
74 }
75
76 Tag(composite)
77 }
78}
79
80#[derive(Debug)]
97pub struct CertD {
98 base: PathBuf,
99}
100
101impl CertD {
102 pub fn new() -> Result<CertD> {
110 CertD::with_base_dir(Self::user_configured_store_path()?)
111 }
112
113 pub fn user_configured_store_path() -> Result<PathBuf> {
119 if let Some(path) = env::var_os("PGP_CERT_D") {
120 Ok(PathBuf::from(path))
121 } else {
122 CertD::default_store_path()
123 }
124 }
125
126 pub fn default_store_path() -> Result<PathBuf> {
133 Ok(dirs::data_dir()
134 .ok_or(Error::UnsupportedPlatform(
135 "Default store's path".into()))?
136 .join("pgp.cert.d"))
137 }
138
139 pub fn with_base_dir<P: AsRef<Path>>(base: P) -> Result<CertD> {
147 Ok(CertD {
148 base: base.as_ref().into(),
149 })
150 }
151
152 pub fn base_dir(&self) -> &Path {
154 &self.base
155 }
156
157 pub fn tag(&self) -> Tag {
171 let revert_to_readdir = Some(4);
172
173 platform! {
174 unix => self.tag_probe_unix(revert_to_readdir),
175 windows => self.tag_probe_std(revert_to_readdir),
176 }
177 }
178
179 #[doc(hidden)]
188 pub fn tag_readdir_std(&self) -> Tag {
189 tracer!(TRACE, "CertD::tag_readdir_std");
190
191 let mut composite = CertDTag::null();
192
193 let dir = std::fs::read_dir(&self.base);
194 if let Ok(dir) = dir {
195 'entry: for e in dir {
196 let e = if let Ok(e) = e {
197 e
198 } else {
199 continue;
200 };
201
202 if let Ok(file_type) = e.file_type() {
209 if file_type.is_dir() {
210 } else {
212 continue;
213 }
214 } else {
215 continue;
216 }
217
218 let filename = e.file_name();
219 t!("Examining {:?}", filename);
220 let filename: &[u8] = platform! {
221 unix => {
222 use std::os::unix::ffi::OsStrExt;
223 filename.as_bytes()
224 },
225 windows => {
226 if let Some(filename) = filename.to_str() {
227 filename.as_bytes()
228 } else {
229 t!("Can't convert to a str.");
230 continue;
231 }
232 }
233 };
234
235 if filename.len() != 2 {
236 t!("Wrong length.");
237 continue;
238 }
239
240 let mut nibbles: [u8; 2] = [0; 2];
241
242 for i in 0..2usize {
243 let v = filename[i];
244 nibbles[i] = match v {
245 b'0'..=b'9' => v - b'0',
246 b'a'..=b'f' => 10 + v - b'a',
247 _ => {
248 t!("{}: contains non-lower-hex characters.",
249 String::from_utf8_lossy(filename));
250 continue 'entry;
251 }
252 };
253 }
254 let i = ((nibbles[0] << 4) + nibbles[1]) as usize;
255
256 let metadata = if let Ok(metadata) = e.metadata() {
259 metadata
260 } else {
261 t!("{:02x}: Can't read meta-data.", i);
262 continue;
263 };
264
265 let tag = if let Ok(tag) = Tag::try_from(metadata) {
266 tag
267 } else {
268 t!("Can't compute tag.");
269 continue;
270 };
271
272 t!("{:02x} => Tag({:x})", i, tag.0);
273
274 composite.0[i] = tag;
275 }
276 }
277
278 composite.compress()
279 }
280
281 #[cfg(unix)]
289 #[doc(hidden)]
290 pub fn tag_readdir_unix(&self) -> Tag {
291 use crate::unixdir::Dir;
292
293 tracer!(TRACE, "CertD::tag_readdir_unix");
294
295 let mut composite = CertDTag::null();
296
297 let dir = Dir::open(&self.base);
298
299 if let Ok(mut dir) = dir {
300 'entry: while let Some(e) = dir.readdir() {
301 let file_type = e.file_type();
302 if file_type.is_dir() || file_type.is_unknown() {
303 } else {
305 continue;
306 }
307
308 let filename = e.file_name();
309 t!("Examining {}", String::from_utf8_lossy(filename));
310 if filename.len() != 2 {
311 t!("Wrong length.");
312 continue;
313 }
314
315 let mut nibbles: [u8; 2] = [0; 2];
316 for i in 0..2usize {
317 let v = filename[i];
318 nibbles[i] = match v {
319 b'0'..=b'9' => v - b'0',
320 b'a'..=b'f' => 10 + v - b'a',
321 _ => {
322 t!("{}: contains non-lower-hex characters.",
323 String::from_utf8_lossy(filename));
324 continue 'entry;
325 }
326 };
327 }
328 let i = ((nibbles[0] << 4) + nibbles[1]) as usize;
329
330 let metadata = if let Ok(metadata) = e.metadata() {
331 metadata
332 } else {
333 t!("{:02x}: Can't read meta-data.", i);
334 continue;
335 };
336
337 if ! metadata.is_dir() {
340 t!("{:02x}: Not a directory.");
341 continue;
342 }
343
344 let tag = Tag::from(metadata);
345 t!("{:02x} => Tag({:x})", i, tag.0);
346
347 composite.0[i] = tag;
348 }
349 }
350
351 composite.compress()
352 }
353
354 #[doc(hidden)]
363 pub fn tag_probe_std(&self, revert_to_readir: Option<usize>) -> Tag {
364 tracer!(TRACE, "CertD::tag_probe_std");
365
366 const FILENAMES: [&str; 256] = [
367 "00", "01", "02", "03", "04", "05", "06", "07",
368 "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
369 "10", "11", "12", "13", "14", "15", "16", "17",
370 "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
371 "20", "21", "22", "23", "24", "25", "26", "27",
372 "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
373 "30", "31", "32", "33", "34", "35", "36", "37",
374 "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
375 "40", "41", "42", "43", "44", "45", "46", "47",
376 "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
377 "50", "51", "52", "53", "54", "55", "56", "57",
378 "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
379 "60", "61", "62", "63", "64", "65", "66", "67",
380 "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
381 "70", "71", "72", "73", "74", "75", "76", "77",
382 "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
383 "80", "81", "82", "83", "84", "85", "86", "87",
384 "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
385 "90", "91", "92", "93", "94", "95", "96", "97",
386 "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
387 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
388 "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
389 "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7",
390 "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
391 "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
392 "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
393 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
394 "d8", "d9", "da", "db", "dc", "dd", "de", "df",
395 "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7",
396 "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
397 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
398 "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
399 ];
400
401 let mut misses = 0;
402 let mut hits = 0;
403
404 let mut composite = CertDTag::null();
405
406 let base = self.base_dir();
407
408 for (i, filename) in FILENAMES.iter().enumerate() {
409 let mut path = base.to_path_buf();
410 path.push(filename);
411
412 let metadata = if let Ok(metadata) = std::fs::metadata(path) {
413 hits += 1;
414 metadata
415 } else {
416 t!("{:02x}: No such file or directory.", i);
417
418 misses += 1;
419
420 if let Some(revert_to_readir) = revert_to_readir {
421 if revert_to_readir == misses && hits <= 1 {
422 t!("Too many misses; switching to readir implementation.");
423 return self.tag_readdir_std();
424 }
425 }
426
427 continue;
428 };
429
430 if ! metadata.file_type().is_dir() {
431 t!("{:02x}: Not a directory.");
432 continue;
433 }
434
435 let tag = match Tag::try_from(&metadata) {
436 Ok(tag) => tag,
437 Err(err) => {
438 t!("{:02x}: Can't compute tag: {}.", i, err);
439 continue;
440 }
441 };
442
443 t!("{:02x} => Tag({:x})", i, tag.0);
444
445 composite.0[i] = tag;
446 }
447
448 composite.compress()
449 }
450
451 #[doc(hidden)]
460 #[cfg(unix)]
461 pub fn tag_probe_unix(&self, revert_to_readir: Option<usize>) -> Tag {
462 use crate::unixdir::Dir;
463
464 tracer!(TRACE, "CertD::tag_probe_unix");
465
466 const FILENAMES: [&str; 256] = [
467 "00\0", "01\0", "02\0", "03\0", "04\0", "05\0", "06\0", "07\0",
468 "08\0", "09\0", "0a\0", "0b\0", "0c\0", "0d\0", "0e\0", "0f\0",
469 "10\0", "11\0", "12\0", "13\0", "14\0", "15\0", "16\0", "17\0",
470 "18\0", "19\0", "1a\0", "1b\0", "1c\0", "1d\0", "1e\0", "1f\0",
471 "20\0", "21\0", "22\0", "23\0", "24\0", "25\0", "26\0", "27\0",
472 "28\0", "29\0", "2a\0", "2b\0", "2c\0", "2d\0", "2e\0", "2f\0",
473 "30\0", "31\0", "32\0", "33\0", "34\0", "35\0", "36\0", "37\0",
474 "38\0", "39\0", "3a\0", "3b\0", "3c\0", "3d\0", "3e\0", "3f\0",
475 "40\0", "41\0", "42\0", "43\0", "44\0", "45\0", "46\0", "47\0",
476 "48\0", "49\0", "4a\0", "4b\0", "4c\0", "4d\0", "4e\0", "4f\0",
477 "50\0", "51\0", "52\0", "53\0", "54\0", "55\0", "56\0", "57\0",
478 "58\0", "59\0", "5a\0", "5b\0", "5c\0", "5d\0", "5e\0", "5f\0",
479 "60\0", "61\0", "62\0", "63\0", "64\0", "65\0", "66\0", "67\0",
480 "68\0", "69\0", "6a\0", "6b\0", "6c\0", "6d\0", "6e\0", "6f\0",
481 "70\0", "71\0", "72\0", "73\0", "74\0", "75\0", "76\0", "77\0",
482 "78\0", "79\0", "7a\0", "7b\0", "7c\0", "7d\0", "7e\0", "7f\0",
483 "80\0", "81\0", "82\0", "83\0", "84\0", "85\0", "86\0", "87\0",
484 "88\0", "89\0", "8a\0", "8b\0", "8c\0", "8d\0", "8e\0", "8f\0",
485 "90\0", "91\0", "92\0", "93\0", "94\0", "95\0", "96\0", "97\0",
486 "98\0", "99\0", "9a\0", "9b\0", "9c\0", "9d\0", "9e\0", "9f\0",
487 "a0\0", "a1\0", "a2\0", "a3\0", "a4\0", "a5\0", "a6\0", "a7\0",
488 "a8\0", "a9\0", "aa\0", "ab\0", "ac\0", "ad\0", "ae\0", "af\0",
489 "b0\0", "b1\0", "b2\0", "b3\0", "b4\0", "b5\0", "b6\0", "b7\0",
490 "b8\0", "b9\0", "ba\0", "bb\0", "bc\0", "bd\0", "be\0", "bf\0",
491 "c0\0", "c1\0", "c2\0", "c3\0", "c4\0", "c5\0", "c6\0", "c7\0",
492 "c8\0", "c9\0", "ca\0", "cb\0", "cc\0", "cd\0", "ce\0", "cf\0",
493 "d0\0", "d1\0", "d2\0", "d3\0", "d4\0", "d5\0", "d6\0", "d7\0",
494 "d8\0", "d9\0", "da\0", "db\0", "dc\0", "dd\0", "de\0", "df\0",
495 "e0\0", "e1\0", "e2\0", "e3\0", "e4\0", "e5\0", "e6\0", "e7\0",
496 "e8\0", "e9\0", "ea\0", "eb\0", "ec\0", "ed\0", "ee\0", "ef\0",
497 "f0\0", "f1\0", "f2\0", "f3\0", "f4\0", "f5\0", "f6\0", "f7\0",
498 "f8\0", "f9\0", "fa\0", "fb\0", "fc\0", "fd\0", "fe\0", "ff\0",
499 ];
500
501 let mut misses = 0;
502 let mut hits = 0;
503
504 let mut composite = CertDTag::null();
505
506 let base = self.base_dir();
507 let dir = Dir::open(base);
508
509 if let Ok(mut dir) = dir {
510 for (i, filename) in FILENAMES.iter().enumerate() {
511 let metadata = if let Ok(metadata) = dir.fstat(filename.as_bytes()) {
512 hits += 1;
513 metadata
514 } else {
515 t!("{:02x}: No such file or directory.", i);
516
517 misses += 1;
518
519 if let Some(revert_to_readir) = revert_to_readir {
520 if revert_to_readir == misses && hits <= 1 {
521 t!("Too many misses; switching to readdir implementation.");
522 return self.tag_readdir_unix();
523 }
524 }
525
526 continue;
527 };
528
529 if ! metadata.is_dir() {
530 t!("{:02x}: Not a directory.");
531 continue;
532 }
533
534 let tag = Tag::from(&metadata);
535 t!("{:02x} => Tag({:x})", i, tag.0);
536
537 composite.0[i] = tag
538 }
539 }
540
541 composite.compress()
542 }
543
544 pub fn get_path_by_fingerprint(&self, fingerprint: &str) -> Result<PathBuf> {
551 if ! [pgp::FINGERPRINT_LEN_CHARS_V4,
552 pgp::FINGERPRINT_LEN_CHARS_V6].contains(&fingerprint.len()) {
553 return Err(Error::BadName);
554 }
555 if fingerprint.chars().any(|c| !c.is_ascii_hexdigit()) {
556 return Err(Error::BadName);
557 }
558 let fingerprint = fingerprint.to_ascii_lowercase();
559 Ok(self.base.join(&fingerprint[..2]).join(&fingerprint[2..]))
560 }
561
562 fn get_fingerprint_by_path(
565 &self,
566 path: &Path,
567 ) -> std::result::Result<String, InternalError> {
568 let path = if path.is_absolute() {
569 path.strip_prefix(&self.base)
570 .map_err(|_| InternalError::PathNotInStore)?
571 } else {
572 path
573 };
574 if !self.base.join(path).is_file() {
575 return Err(InternalError::BadFingerprintPath);
576 }
577 if path.components().count() != 2 {
578 return Err(InternalError::BadFingerprintPath);
579 }
580 let components =
581 path.components().map(|c| c.as_os_str()).collect::<Vec<_>>();
582 if components.iter().any(|c| !c.is_ascii()) {
583 return Err(InternalError::BadFingerprintPath);
584 }
585 let head = components[0].to_string_lossy();
586 if head.len() != PATH_PREFIX_LEN {
587 return Err(InternalError::BadFingerprintPath);
588 }
589 let tail = components[1].to_string_lossy();
590 if tail.len() != pgp::FINGERPRINT_LEN_CHARS_V4 - PATH_PREFIX_LEN
591 && tail.len() != pgp::FINGERPRINT_LEN_CHARS_V6 - PATH_PREFIX_LEN
592 {
593 return Err(InternalError::BadFingerprintPath);
594 }
595 Ok(head.to_string() + &tail)
596 }
597
598 pub fn get_path_by_special(&self, special: &str) -> Result<PathBuf> {
608 Self::get_relative_path_by_special(special).map(|special| {
609 self.base.join(special)
610 })
611 }
612
613 pub fn is_special(special: &str) -> Result<()> {
623 Self::get_relative_path_by_special(special).map(|_| ())
624 }
625
626 fn get_relative_path_by_special(special: &str) -> Result<PathBuf> {
629 if let Some('_') = special.chars().next() {
630 let special = PathBuf::from(special);
631 if special.components().count() != 1 {
632 Err(Error::BadName)
633 } else {
634 Ok(special)
635 }
636 } else if SPECIAL_NAMES.binary_search(&special).is_ok() {
637 Ok(PathBuf::from(special))
638 } else {
639 Err(Error::BadName)
640 }
641 }
642
643 pub fn get(&self, name: &str) -> Result<Option<(Tag, Vec<u8>)>> {
667 if let Some(mut fp) = self.get_file(name)? {
668 let tag = Tag::try_from(&fp)?;
669 let mut buf = Vec::new();
670 fp.read_to_end(&mut buf)?;
671 Ok(Some((tag, buf)))
672 } else {
673 Ok(None)
674 }
675 }
676
677 pub fn get_file(&self, name: &str) -> Result<Option<File>> {
697 let path = self.get_path(name)?;
698 match fs::File::open(path) {
699 Ok(f) => Ok(Some(f)),
700 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
701 Err(e) => Err(e.into()),
702 }
703 }
704
705 pub fn get_if_changed(
728 &self,
729 since: Tag,
730 name: &str,
731 ) -> Result<Option<(Tag, Vec<u8>)>> {
732 let path = self.get_path(name)?;
733 match fs::File::open(path) {
734 Ok(mut f) => {
735 let tag = f.metadata()?.try_into()?;
736 if since == tag {
737 Ok(None) } else {
739 let mut buf = Vec::new();
740 f.read_to_end(&mut buf)?;
741 Ok(Some((tag, buf)))
742 }
743 }
744 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
745 Err(e) => Err(e.into()),
746 }
747 }
748
749 pub fn get_path(&self, name: &str) -> Result<PathBuf> {
759 self.get_path_by_fingerprint(name)
764 .or_else(|_| self.get_path_by_special(name))
765 }
766
767 pub fn insert<'a, D, M>(&self, fingerprint: &str, data: D,
784 return_inserted: bool, merge: M)
785 -> Result<(Tag, Option<Vec<u8>>)>
786 where
787 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
788 {
789 self.insert_extended(
790 fingerprint, data, return_inserted,
791 |d| Ok(d),
792 merge,
793 |r| Ok(r))
794 }
795
796 pub fn insert_extended<'a, D, PRE, M, POST>(
810 &self, fingerprint: &str, data: D,
811 return_inserted: bool, pre: PRE, merge: M, post: POST)
812 -> Result<(Tag, Option<Vec<u8>>)>
813 where
814 PRE: FnOnce(D) -> Result<D>,
815 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
816 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
817 {
818 let blocking = true;
819 self.insert_impl(
820 fingerprint, true, data, return_inserted,
821 pre, merge, post,
822 blocking)
823 }
824
825 pub fn try_insert<'a, D, M>(&self, fingerprint: &str, data: D,
843 return_inserted: bool, merge: M)
844 -> Result<(Tag, Option<Vec<u8>>)>
845 where
846 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
847 {
848 self.try_insert_extended(
849 fingerprint, data, return_inserted,
850 |d| Ok(d),
851 merge,
852 |r| Ok(r))
853 }
854
855 pub fn try_insert_extended<'a, D, PRE, M, POST>(
869 &self, fingerprint: &str, data: D,
870 return_inserted: bool, pre: PRE, merge: M, post: POST)
871 -> Result<(Tag, Option<Vec<u8>>)>
872 where
873 PRE: FnOnce(D) -> Result<D>,
874 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
875 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
876 {
877 let blocking = false;
878 self.insert_impl(
879 fingerprint, true, data, return_inserted,
880 pre, merge, post, blocking)
881 }
882
883 pub fn insert_data<'a, M>(&self, data: &'a [u8],
900 return_inserted: bool, merge: M)
901 -> Result<(Tag, Option<Vec<u8>>)>
902 where
903 M: FnOnce(&'a [u8], Option<&[u8]>) -> Result<MergeResult<'a>>,
904 {
905 self.insert_data_extended(
906 data, return_inserted,
907 |d| Ok(d),
908 merge,
909 |r| Ok(r))
910 }
911
912 pub fn insert_data_extended<'a, PRE, M, POST>(
923 &self, data: &'a [u8],
924 return_inserted: bool,
925 pre: PRE, merge: M, post: POST)
926 -> Result<(Tag, Option<Vec<u8>>)>
927 where
928 PRE: FnOnce(&'a [u8]) -> Result<&'a [u8]>,
929 M: FnOnce(&'a [u8], Option<&[u8]>) -> Result<MergeResult<'a>>,
930 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
931 {
932 let blocking = true;
933 let fingerprint = pgp::fingerprint(data)?;
934 self.insert_impl(
935 &fingerprint, true, data, return_inserted,
936 pre, merge, post,
937 blocking)
938 }
939
940 pub fn try_insert_data<'a, M>(&self, data: &'a [u8],
958 return_inserted: bool, merge: M)
959 -> Result<(Tag, Option<Vec<u8>>)>
960 where
961 M: FnOnce(&'a [u8], Option<&[u8]>) -> Result<MergeResult<'a>>,
962 {
963 self.try_insert_data_extended(
964 data, return_inserted,
965 |d| Ok(d),
966 merge,
967 |r| Ok(r))
968 }
969
970 pub fn try_insert_data_extended<'a, PRE, M, POST>(
982 &self, data: &'a [u8],
983 return_inserted: bool,
984 pre: PRE, merge: M, post: POST)
985 -> Result<(Tag, Option<Vec<u8>>)>
986 where
987 PRE: FnOnce(&'a [u8]) -> Result<&'a [u8]>,
988 M: FnOnce(&'a [u8], Option<&[u8]>) -> Result<MergeResult<'a>>,
989 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
990 {
991 let blocking = false;
992 pgp::plausible_tsk_or_tpk(data)?;
993 let fingerprint = pgp::fingerprint(data)?;
994 self.insert_impl(
995 &fingerprint, true, data, return_inserted,
996 pre, merge, post,
997 blocking)
998 }
999
1000 pub fn insert_special<'a, D, M>(
1024 &self,
1025 special_name: &str,
1026 data: D,
1027 return_inserted: bool,
1028 merge: M,
1029 ) -> Result<(Tag, Option<Vec<u8>>)>
1030 where
1031 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
1032 {
1033 self.insert_special_extended(
1034 special_name, data, return_inserted,
1035 |d| Ok(d),
1036 merge,
1037 |r| Ok(r))
1038 }
1039
1040 pub fn insert_special_extended<'a, D, PRE, M, POST>(
1055 &self,
1056 special_name: &str,
1057 data: D,
1058 return_inserted: bool,
1059 pre: PRE, merge: M, post: POST
1060 ) -> Result<(Tag, Option<Vec<u8>>)>
1061 where
1062 PRE: FnOnce(D) -> Result<D>,
1063 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
1064 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
1065 {
1066 let blocking = true;
1067 self.insert_impl(
1068 special_name, false, data, return_inserted,
1069 pre,
1070 merge,
1071 post,
1072 blocking)
1073 }
1074
1075 pub fn try_insert_special<'a, D, M>(
1101 &self,
1102 special_name: &str,
1103 data: D,
1104 return_inserted: bool,
1105 merge: M,
1106 ) -> Result<(Tag, Option<Vec<u8>>)>
1107 where
1108 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
1109 {
1110 self.try_insert_special_extended(
1111 special_name, data, return_inserted,
1112 |d| Ok(d),
1113 merge,
1114 |r| Ok(r))
1115 }
1116
1117 pub fn try_insert_special_extended<'a, D, PRE, M, POST>(
1132 &self,
1133 special_name: &str,
1134 data: D,
1135 return_inserted: bool,
1136 pre: PRE, merge: M, post: POST
1137 ) -> Result<(Tag, Option<Vec<u8>>)>
1138 where
1139 PRE: FnOnce(D) -> Result<D>,
1140 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
1141 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
1142 {
1143 let blocking = false;
1144 self.insert_impl(
1145 special_name, false, data, return_inserted,
1146 pre,
1147 merge,
1148 post,
1149 blocking)
1150 }
1151
1152 fn insert_impl<'a, D, PRE, M, POST>(
1153 &self,
1154 name: &str, name_is_fingerprint: bool,
1155 data: D,
1156 return_inserted: bool,
1157 pre: PRE,
1158 merge: M,
1159 post: POST,
1160 blocking: bool,
1161 ) -> Result<(Tag, Option<Vec<u8>>)>
1162 where
1163 PRE: FnOnce(D) -> Result<D>,
1164 M: FnOnce(D, Option<&[u8]>) -> Result<MergeResult<'a>>,
1165 POST: FnOnce((Tag, Option<Vec<u8>>)) -> Result<(Tag, Option<Vec<u8>>)>,
1166 {
1167 let name = if name_is_fingerprint {
1168 pgp::canonicalize_fingerprint(name)?
1169 } else {
1170 Cow::Borrowed(name)
1171 };
1172
1173 let target_path = self.get_path(&name)?;
1174
1175 let mut lf = RwLock::new(self.idempotent_create_lockfile()?);
1176 let lock = if blocking {
1178 lf.write()?
1179 } else {
1180 lf.try_write()?
1181 };
1182
1183 let data = pre(data)?;
1185
1186 fs::create_dir_all(target_path.parent().expect("at least one leg"))?;
1188
1189 let old_cert = self.get(&name)?.map(|(_, cert)| cert);
1190 let old_cert = old_cert.as_deref();
1191 let merge_result = merge(data, old_cert)?;
1192 let new_cert = match merge_result {
1193 MergeResult::Keep => old_cert.unwrap_or(&[]),
1194 MergeResult::DataRef(data) => data,
1195 MergeResult::Data(ref data) => data,
1196 };
1197
1198 pgp::plausible_tsk_or_tpk(new_cert)?;
1199
1200 if name_is_fingerprint {
1201 let fingerprint = pgp::fingerprint(new_cert)?;
1202 if fingerprint != name {
1203 return Err(Error::BadData(pgp::Error::WrongCertificate(
1204 name.to_string(), fingerprint.to_string())));
1205 }
1206 }
1207
1208 if let MergeResult::Keep = merge_result {
1209 } else {
1211 let mut tmp = NamedTempFile::new_in(&self.base)?;
1212 tmp.write_all(new_cert.as_ref())?;
1213 tmp.persist(&target_path).map_err(|e| e.error)?;
1214 }
1215
1216 let tag = fs::File::open(&target_path)?.metadata()?.try_into()?;
1217
1218 let cert = if return_inserted {
1219 Some(new_cert.to_vec())
1220 } else {
1221 None
1222 };
1223
1224 let (tag, cert) = post((tag, cert))?;
1226
1227 drop(lock);
1228
1229 Ok((tag, cert))
1230 }
1231
1232 pub fn fingerprints(&self) -> impl Iterator<Item = Result<String>> + '_ {
1238 WalkDir::new(&self.base)
1239 .max_depth(2)
1241 .min_depth(2)
1242 .into_iter()
1243 .filter_map(move |e| match e {
1247 Ok(entry) => match self.get_fingerprint_by_path(entry.path()) {
1248 Ok(fingerprint) => Some(Ok(fingerprint)),
1249 Err(_) => None,
1250 },
1251 Err(err) => {
1252 if let Some(std::io::ErrorKind::NotFound)
1253 = err.io_error().map(|err| err.kind())
1254 {
1255 None
1257 } else {
1258 Some(Err(err.into()))
1259 }
1260 }
1261 })
1262 }
1263
1264 pub fn iter_files(
1273 &self,
1274 ) -> impl Iterator<Item = Result<(String, File)>> + '_ {
1275 let get_with_fingerprint = move |fingerprint: &str| -> Result<(String, File)> {
1278 match self.get_file(fingerprint)? {
1279 None => Err(Error::IoError(io::Error::new(
1280 io::ErrorKind::Other,
1281 format!("The file for {} disappeared.", fingerprint),
1284 ))),
1285 Some(file) => Ok((fingerprint.to_owned(), file)),
1286 }
1287 };
1288
1289 self.fingerprints()
1290 .map(move |fingerprint_result| {
1291 fingerprint_result.and_then(|fingerprint| {
1292 get_with_fingerprint(&fingerprint)
1293 })
1294 })
1295 }
1296
1297 pub fn iter(
1306 &self,
1307 ) -> impl Iterator<Item = Result<(String, Tag, Vec<u8>)>> + '_ {
1308 self.iter_files()
1309 .map(|r| {
1310 let (fingerprint, mut fp) = r?;
1311 let tag = Tag::try_from(&fp)?;
1312
1313 let mut data = Vec::new();
1314 fp.read_to_end(&mut data)?;
1315
1316 Ok((fingerprint, tag, data))
1317 })
1318 }
1319
1320 fn idempotent_create_lockfile(&self) -> Result<std::fs::File> {
1321 let lock_path = self.base.join("writelock");
1322 std::fs::OpenOptions::new()
1324 .write(true)
1325 .create(true)
1326 .truncate(true)
1327 .open(lock_path)
1328 .map_err(Into::into)
1329 }
1330}
1331
1332#[cfg(test)]
1333mod tests {
1334 use super::*;
1335 use assert_fs::prelude::*;
1336 use predicates::prelude::*;
1337
1338 use crate::TRUST_ROOT;
1339
1340 fn test_base() -> assert_fs::TempDir {
1341 let base = assert_fs::TempDir::new().unwrap();
1342 match std::env::var_os("CERTD_TEST_PERSIST") {
1343 Some(_) => {
1344 eprintln!("Test base dir: {}", &base.path().to_string_lossy());
1345 base.into_persistent()
1346 }
1347 None => base,
1348 }
1349 }
1350
1351 struct Testdata<'a> {
1352 data: &'a [u8],
1353 fingerprint: &'a str,
1354 }
1355
1356 impl Testdata<'_> {
1357 fn path(&self) -> String {
1358 [&self.fingerprint[..2], &self.fingerprint[2..]].join("/")
1359 }
1360
1361 fn add_to_certd(&self, base: &assert_fs::TempDir) {
1362 base.child(self.path()).write_binary(self.data).unwrap();
1363 }
1364 }
1365
1366 static ALICE: Testdata = Testdata {
1367 fingerprint: "eb85bb5fa33a75e15e944e63f231550c4f47e38e",
1368 data: include_bytes!("../../testdata/alice.pgp"),
1369 };
1370
1371 static BOB: Testdata = Testdata {
1372 fingerprint: "d1a66e1a23b182c9980f788cfbfcc82a015e7330",
1373 data: include_bytes!("../../testdata/bob.pgp"),
1374 };
1375
1376 static TESTY: Testdata = Testdata {
1377 fingerprint: "39d100ab67d5bd8c04010205fb3751f1587daef1",
1378 data: include_bytes!("../../testdata/testy-new.pgp"),
1379 };
1380
1381 fn setup_testdir(
1382 testdata: &[&Testdata],
1383 ) -> Result<(assert_fs::TempDir, CertD)> {
1384 let base = test_base();
1385 for t in testdata.iter() {
1386 t.add_to_certd(&base);
1387 }
1388
1389 let trust_root_data = include_bytes!("../../testdata/sender.pgp");
1390 base.child("trust-root")
1391 .write_binary(trust_root_data)
1392 .unwrap();
1393
1394 let certd = CertD::with_base_dir(&base)?;
1395 Ok((base, certd))
1396 }
1397
1398 #[test]
1399 fn get_fingerprint() -> std::result::Result<(), Box<dyn std::error::Error>> {
1400 let data = include_bytes!("../../testdata/testy-new.pgp");
1401
1402 let base = test_base();
1403 base.child("39/d100ab67d5bd8c04010205fb3751f1587daef1")
1404 .write_binary(data)
1405 .unwrap();
1406
1407 let certd = CertD::with_base_dir(&base)?;
1408
1409 let (tag, cert) = certd
1410 .get("39d100ab67d5bd8c04010205fb3751f1587daef1")?
1411 .unwrap();
1412 assert_eq!(cert, data);
1413
1414 assert!(certd
1415 .get_if_changed(tag, "39d100ab67d5bd8c04010205fb3751f1587daef1")?
1416 .is_none());
1417
1418 let mut fp = certd
1419 .get_file("39d100ab67d5bd8c04010205fb3751f1587daef1")?
1420 .unwrap();
1421 let tag = Tag::try_from(&fp)?;
1422 let mut data = Vec::new();
1423 fp.read_to_end(&mut data)?;
1424 assert_eq!(cert, data);
1425
1426 assert!(certd
1427 .get_if_changed(tag, "39d100ab67d5bd8c04010205fb3751f1587daef1")?
1428 .is_none());
1429
1430 base.close().unwrap();
1431 Ok(())
1432 }
1433
1434 #[test]
1435 fn get_special() -> std::result::Result<(), Box<dyn std::error::Error>> {
1436 let data = include_bytes!("../../testdata/sender.pgp");
1437
1438 let base = test_base();
1439 base.child("trust-root").write_binary(data).unwrap();
1440
1441 let certd = CertD::with_base_dir(&base)?;
1442
1443 let (tag, cert) = certd.get(TRUST_ROOT)?.unwrap();
1444 assert_eq!(cert, data);
1445
1446 assert!(certd.get_if_changed(tag, TRUST_ROOT)?.is_none());
1447
1448 base.close().unwrap();
1449 Ok(())
1450 }
1451
1452 #[test]
1453 fn get_not_found() -> Result<()> {
1454 let base = test_base();
1455 let certd = CertD::with_base_dir(&base)?;
1456 let result = certd.get("39d100ab67d5bd8c04010205fb3751f1587daef1");
1457 assert!(matches!(result, Ok(None)));
1458 Ok(())
1459 }
1460
1461 #[test]
1462 fn insert_locked() -> Result<()> {
1463 let data = include_bytes!("../../testdata/testy-new.pgp");
1464 let base = test_base();
1465
1466 let file = base.child("39/d100ab67d5bd8c04010205fb3751f1587daef1");
1467 file.assert(predicate::path::missing());
1468
1469 let certd = CertD::with_base_dir(&base)?;
1470
1471 let mut lf = RwLock::new(certd.idempotent_create_lockfile()?);
1473 let _lock = lf.write()?;
1475
1476 let result = certd.try_insert_data(
1477 data, false,
1478 |new: &[u8], old: Option<&[u8]>| {
1479 assert!(old.is_none());
1480 Ok(MergeResult::DataRef(new))
1481 });
1482
1483 match result.unwrap_err() {
1484 Error::IoError(e) if e.kind() == io::ErrorKind::WouldBlock => {
1485 Ok(())
1486 }
1487 e => Err(e),
1488 }
1489 }
1490
1491 #[test]
1492 fn insert_special_locked() -> Result<()> {
1493 let data = include_bytes!("../../testdata/sender.pgp");
1494 let base = test_base();
1495
1496 let file = base.child("trust-root");
1497 file.assert(predicate::path::missing());
1498
1499 let certd = CertD::with_base_dir(&base)?;
1500
1501 let mut lock = RwLock::new(certd.idempotent_create_lockfile()?);
1503 let _lock = lock.write()?;
1505
1506 let result = certd.try_insert_special(
1507 TRUST_ROOT,
1508 &data[..],
1509 false,
1510 |new: &[u8], old: Option<&[u8]>| {
1511 assert!(old.is_none());
1512 Ok(MergeResult::DataRef(new))
1513 },
1514 );
1515
1516 match result.unwrap_err() {
1517 Error::IoError(e) if e.kind() == io::ErrorKind::WouldBlock => {
1518 Ok(())
1519 }
1520 e => Err(e),
1521 }
1522 }
1523
1524 #[test]
1525 fn insert_new() -> Result<()> {
1526 let data = include_bytes!("../../testdata/testy-new.pgp");
1527 let data = &data[..];
1528 let base = test_base();
1529
1530 let file = base.child("39/d100ab67d5bd8c04010205fb3751f1587daef1");
1531 file.assert(predicate::path::missing());
1532
1533 let certd = CertD::with_base_dir(&base)?;
1534
1535 let (_, inserted) = certd.insert_data(
1536 data,
1537 true,
1538 |new: &[u8], old: Option<&[u8]>| {
1539 assert!(old.is_none());
1540 Ok(MergeResult::DataRef(new))
1541 })?;
1542 file.assert(data);
1543 assert_eq!(inserted.as_deref(), Some(data));
1544
1545 Ok(())
1546 }
1547
1548 #[test]
1549 fn insert_special_new() -> Result<()> {
1550 let data = include_bytes!("../../testdata/sender.pgp");
1551 let data = &data[..];
1552 let base = test_base();
1553
1554 let file = base.child("trust-root");
1555 file.assert(predicate::path::missing());
1556
1557 let certd = CertD::with_base_dir(&base)?;
1558
1559 let (_, inserted) = certd.insert_special(
1560 "trust-root",
1561 data,
1562 true,
1563 |new: &[u8], old: Option<&[u8]>| {
1564 assert!(old.is_none());
1565 Ok(MergeResult::DataRef(new))
1566 },
1567 )?;
1568 file.assert(data);
1569 assert_eq!(inserted.as_deref(), Some(data));
1570
1571 Ok(())
1572 }
1573
1574 #[test]
1575 fn insert_update() -> std::result::Result<(), Box<dyn std::error::Error>> {
1576 let data = include_bytes!("../../testdata/testy-new.pgp");
1577 let data = &data[..];
1578 let base = test_base();
1579
1580 let file = base.child("39/d100ab67d5bd8c04010205fb3751f1587daef1");
1581 file.touch().unwrap();
1582 file.assert(predicate::str::is_empty());
1583
1584 let certd = CertD::with_base_dir(&base)?;
1585
1586 let (_, inserted) = certd.insert_data(
1587 data,
1588 true,
1589 |new: &[u8], old: Option<&[u8]>| {
1590 assert!(old.is_some());
1591 Ok(MergeResult::DataRef(new))
1592 })?;
1593 file.assert(data);
1594 assert_eq!(inserted.as_deref(), Some(data));
1595
1596 Ok(())
1597 }
1598
1599 #[test]
1600 fn insert_special_update(
1601 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
1602 let data = include_bytes!("../../testdata/sender.pgp");
1603 let data = &data[..];
1604 let base = test_base();
1605
1606 let file = base.child("trust-root");
1607 file.touch().unwrap();
1608 file.assert(predicate::str::is_empty());
1609
1610 let certd = CertD::with_base_dir(&base)?;
1611
1612 let (_, inserted) = certd.insert_special(
1613 TRUST_ROOT,
1614 data,
1615 true,
1616 |new: &[u8], old: Option<&[u8]>| {
1617 assert!(old.is_some());
1618 Ok(MergeResult::DataRef(new))
1619 },
1620 )?;
1621 file.assert(data);
1622 assert_eq!(inserted.as_deref(), Some(data));
1623
1624 Ok(())
1625 }
1626
1627 #[test]
1628 fn insert_get() -> std::result::Result<(), Box<dyn std::error::Error>> {
1629 let data = include_bytes!("../../testdata/testy-new.pgp");
1630 let data = &data[..];
1631 let base = test_base();
1632
1633 let certd = CertD::with_base_dir(&base)?;
1634
1635 certd.insert_data(
1636 data,
1637 false,
1638 |new: &[u8], old: Option<&[u8]>| {
1639 assert!(old.is_none());
1640 Ok(MergeResult::DataRef(new))
1641 })?;
1642 let (_, cert) = certd
1643 .get("39d100ab67d5bd8c04010205fb3751f1587daef1")?
1644 .unwrap();
1645 assert_eq!(cert, data);
1646
1647 Ok(())
1648 }
1649
1650 #[test]
1651 fn get_path_by_fingerprint() -> Result<()> {
1652 let base = test_base();
1653 let certd = CertD::with_base_dir(&base)?;
1654
1655 let expected = base
1656 .path()
1657 .join("39")
1658 .join("d100ab67d5bd8c04010205fb3751f1587daef1");
1659
1660 let fingerprint = "39d100ab67d5bd8c04010205fb3751f1587daef1";
1661 assert_eq!(certd.get_path_by_fingerprint(fingerprint)?, expected);
1662
1663 let fingerprint = "39D100AB67D5BD8C04010205FB3751F1587DAEF1";
1664 assert_eq!(certd.get_path_by_fingerprint(fingerprint)?, expected);
1665
1666 let fingerprint = "39D100ab67D5bD8C04010205FB3751f1587DAeF1";
1667 assert_eq!(certd.get_path_by_fingerprint(fingerprint)?, expected);
1668
1669 Ok(())
1670 }
1671
1672 #[test]
1673 fn get_path_by_fingerprint_negative() -> Result<()> {
1674 let base = test_base();
1675 let certd = CertD::with_base_dir(&base)?;
1676
1677 let fingerprint = "";
1679 let result = certd.get_path_by_fingerprint(fingerprint);
1680 assert!(matches!(result.unwrap_err(), Error::BadName));
1681
1682 let fingerprint = "39d100ab67d5bd8c04010205fb3751f1587daef";
1684 let result = certd.get_path_by_fingerprint(fingerprint);
1685 assert!(matches!(result.unwrap_err(), Error::BadName));
1686
1687 let fingerprint = "peter";
1689 let result = certd.get_path_by_fingerprint(fingerprint);
1690 assert!(matches!(result.unwrap_err(), Error::BadName));
1691 Ok(())
1692 }
1693
1694 #[test]
1695 fn get_path_by_special() -> Result<()> {
1696 let base = test_base();
1697 let certd = CertD::with_base_dir(&base)?;
1698
1699 let expected = base.path().join(TRUST_ROOT);
1700
1701 let name = "trust-root";
1702 assert_eq!(certd.get_path_by_special(name)?, expected);
1703
1704 let name = "_sequoia";
1705 assert_eq!(certd.get_path_by_special(name)?,
1706 base.path().join(name));
1707
1708 let name = "_sequoia_foo";
1709 assert_eq!(certd.get_path_by_special(name)?,
1710 base.path().join(name));
1711
1712 Ok(())
1713 }
1714
1715 #[test]
1716 fn get_path_by_special_negative() -> Result<()> {
1717 let base = test_base();
1718 let certd = CertD::with_base_dir(&base)?;
1719
1720 let name = "";
1722 let result = certd.get_path_by_special(name);
1723 assert!(matches!(result.unwrap_err(), Error::BadName));
1724
1725 let name = "mySpecialName";
1727 let result = certd.get_path_by_special(name);
1728 assert!(matches!(result.unwrap_err(), Error::BadName));
1729
1730 let name = "TRUST-ROOT";
1732 let result = certd.get_path_by_special(name);
1733 assert!(matches!(result.unwrap_err(), Error::BadName));
1734
1735 let name = "TrUsT-RooT";
1736 let result = certd.get_path_by_special(name);
1737 assert!(matches!(result.unwrap_err(), Error::BadName));
1738
1739 let name = "_sequoia_foo/bar";
1741 let result = certd.get_path_by_special(name);
1742 assert!(matches!(result.unwrap_err(), Error::BadName));
1743
1744 Ok(())
1745 }
1746
1747 #[test]
1748 fn is_special() -> Result<()> {
1749 assert!(CertD::is_special("trust-root").is_ok());
1750 assert!(CertD::is_special("TRUST-ROOT").is_err());
1751
1752 assert!(CertD::is_special("_special_foo_bar").is_ok());
1753 assert!(CertD::is_special("special_foo_bar").is_err());
1754
1755 assert!(CertD::is_special("8f17777118a33dda9ba48e62aacb3243630052d9").is_err());
1756
1757 Ok(())
1758 }
1759
1760 #[test]
1761 fn fingerprints() -> Result<()> {
1762 use std::collections::HashSet;
1763
1764 let (_base, certd) = setup_testdir(&[&ALICE, &BOB, &TESTY])?;
1765
1766 let iter_fingerprint = certd.fingerprints();
1767 let fingerprints = iter_fingerprint.collect::<Result<HashSet<_>>>()?;
1768 let expected: HashSet<_> =
1769 [ALICE.fingerprint, BOB.fingerprint, TESTY.fingerprint]
1770 .iter()
1771 .map(|&s| s.to_owned())
1772 .collect();
1773 assert_eq!(expected, fingerprints);
1774
1775 Ok(())
1776 }
1777
1778 #[test]
1779 fn fingerprints_empty() -> Result<()> {
1780 use std::collections::HashSet;
1781
1782 let base = test_base();
1783 let certd = CertD::with_base_dir(&base)?;
1784
1785 let iter_fingerprint = certd.fingerprints();
1786 let fingerprints = iter_fingerprint.collect::<Result<HashSet<_>>>()?;
1787 assert!(fingerprints.is_empty());
1788
1789 Ok(())
1790 }
1791
1792 #[test]
1793 fn fingerprints_junk() -> Result<()> {
1794 use std::collections::HashSet;
1795
1796 let (base, certd) = setup_testdir(&[&ALICE, &BOB, &TESTY])?;
1797 base.child("some_file").write_str("some_text").unwrap();
1798 base.child("aa/some_file").write_str("some_text").unwrap();
1799 base.child("aa/aa/some_file")
1800 .write_str("some_text")
1801 .unwrap();
1802
1803 let iter_fingerprint = certd.fingerprints();
1804 let fingerprints = iter_fingerprint.collect::<Result<HashSet<_>>>()?;
1805 let expected: HashSet<_> =
1806 [ALICE.fingerprint, BOB.fingerprint, TESTY.fingerprint]
1807 .iter()
1808 .map(|&s| s.to_owned())
1809 .collect();
1810 assert_eq!(expected, fingerprints);
1811
1812 Ok(())
1813 }
1814
1815 #[test]
1816 fn iter() -> Result<()> {
1817 use std::collections::HashSet;
1818
1819 let (_base, certd) = setup_testdir(&[&ALICE, &BOB, &TESTY])?;
1820
1821 let mut expected: HashSet<_> = [&ALICE, &BOB, &TESTY]
1822 .iter()
1823 .map(|&s| {
1824 (
1825 s.fingerprint.to_owned(),
1826 certd.get(s.fingerprint).unwrap().unwrap().0,
1827 s.data.to_vec(),
1828 )
1829 })
1830 .collect();
1831
1832 for item in certd.iter() {
1833 let item = item?;
1834 assert!(expected.contains(&item));
1835 expected.remove(&item);
1836 }
1837 assert!(expected.is_empty());
1838
1839 Ok(())
1840 }
1841
1842 #[test]
1843 fn iter_files() -> Result<()> {
1844 use std::collections::HashSet;
1845
1846 let (_base, certd) = setup_testdir(&[&ALICE, &BOB, &TESTY])?;
1847
1848 let mut expected: HashSet<_> = [&ALICE, &BOB, &TESTY]
1849 .iter()
1850 .map(|&s| {
1851 (
1852 s.fingerprint.to_owned(),
1853 certd.get(s.fingerprint).unwrap().unwrap().0,
1854 s.data.to_vec().into_boxed_slice(),
1855 )
1856 })
1857 .collect();
1858
1859 for item in certd.iter_files() {
1860 let (fingerprint, mut fp) = item?;
1861 let tag = Tag::try_from(&fp)?;
1862
1863 let mut cert = Vec::new();
1864 fp.read_to_end(&mut cert)?;
1865
1866 let item = (fingerprint, tag, cert.into());
1867
1868 assert!(expected.contains(&item));
1869 expected.remove(&item);
1870 }
1871 assert!(expected.is_empty());
1872
1873 Ok(())
1874 }
1875
1876 #[test]
1877 fn base_path() -> Result<()> {
1878 let base = assert_fs::TempDir::new().unwrap();
1879 let certd = CertD::with_base_dir(&base)?;
1880
1881 assert_eq!(certd.base_dir(), base.path());
1882 Ok(())
1883 }
1884
1885 #[test]
1886 fn default_store_path() {
1887 assert!(CertD::default_store_path().is_ok(),
1888 "The default store's path is not defined for this platform.");
1889 }
1890
1891 #[test]
1892 fn certd_does_not_exist() -> Result<()> {
1893 let mut base = assert_fs::TempDir::new().unwrap().path().to_path_buf();
1894 base.push("asdflkj");
1895
1896 assert!(std::fs::metadata(&base).is_err());
1899
1900 let certd = CertD::with_base_dir(&base)?;
1901
1902 let fingerprints = certd.fingerprints().collect::<Result<Vec<_>>>()?;
1905 assert_eq!(fingerprints.len(), 0);
1906
1907 Ok(())
1908 }
1909
1910 #[test]
1911 fn certd_tag() -> Result<()> {
1912 let (_base, certd) = setup_testdir(&[&ALICE])?;
1913
1914 let certd_tag = || -> Tag {
1915 let tag = certd.tag();
1916
1917 let tag_readdir_std = certd.tag_readdir_std();
1918 assert_eq!(tag, tag_readdir_std);
1919
1920 #[cfg(unix)]
1921 {
1922 let tag_readdir_unix = certd.tag_readdir_unix();
1923 assert_eq!(tag, tag_readdir_unix);
1924 }
1925
1926 let tag_probe_std = certd.tag_probe_std(None);
1927 assert_eq!(tag, tag_probe_std);
1928
1929 #[cfg(unix)]
1930 {
1931 let tag_probe_unix = certd.tag_probe_unix(None);
1932 assert_eq!(tag, tag_probe_unix);
1933 }
1934
1935 tag
1936 };
1937
1938 let iter_fingerprint = certd.fingerprints();
1939 let fingerprints = iter_fingerprint.collect::<Result<Vec<_>>>()?;
1940 assert_eq!(fingerprints.len(), 1);
1941
1942 let tag0 = certd_tag();
1943 eprintln!("tag0: {:x}", tag0.0);
1944
1945 eprintln!("Inserting BOB");
1947 certd.insert_data(
1948 &BOB.data,
1949 false,
1950 |new: &[u8], old: Option<&[u8]>| {
1951 assert!(old.is_none());
1952 Ok(MergeResult::DataRef(new))
1953 })?;
1954
1955 let tag1 = certd_tag();
1956 eprintln!("tag1: {:x}", tag1.0);
1957
1958 assert_ne!(tag0, tag1);
1959
1960 eprintln!("Inserting special _bob");
1962 certd.insert_special(
1963 "_bob",
1964 BOB.data,
1965 false,
1966 |new: &[u8], old: Option<&[u8]>| {
1967 assert!(old.is_none());
1968 Ok(MergeResult::DataRef(new))
1969 })?;
1970
1971 let tag2 = certd_tag();
1972 eprintln!("tag2: {:x}", tag2.0);
1973 assert_eq!(tag1, tag2);
1974
1975 eprintln!("Inserting TESTY");
1977 certd.insert_data(
1978 TESTY.data,
1979 false,
1980 |new: &[u8], old: Option<&[u8]>| {
1981 assert!(old.is_none());
1982 Ok(MergeResult::DataRef(new))
1983 })?;
1984
1985 let tag3 = certd_tag();
1986 eprintln!("tag3: {:x}", tag3.0);
1987 assert_ne!(tag2, tag3);
1988
1989 Ok(())
1990 }
1991}