@@ -4,18 +4,20 @@ use std::{
44 path,
55} ;
66
7- use getset :: Setters ;
7+ use fslock :: LockFile ;
88use zip:: { ZipArchive , ZipWriter } ;
99
1010/// A convenient type which can read and write to a ZIP file and cleanly switch between the two modes.
1111///
1212/// Whilst writing, you can also read the previous file, as it writes to a new temporary file, until
1313/// [`ZipReaderWriter::conclude_write`] is called.
14- #[ derive( Default , Setters ) ]
14+ #[ derive( Default ) ]
1515pub ( crate ) struct ZipReaderWriter {
1616 /// The path of this zip file, if set
17- #[ getset( set = "pub" ) ]
18- file : Option < path:: PathBuf > ,
17+ path : Option < path:: PathBuf > ,
18+ /// The locking file for this evidence package. If this is [`Some`],
19+ /// you can be assured that the lock is obtained.
20+ lock_file : Option < LockFile > ,
1921 /// The reader, if in write mode, of this reader/writer
2022 reader : Option < ZipArchive < BufReader < fs:: File > > > ,
2123 /// The writer, if in write mode, of this reader/writer
@@ -25,7 +27,7 @@ pub(crate) struct ZipReaderWriter {
2527impl Clone for ZipReaderWriter {
2628 fn clone ( & self ) -> Self {
2729 Self {
28- file : self . file . clone ( ) ,
30+ path : self . path . clone ( ) ,
2931 ..Default :: default ( )
3032 }
3133 }
@@ -41,19 +43,46 @@ impl fmt::Debug for ZipReaderWriter {
4143 "idle"
4244 } ;
4345 f. debug_struct ( "ZipReadWriter" )
44- . field ( "file" , & self . file )
46+ . field ( "file" , & self . path )
4547 . field ( "current_mode" , & mode)
46- . finish ( )
48+ . finish_non_exhaustive ( )
4749 }
4850}
4951
5052impl ZipReaderWriter {
5153 /// Create a new [`ZipReaderWriter`] instance.
52- pub fn new ( path : path:: PathBuf ) -> Self {
53- Self {
54- file : Some ( path) ,
54+ pub fn new ( path : path:: PathBuf ) -> crate :: Result < Self > {
55+ let mut o = Self {
56+ path : Some ( path) ,
5557 ..Default :: default ( )
58+ } ;
59+ o. update_lock_file ( ) ?;
60+ Ok ( o)
61+ }
62+
63+ /// Update the locking file for this [`ZipReaderWriter`]. This will
64+ /// either obtain it (if a path is set), drop it (if a path isn't
65+ /// set), or will return a [`crate::Error::Locking`] error.
66+ fn update_lock_file ( & mut self ) -> crate :: Result < ( ) > {
67+ if let Some ( path) = & self . path {
68+ let mut lock_path = path. clone ( ) ;
69+ // SAFETY: only a file can be specified here
70+ lock_path. set_file_name ( format ! (
71+ ".~{}" ,
72+ lock_path. file_name( ) . unwrap( ) . to_str( ) . unwrap( )
73+ ) ) ;
74+ let mut lock_file = LockFile :: open ( & lock_path) . map_err ( crate :: Error :: Locking ) ?;
75+ if !lock_file
76+ . try_lock_with_pid ( )
77+ . map_err ( crate :: Error :: Locking ) ?
78+ {
79+ return Err ( crate :: Error :: LockNotObtained ) ;
80+ }
81+ self . lock_file = Some ( lock_file) ;
82+ } else {
83+ self . lock_file = None ;
5684 }
85+ Ok ( ( ) )
5786 }
5887
5988 /// Get this [`ZipReaderWriter`] instance in read mode.
@@ -66,7 +95,7 @@ impl ZipReaderWriter {
6695 // Open reader
6796 tracing:: debug!( "Opening reader" ) ;
6897 self . reader = Some ( ZipArchive :: new ( BufReader :: new ( fs:: File :: open (
69- self . file
98+ self . path
7099 . as_ref ( )
71100 . expect ( "zipreadwriter must not be called upon until file is set." ) ,
72101 ) ?) ) ?) ;
@@ -82,11 +111,14 @@ impl ZipReaderWriter {
82111 Option < & mut ZipArchive < BufReader < fs:: File > > > ,
83112 & mut ZipWriter < BufWriter < fs:: File > > ,
84113 ) > {
114+ if self . lock_file . is_none ( ) {
115+ return Err ( crate :: Error :: LockNotObtained ) ;
116+ }
85117 if self . writer . is_none ( ) {
86118 tracing:: debug!( "Opening writer" ) ;
87119 // Open writer
88120 self . writer = Some ( ZipWriter :: new ( BufWriter :: new ( fs:: File :: create (
89- self . file
121+ self . path
90122 . as_ref ( )
91123 . map ( |p| {
92124 let mut p = p. clone ( ) ;
@@ -104,6 +136,9 @@ impl ZipReaderWriter {
104136
105137 /// Conclude writing to the ZIP file and reset for reading or writing again.
106138 pub fn conclude_write ( & mut self ) -> crate :: Result < ( ) > {
139+ if self . lock_file . is_none ( ) {
140+ return Err ( crate :: Error :: LockNotObtained ) ;
141+ }
107142 if self . writer . is_some ( ) {
108143 // Close write
109144 tracing:: debug!( "Closing writer" ) ;
@@ -116,7 +151,7 @@ impl ZipReaderWriter {
116151 // Move temp file
117152 tracing:: debug!( "Moving temp file to overwrite package" ) ;
118153 let tmp_path = self
119- . file
154+ . path
120155 . as_ref ( )
121156 . map ( |p| {
122157 let mut p = p. clone ( ) ;
@@ -126,7 +161,7 @@ impl ZipReaderWriter {
126161 . expect ( "zipreadwriter must not be called upon until file is set." ) ;
127162 fs:: rename (
128163 tmp_path,
129- self . file
164+ self . path
130165 . as_ref ( )
131166 . expect ( "zipreadwriter must not be called upon until file is set." ) ,
132167 ) ?;
@@ -136,6 +171,9 @@ impl ZipReaderWriter {
136171
137172 /// Interrupt a write early, concluding the write and removing the temporary file.
138173 pub fn interrupt_write ( & mut self ) -> crate :: Result < ( ) > {
174+ if self . lock_file . is_none ( ) {
175+ return Err ( crate :: Error :: LockNotObtained ) ;
176+ }
139177 if self . writer . is_some ( ) {
140178 // Close write
141179 tracing:: debug!( "Closing writer" ) ;
@@ -148,7 +186,7 @@ impl ZipReaderWriter {
148186 // Delete temp file
149187 tracing:: debug!( "Moving temp file to overwrite package" ) ;
150188 let tmp_path = self
151- . file
189+ . path
152190 . as_ref ( )
153191 . map ( |p| {
154192 let mut p = p. clone ( ) ;
0 commit comments