Skip to content

Commit 6078a2a

Browse files
jwebster7Elliot Li
authored andcommitted
Consider nonexistent device errors when closing a LUKS device
Change-Id: I4335da745be563f24232a9088ca4f2c8b4f9379b
1 parent e96d66d commit 6078a2a

2 files changed

Lines changed: 41 additions & 21 deletions

File tree

utils/devices_linux.go

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
178182
func 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
210224
func 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
}

utils/exec/command.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,18 @@ import (
1919
var (
2020
xtermControlRegex = regexp.MustCompile(`\x1B\[[0-9;]*[a-zA-Z]`)
2121

22-
_ Command = NewCommand()
22+
// Ensure these structures always implement these interfaces at compilation.
23+
_ Command = NewCommand()
24+
_ ExitError = &exec.ExitError{}
2325
)
2426

27+
// ExitError defines the methods that exec.ExitError implements.
28+
// This enables unit testing and mocking of exit codes.
29+
type ExitError interface {
30+
error
31+
ExitCode() int
32+
}
33+
2534
// Command defines a set of behaviors for executing commands on a host.
2635
type Command interface {
2736
Execute(ctx context.Context, name string, args ...string) ([]byte, error)

0 commit comments

Comments
 (0)