@@ -13,6 +13,7 @@ import (
1313 "time"
1414 "unsafe"
1515
16+ execCmd "github.com/netapp/trident/utils/exec"
1617 log "github.com/sirupsen/logrus"
1718
1819 "github.com/cenkalti/backoff/v4"
@@ -29,6 +30,8 @@ const (
2930 luksType = "luks2"
3031 // Return code for "no permission (bad passphrase)" from cryptsetup command
3132 luksCryptsetupBadPassphraseReturnCode = 2
33+ luksCloseDeviceSafelyClosedExitCode = 0
34+ luksCloseDeviceAlreadyClosedExitCode = 4
3235)
3336
3437// flushOneDevice flushes any outstanding I/O to a disk
@@ -175,17 +178,28 @@ func (d *LUKSDevice) Close(ctx context.Context) error {
175178}
176179
177180// closeLUKSDevice performs a luksClose on the specified LUKS device
181+ // It gracefully handles the cases where a LUKS device has already been closed or the device doesn't exist.
178182func closeLUKSDevice (ctx context.Context , luksDevicePath string ) error {
179183 output , err := command .ExecuteWithTimeoutAndInput (
180184 ctx , "cryptsetup" , luksCommandTimeout , true , "" , "luksClose" , luksDevicePath ,
181185 )
182186 if nil != err {
183- Log ().WithFields (LogFields {
184- "MappedDeviceName" : luksDevicePath ,
185- "error" : err .Error (),
186- "output" : string (output ),
187- }).Debug ("Failed to Close LUKS device" )
188- return fmt .Errorf ("failed to Close LUKS device %s; %v" , luksDevicePath , err )
187+ fields := LogFields {"luksDevicePath" : luksDevicePath , "output" : string (output ), "err" : err .Error ()}
188+ var exitErr execCmd.ExitError
189+ if ! errors .As (err , & exitErr ) {
190+ Logc (ctx ).WithFields (fields ).Error ("Failed to close LUKS device with unknown error." )
191+ return fmt .Errorf ("failed to close LUKS device %s; %w" , luksDevicePath , err )
192+ }
193+
194+ switch exitErr .ExitCode () {
195+ // exit code "0" and "4" are safe to ignore. "0" will likely never be hit but check for it regardless.
196+ case luksCloseDeviceSafelyClosedExitCode , luksCloseDeviceAlreadyClosedExitCode :
197+ Logc (ctx ).WithFields (fields ).Debug ("LUKS device is already closed or did not exist." )
198+ return nil
199+ default :
200+ Logc (ctx ).WithFields (fields ).Error ("Failed to close LUKS device." )
201+ return fmt .Errorf ("exit code '%d' when closing LUKS device '%s'; %w" , exitErr .ExitCode (), luksDevicePath , err )
202+ }
189203 }
190204 return nil
191205}
@@ -209,21 +223,18 @@ func IsLUKSDeviceOpen(ctx context.Context, luksDevicePath string) (bool, error)
209223// EnsureLUKSDeviceClosed ensures there is not an open LUKS device at the specified path
210224func EnsureLUKSDeviceClosed (ctx context.Context , luksDevicePath string ) error {
211225 GenerateRequestContextForLayer (ctx , LogLayerUtils )
226+ fields := LogFields {"luksDevicePath" : luksDevicePath }
212227
213- _ , err := osFs .Stat (luksDevicePath )
214- if err == nil {
215- // Need to Close the LUKS device
216- return closeLUKSDevice (ctx , luksDevicePath )
217- } else if os .IsNotExist (err ) {
218- Logc (ctx ).WithFields (LogFields {
219- "device" : luksDevicePath ,
220- }).Debug ("LUKS device not found." )
221- } else {
222- Logc (ctx ).WithFields (LogFields {
223- "device" : luksDevicePath ,
224- "error" : err .Error (),
225- }).Debug ("Failed to stat device" )
226- return fmt .Errorf ("could not stat device: %s %v." , luksDevicePath , err )
228+ if err := closeLUKSDevice (ctx , luksDevicePath ); err != nil {
229+ Logc (ctx ).WithFields (fields ).WithError (err ).Error ("Could not close LUKS device." )
230+ return fmt .Errorf ("could not close LUKS device %s; %w" , luksDevicePath , err )
231+ }
232+
233+ // If LUKS close succeeded, the block device node should be gone.
234+ // It's the responsibility of the kernel and udev to manage /dev/mapper entries.
235+ // If the /dev/mapper entry lives on, log a warning and return success.
236+ if _ , err := osFs .Stat (luksDevicePath ); err != nil {
237+ Logc (ctx ).WithFields (fields ).Warn ("Stale device mapper file found for LUKS device. Is udev is running?" )
227238 }
228239 return nil
229240}
0 commit comments