@@ -23,8 +23,10 @@ import (
2323)
2424
2525const (
26- luksCloseTimeout = 30 * time .Second
27- luksCloseMaxWaitDuration = 2 * time .Minute
26+ luksCloseTimeout = 30 * time .Second
27+ luksCloseMaxWaitDuration = 2 * time .Minute
28+ luksCloseDeviceSafelyClosedExitCode = 0
29+ luksCloseDeviceAlreadyClosedExitCode = 4
2830)
2931
3032var (
@@ -47,12 +49,11 @@ func (c *Client) FlushOneDevice(ctx context.Context, devicePath string) error {
4749 ctx , "blockdev" , deviceOperationsTimeout , true , "--flushbufs" , devicePath ,
4850 )
4951 if err != nil {
50- Logc (ctx ).WithFields (
51- LogFields {
52- "error" : err ,
53- "output" : string (out ),
54- "device" : devicePath ,
55- }).Debug ("blockdev --flushbufs failed." )
52+ Logc (ctx ).WithFields (LogFields {
53+ "error" : err ,
54+ "output" : string (out ),
55+ "device" : devicePath ,
56+ }).Debug ("blockdev --flushbufs failed." )
5657 return fmt .Errorf ("flush device failed for %s : %s" , devicePath , err )
5758 }
5859
@@ -112,6 +113,7 @@ func (c *Client) VerifyMultipathDeviceSize(
112113}
113114
114115// CloseLUKSDevice performs a luksClose on the device at the specified path (example: "/dev/mapper/<luks>").
116+ // It gracefully handles the cases where a LUKS device has already been closed or the device doesn't exist.
115117func (c * Client ) CloseLUKSDevice (ctx context.Context , devicePath string ) error {
116118 if err := beforeLuksClose .Inject (); err != nil {
117119 return err
@@ -126,9 +128,22 @@ func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error {
126128 }
127129
128130 if err != nil {
129- fields := LogFields {"luksDevicePath" : devicePath , "output" : string (output )}
130- Logc (ctx ).WithFields (fields ).WithError (err ).Debug ("Failed to close LUKS device" )
131- return fmt .Errorf ("failed to close LUKS device %s; %w" , devicePath , err )
131+ fields := LogFields {"luksDevicePath" : devicePath , "output" : string (output ), "err" : err .Error ()}
132+ var exitErr execCmd.ExitError
133+ if ! errors .As (err , & exitErr ) {
134+ Logc (ctx ).WithFields (fields ).Error ("Failed to close LUKS device with unknown error." )
135+ return fmt .Errorf ("failed to close LUKS device %s; %w" , devicePath , err )
136+ }
137+
138+ switch exitErr .ExitCode () {
139+ // exit code "0" and "4" are safe to ignore. "0" will likely never be hit but check for it regardless.
140+ case luksCloseDeviceSafelyClosedExitCode , luksCloseDeviceAlreadyClosedExitCode :
141+ Logc (ctx ).WithFields (fields ).Debug ("LUKS device is already closed or did not exist." )
142+ return nil
143+ default :
144+ Logc (ctx ).WithFields (fields ).Error ("Failed to close LUKS device." )
145+ return fmt .Errorf ("exit code '%d' when closing LUKS device '%s'; %w" , exitErr .ExitCode (), devicePath , err )
146+ }
132147 }
133148
134149 Logc (ctx ).WithField ("luksDevicePath" , devicePath ).Debug ("Closed LUKS device." )
@@ -138,19 +153,19 @@ func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error {
138153// EnsureLUKSDeviceClosed ensures there is no open LUKS device at the specified path (example: "/dev/mapper/<luks>").
139154func (c * Client ) EnsureLUKSDeviceClosed (ctx context.Context , devicePath string ) error {
140155 GenerateRequestContextForLayer (ctx , LogLayerUtils )
141- _ , err := c .osFs .Stat (devicePath )
142- if err == nil {
143- return c .CloseLUKSDevice (ctx , devicePath )
144- } else if ! os .IsNotExist (err ) {
145- Logc (ctx ).WithFields (LogFields {
146- "device" : devicePath ,
147- "error" : err .Error (),
148- }).Debug ("Failed to stat device." )
149- return fmt .Errorf ("could not stat device: %s; %v" , devicePath , err )
156+ fields := LogFields {"luksDevicePath" : devicePath }
157+
158+ if err := c .CloseLUKSDevice (ctx , devicePath ); err != nil {
159+ Logc (ctx ).WithFields (fields ).WithError (err ).Error ("Could not close LUKS device." )
160+ return fmt .Errorf ("could not close LUKS device %s; %w" , devicePath , err )
161+ }
162+
163+ // If LUKS close succeeded, the block device node should be gone.
164+ // It's the responsibility of the kernel and udev to manage /dev/mapper entries.
165+ // If the /dev/mapper entry lives on, log a warning and return success.
166+ if _ , err := c .osFs .Stat (devicePath ); err == nil {
167+ Logc (ctx ).WithFields (fields ).Warn ("Stale device mapper file found for LUKS device. Is udev is running?" )
150168 }
151- Logc (ctx ).WithFields (LogFields {
152- "device" : devicePath ,
153- }).Debug ("LUKS device not found." )
154169
155170 return nil
156171}
@@ -163,12 +178,11 @@ func (c *Client) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luk
163178 return durationErr
164179 }
165180 if elapsed > luksCloseMaxWaitDuration {
166- Logc (ctx ).WithFields (
167- LogFields {
168- "device" : luksDevicePath ,
169- "elapsed" : elapsed ,
170- "maxWait" : luksDevicePath ,
171- }).Debug ("LUKS close max wait time expired, continuing with removal." )
181+ Logc (ctx ).WithFields (LogFields {
182+ "device" : luksDevicePath ,
183+ "elapsed" : elapsed ,
184+ "maxWait" : luksDevicePath ,
185+ }).Debug ("LUKS close max wait time expired, continuing with removal." )
172186 return errors .MaxWaitExceededError (fmt .Sprintf ("LUKS close wait time expired. Elapsed: %v" , elapsed ))
173187 }
174188 return err
@@ -184,7 +198,7 @@ func (c *Client) GetDeviceFSType(ctx context.Context, device string) (string, er
184198 // blkid return status=2 both in case of an unformatted filesystem as well as for the case when it is
185199 // unable to get the filesystem (e.g. IO error), therefore ensure device is available before calling blkid
186200 if err := c .WaitForDevice (ctx , device ); err != nil {
187- return "" , fmt .Errorf ("could not find device before checking for the filesystem %v; %s. " , device , err )
201+ return "" , fmt .Errorf ("could not find device before checking for the filesystem %v; %s" , device , err )
188202 }
189203
190204 out , err := c .command .ExecuteWithTimeout (ctx , "blkid" , 5 * time .Second , true , device )
0 commit comments