@@ -150,6 +150,64 @@ def pxeconfig_file(mac)
150150 end
151151 end
152152
153+ def self . fetch_system_image ( image_dst , url , files , tftp_path )
154+ # Build paths, verify parameter do not contain ".." (switch folder), and check existing files
155+ image_root = Pathname . new ( Proxy ::TFTP ::Plugin . settings . system_image_root ) . cleanpath
156+ image_path = Pathname . new ( File . expand_path ( image_dst , image_root ) ) . cleanpath
157+ tftproot = Pathname . new ( Proxy ::TFTP ::Plugin . settings . tftproot ) . cleanpath
158+ raise_error_on_prohibited_path ( image_root , image_path , image_dst )
159+ file_exists = File . exist? image_path
160+ extr_file_map = { }
161+ files . each do |file |
162+ extr_filename = boot_filename ( tftp_path , file )
163+ extr_file_path = Pathname . new ( File . expand_path ( extr_filename , tftproot ) ) . cleanpath
164+ raise_error_on_prohibited_path ( tftproot , extr_file_path , file )
165+ file_exists = false unless File . exist? extr_file_path
166+ extr_file_map [ file ] = extr_file_path
167+ end
168+
169+ if file_exists
170+ 200 # Return 200 if all files exist already
171+ else
172+ fetch_system_image_worker ( url , image_path , extr_file_map )
173+ 202 # Return 202 if download process was triggered
174+ end
175+ end
176+
177+ def self . fetch_system_image_worker ( url , image_path , extr_file_map )
178+ lock_file = ".#{ File . basename ( image_path . sub_ext ( '' ) ) } .lock"
179+ # Lock
180+ image_path . parent . mkpath
181+ lock = Proxy ::FileLock . try_locking ( File . join ( File . dirname ( image_path ) , lock_file ) )
182+ if lock . nil?
183+ raise IOError . new , "System image download and extraction is still in progress"
184+ end
185+
186+ Thread . new ( lock , url , image_path , extr_file_map ) do |t_lock , t_url , t_image_path , t_extr_file_map |
187+ # Wait for download completion
188+ download_task = choose_protocol_and_fetch ( t_url , t_image_path )
189+ if download_task . is_a? ( FalseClass )
190+ logger . error "TFTP image download error: Is another process downloading it already?"
191+ Thread . stop
192+ end
193+ unless download_task . join == 0
194+ logger . error "TFTP image download error: Task did not complete"
195+ Thread . stop
196+ end
197+
198+ t_extr_file_map . each do |file_in_image , extr_file |
199+ # Create destination directory and extract file from iso
200+ extr_file . parent . mkpath
201+ extract_task = ::Proxy ::ArchiveExtract . new ( t_image_path , file_in_image , extr_file ) . start
202+ logger . error "TFTP image file extraction error: #{ file_in_image } => #{ extr_file } " unless extract_task . join == 0
203+ end
204+ ensure
205+ # Unlock
206+ Proxy ::FileLock . unlock ( t_lock )
207+ File . unlink ( t_lock )
208+ end
209+ end
210+
153211 def self . fetch_boot_file ( dst , src )
154212 filename = boot_filename ( dst , src )
155213 destination = Pathname . new ( File . expand_path ( filename , Proxy ::TFTP ::Plugin . settings . tftproot ) ) . cleanpath
@@ -180,4 +238,10 @@ def self.boot_filename(dst, src)
180238 # Do not append a '-' if the dst is a directory path
181239 dst . end_with? ( '/' ) ? dst + src . split ( "/" ) [ -1 ] : dst + '-' + src . split ( "/" ) [ -1 ]
182240 end
241+
242+ def self . raise_error_on_prohibited_path ( base_path , relative_path , error_parameter )
243+ if relative_path . expand_path . relative_path_from ( base_path ) . to_s . start_with? ( '..' )
244+ raise "File to extract from image contains up-directory: #{ error_parameter } "
245+ end
246+ end
183247end
0 commit comments