@@ -90,7 +90,7 @@ def create_inner(archive, cache, fso):
9090 raise Error (f"{ path !r} : { e } " )
9191 else :
9292 status = "+" # included
93- self .print_file_status (status , path )
93+ self .print_file_status (status , path , phase = "end" )
9494 elif args .paths_from_command or args .paths_from_shell_command or args .paths_from_stdin :
9595 paths_sep = eval_escapes (args .paths_delimiter ) if args .paths_delimiter is not None else "\n "
9696 if args .paths_from_command or args .paths_from_shell_command :
@@ -139,7 +139,7 @@ def create_inner(archive, cache, fso):
139139 status = "E"
140140 if status == "C" :
141141 self .print_warning_instance (FileChangedWarning (path ))
142- self .print_file_status (status , path )
142+ self .print_file_status (status , path , phase = "end" )
143143 if not dry_run and status is not None :
144144 fso .stats .files_stats [status ] += 1
145145 if args .paths_from_command or args .paths_from_shell_command :
@@ -167,7 +167,7 @@ def create_inner(archive, cache, fso):
167167 status = "E"
168168 else :
169169 status = "+" # included
170- self .print_file_status (status , path )
170+ self .print_file_status (status , path , phase = "end" )
171171 if not dry_run and status is not None :
172172 fso .stats .files_stats [status ] += 1
173173 continue
@@ -293,134 +293,147 @@ def _process_any(self, *, path, parent_fd, name, st, fso, cache, read_special, d
293293 """
294294 Call the right method on the given FilesystemObjectProcessor.
295295 """
296-
297296 if dry_run :
298297 return "+" # included
299- MAX_RETRIES = 10 # count includes the initial try (initial try == "retry 0")
300- for retry in range (MAX_RETRIES ):
301- last_try = retry == MAX_RETRIES - 1
302- try :
303- if stat .S_ISREG (st .st_mode ):
304- return fso .process_file (
305- path = path ,
306- parent_fd = parent_fd ,
307- name = name ,
308- st = st ,
309- cache = cache ,
310- last_try = last_try ,
311- strip_prefix = strip_prefix ,
312- )
313- elif stat .S_ISDIR (st .st_mode ):
314- return fso .process_dir (path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix )
315- elif stat .S_ISLNK (st .st_mode ):
316- if not read_special :
317- return fso .process_symlink (
318- path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
298+
299+ # Types not archived: no list start/end pair (matches prior behavior of no status line).
300+ if stat .S_ISSOCK (st .st_mode ):
301+ return
302+ elif stat .S_ISDOOR (st .st_mode ):
303+ return
304+ elif stat .S_ISPORT (st .st_mode ):
305+ return
306+
307+ m = st .st_mode
308+ if not (
309+ stat .S_ISREG (m )
310+ or stat .S_ISDIR (m )
311+ or stat .S_ISLNK (m )
312+ or stat .S_ISFIFO (m )
313+ or stat .S_ISCHR (m )
314+ or stat .S_ISBLK (m )
315+ ):
316+ self .print_warning ("Unknown file type: %s" , path )
317+ return
318+
319+ # Emit START once, before any processing, before the retry loop.
320+ self .print_file_status (None , path , phase = "start" )
321+
322+ try :
323+ MAX_RETRIES = 10 # count includes the initial try (initial try == "retry 0")
324+ for retry in range (MAX_RETRIES ):
325+ last_try = retry == MAX_RETRIES - 1
326+ try :
327+ if stat .S_ISREG (st .st_mode ):
328+ return fso .process_file (
329+ path = path ,
330+ parent_fd = parent_fd ,
331+ name = name ,
332+ st = st ,
333+ cache = cache ,
334+ last_try = last_try ,
335+ strip_prefix = strip_prefix ,
319336 )
320- else :
321- try :
322- st_target = os_stat (path = path , parent_fd = parent_fd , name = name , follow_symlinks = True )
323- except OSError :
324- special = False
337+ elif stat .S_ISDIR (st .st_mode ):
338+ return fso .process_dir (path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix )
339+ elif stat .S_ISLNK (st .st_mode ):
340+ if not read_special :
341+ return fso .process_symlink (
342+ path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
343+ )
344+ else :
345+ try :
346+ st_target = os_stat (path = path , parent_fd = parent_fd , name = name , follow_symlinks = True )
347+ except OSError :
348+ special = False
349+ else :
350+ special = is_special (st_target .st_mode )
351+ if special :
352+ return fso .process_file (
353+ path = path ,
354+ parent_fd = parent_fd ,
355+ name = name ,
356+ st = st_target ,
357+ cache = cache ,
358+ flags = flags_special_follow ,
359+ last_try = last_try ,
360+ strip_prefix = strip_prefix ,
361+ )
362+ else :
363+ return fso .process_symlink (
364+ path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
365+ )
366+ elif stat .S_ISFIFO (st .st_mode ):
367+ if not read_special :
368+ return fso .process_fifo (
369+ path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
370+ )
325371 else :
326- special = is_special (st_target .st_mode )
327- if special :
328372 return fso .process_file (
329373 path = path ,
330374 parent_fd = parent_fd ,
331375 name = name ,
332- st = st_target ,
376+ st = st ,
333377 cache = cache ,
334- flags = flags_special_follow ,
378+ flags = flags_special ,
335379 last_try = last_try ,
336380 strip_prefix = strip_prefix ,
337381 )
382+ elif stat .S_ISCHR (st .st_mode ):
383+ if not read_special :
384+ return fso .process_dev (
385+ path = path , parent_fd = parent_fd , name = name , st = st , dev_type = "c" , strip_prefix = strip_prefix
386+ )
338387 else :
339- return fso .process_symlink (
340- path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
388+ return fso .process_file (
389+ path = path ,
390+ parent_fd = parent_fd ,
391+ name = name ,
392+ st = st ,
393+ cache = cache ,
394+ flags = flags_special ,
395+ last_try = last_try ,
396+ strip_prefix = strip_prefix ,
397+ )
398+ elif stat .S_ISBLK (st .st_mode ):
399+ if not read_special :
400+ return fso .process_dev (
401+ path = path , parent_fd = parent_fd , name = name , st = st , dev_type = "b" , strip_prefix = strip_prefix
402+ )
403+ else :
404+ return fso .process_file (
405+ path = path ,
406+ parent_fd = parent_fd ,
407+ name = name ,
408+ st = st ,
409+ cache = cache ,
410+ flags = flags_special ,
411+ last_try = last_try ,
412+ strip_prefix = strip_prefix ,
341413 )
342- elif stat .S_ISFIFO (st .st_mode ):
343- if not read_special :
344- return fso .process_fifo (
345- path = path , parent_fd = parent_fd , name = name , st = st , strip_prefix = strip_prefix
346- )
347- else :
348- return fso .process_file (
349- path = path ,
350- parent_fd = parent_fd ,
351- name = name ,
352- st = st ,
353- cache = cache ,
354- flags = flags_special ,
355- last_try = last_try ,
356- strip_prefix = strip_prefix ,
357- )
358- elif stat .S_ISCHR (st .st_mode ):
359- if not read_special :
360- return fso .process_dev (
361- path = path , parent_fd = parent_fd , name = name , st = st , dev_type = "c" , strip_prefix = strip_prefix
362- )
363414 else :
364- return fso .process_file (
365- path = path ,
366- parent_fd = parent_fd ,
367- name = name ,
368- st = st ,
369- cache = cache ,
370- flags = flags_special ,
371- last_try = last_try ,
372- strip_prefix = strip_prefix ,
373- )
374- elif stat .S_ISBLK (st .st_mode ):
375- if not read_special :
376- return fso .process_dev (
377- path = path , parent_fd = parent_fd , name = name , st = st , dev_type = "b" , strip_prefix = strip_prefix
415+ self .print_warning ("Unknown file type: %s" , path )
416+ return
417+ except BackupItemExcluded :
418+ return "-"
419+ except BackupError as err :
420+ if isinstance (err , BackupOSError ):
421+ if err .errno in (errno .EPERM , errno .EACCES ):
422+ raise
423+ sleep_s = 1000.0 / 1e6 * 10 ** (retry / 2 )
424+ time .sleep (sleep_s )
425+ if retry < MAX_RETRIES - 1 :
426+ logger .warning (
427+ f"{ path } : { err } , slept { sleep_s :.3f} s, next: retry: { retry + 1 } of { MAX_RETRIES - 1 } ..."
378428 )
379429 else :
380- return fso .process_file (
381- path = path ,
382- parent_fd = parent_fd ,
383- name = name ,
384- st = st ,
385- cache = cache ,
386- flags = flags_special ,
387- last_try = last_try ,
388- strip_prefix = strip_prefix ,
389- )
390- elif stat .S_ISSOCK (st .st_mode ):
391- # Ignore unix sockets
392- return
393- elif stat .S_ISDOOR (st .st_mode ):
394- # Ignore Solaris doors
395- return
396- elif stat .S_ISPORT (st .st_mode ):
397- # Ignore Solaris event ports
398- return
399- else :
400- self .print_warning ("Unknown file type: %s" , path )
401- return
402- except BackupItemExcluded :
403- return "-"
404- except BackupError as err :
405- if isinstance (err , BackupOSError ):
406- if err .errno in (errno .EPERM , errno .EACCES ):
407- # Do not try again, such errors can not be fixed by retrying.
408430 raise
409- # sleep a bit, so temporary problems might go away...
410- sleep_s = 1000.0 / 1e6 * 10 ** (retry / 2 ) # retry 0: 1ms, retry 6: 1s, ...
411- time .sleep (sleep_s )
412- if retry < MAX_RETRIES - 1 :
413- logger .warning (
414- f"{ path } : { err } , slept { sleep_s :.3f} s, next: retry: { retry + 1 } of { MAX_RETRIES - 1 } ..."
415- )
416- else :
417- # giving up with retries, error will be dealt with (logged) by upper error handler
418- raise
419- # we better do a fresh stat on the file, just to make sure to get the current file
420- # mode right (which could have changed due to a race condition and is important for
421- # dispatching) and also to get current inode number of that file.
422- with backup_io ("stat" ):
423- st = os_stat (path = path , parent_fd = parent_fd , name = name , follow_symlinks = False )
431+ with backup_io ("stat" ):
432+ st = os_stat (path = path , parent_fd = parent_fd , name = name , follow_symlinks = False )
433+ finally :
434+ # END is always emitted here — after ALL processing including chunked I/O,
435+ # even on exception, even on retry exhaustion.
436+ self .print_file_status (None , path , phase = "end" )
424437
425438 def _rec_walk (
426439 self ,
0 commit comments