5858import logging
5959
6060# External dependencies.
61- from executor import quote
61+ from executor import ExternalCommandFailed , quote
6262
6363# Modules included in our package.
6464from linux_utils import coerce_context , coerce_size
@@ -148,11 +148,10 @@ def unlock_filesystem(device_file, target, key_file=None, options=None, context=
148148 :param target: The mapped device name (a string).
149149 :param key_file: The pathname of the key file used to encrypt the
150150 filesystem (a string or :data:`None`).
151- :param options: An iterable of strings with encryption options
152- or :data:`None` (in which case the default
153- encryption options are used). Currently 'discard' and
154- 'readonly' are the only supported options (other options
155- are silently ignored).
151+ :param options: An iterable of strings with encryption options or
152+ :data:`None` (in which case the default options are used).
153+ Currently 'discard', 'readonly' and 'tries' are the only
154+ supported options (other options are silently ignored).
156155 :param context: An execution context created by :mod:`executor.contexts`
157156 (coerced using :func:`.coerce_context()`).
158157 :raises: :exc:`~executor.ExternalCommandFailed` when the command fails.
@@ -161,16 +160,33 @@ def unlock_filesystem(device_file, target, key_file=None, options=None, context=
161160 """
162161 context = coerce_context (context )
163162 logger .debug ("Unlocking filesystem %s .." , device_file )
163+ tries = 3
164164 open_command = ['cryptsetup' ]
165- if options :
166- if 'discard' in options :
167- open_command .append ('--allow-discards' )
168- if 'readonly' in options :
169- open_command .append ('--readonly' )
165+ open_options = []
170166 if key_file :
171- open_command .append ('--key-file=%s' % key_file )
167+ open_options .append ('--key-file=%s' % key_file )
168+ if options :
169+ for opt in options :
170+ if opt == 'discard' :
171+ open_options .append ('--allow-discards' )
172+ elif opt == 'readonly' :
173+ open_options .append ('--readonly' )
174+ elif opt .startswith ('tries=' ):
175+ name , _ , value = opt .partition ('=' )
176+ tries = int (value )
177+ open_command .extend (sorted (open_options ))
172178 open_command .extend (['luksOpen' , device_file , target ])
173- context .execute (* open_command , sudo = True , tty = (key_file is None ))
179+ for attempt in range (1 , tries + 1 ):
180+ try :
181+ context .execute (* open_command , sudo = True , tty = (key_file is None ))
182+ except ExternalCommandFailed :
183+ if attempt < tries and not key_file :
184+ logger .warning ("Failed to unlock, retrying .." )
185+ continue
186+ else :
187+ raise
188+ else :
189+ break
174190
175191
176192def lock_filesystem (target , context = None ):
0 commit comments