mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-05 16:00:05 +01:00
Add backend support for new device-mapper kernel options.
This patch adds support for using keyring for volume key and support for new integrity fields for dm-crypt. Also helpers for searching disk by id. To be used later.
This commit is contained in:
@@ -35,6 +35,8 @@
|
||||
#include "internal.h"
|
||||
|
||||
#define DM_UUID_LEN 129
|
||||
#define DM_BY_ID_PREFIX "dm-uuid-"
|
||||
#define DM_BY_ID_PREFIX_LEN 8
|
||||
#define DM_UUID_PREFIX "CRYPT-"
|
||||
#define DM_UUID_PREFIX_LEN 6
|
||||
#define DM_CRYPT_TARGET "crypt"
|
||||
@@ -382,36 +384,179 @@ static size_t get_key_size_strlen(size_t key_size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CLEN 64 /* 2*MAX_CIPHER_LEN */
|
||||
#define CLENS "63" /* for sscanf length + '\0' */
|
||||
#define CAPIL 144 /* should be enough to fit whole capi string */
|
||||
#define CAPIS "143" /* for sscanf of crypto API string + 16 + \0 */
|
||||
|
||||
static int cipher_c2dm(const char *org_c, const char *org_i, unsigned tag_size,
|
||||
char *c_dm, int c_dm_size,
|
||||
char *i_dm, int i_dm_size)
|
||||
{
|
||||
int c_size = 0, i_size = 0, i;
|
||||
char cipher[CLEN], mode[CLEN], iv[CLEN], tmp[CLEN];
|
||||
char capi[CAPIL];
|
||||
|
||||
if (!c_dm || !c_dm_size || !i_dm || !i_dm_size)
|
||||
return -EINVAL;
|
||||
|
||||
i = sscanf(org_c, "%" CLENS "[^-]-%" CLENS "s", cipher, tmp);
|
||||
if (i != 2)
|
||||
return -EINVAL;
|
||||
|
||||
i = sscanf(tmp, "%" CLENS "[^-]-%" CLENS "s", mode, iv);
|
||||
if (i == 1) {
|
||||
strncpy(iv, mode, CLEN);
|
||||
*mode = '\0';
|
||||
if (snprintf(capi, sizeof(capi), "%s", cipher) < 0)
|
||||
return -EINVAL;
|
||||
} else if (i == 2) {
|
||||
if (snprintf(capi, sizeof(capi), "%s(%s)", mode, cipher) < 0)
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if (!org_i) {
|
||||
/* legacy mode: CIPHER-MODE-IV*/
|
||||
i_size = snprintf(i_dm, i_dm_size, "%s", "");
|
||||
c_size = snprintf(c_dm, c_dm_size, "%s", org_c);
|
||||
} else if (!strcmp(org_i, "none")) {
|
||||
/* IV only: capi:MODE(CIPHER)-IV */
|
||||
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:none", tag_size);
|
||||
c_size = snprintf(c_dm, c_dm_size, "capi:%s-%s", capi, iv);
|
||||
} else if (!strcmp(org_i, "aead") && !strcmp(mode, "ccm")) {
|
||||
/* CCM AEAD: capi:rfc4309(MODE(CIPHER))-IV */
|
||||
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
|
||||
c_size = snprintf(c_dm, c_dm_size, "capi:rfc4309(%s)-%s", capi, iv);
|
||||
} else if (!strcmp(org_i, "aead")) {
|
||||
/* AEAD: capi:MODE(CIPHER))-IV */
|
||||
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
|
||||
c_size = snprintf(c_dm, c_dm_size, "capi:%s-%s", capi, iv);
|
||||
} else if (!strcmp(org_i, "poly1305")) {
|
||||
/* POLY1305 AEAD: capi:rfc7539(MODE(CIPHER),POLY1305)-IV */
|
||||
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
|
||||
c_size = snprintf(c_dm, c_dm_size, "capi:rfc7539(%s,poly1305)-%s", capi, iv);
|
||||
} else {
|
||||
/* other AEAD: capi:authenc(<AUTH>,MODE(CIPHER))-IV */
|
||||
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
|
||||
c_size = snprintf(c_dm, c_dm_size, "capi:authenc(%s,%s)-%s", org_i, capi, iv);
|
||||
}
|
||||
|
||||
if (c_size < 0 || c_size == c_dm_size)
|
||||
return -EINVAL;
|
||||
if (i_size < 0 || i_size == i_dm_size)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cipher_dm2c(char **org_c, char **org_i, const char *c_dm, const char *i_dm)
|
||||
{
|
||||
char cipher[CLEN], mode[CLEN], iv[CLEN], auth[CLEN];
|
||||
char tmp[CAPIL], capi[CAPIL];
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
if (!c_dm)
|
||||
return -EINVAL;
|
||||
|
||||
/* legacy mode */
|
||||
if (strncmp(c_dm, "capi:", 4)) {
|
||||
if (!(*org_c = strdup(c_dm)))
|
||||
return -ENOMEM;
|
||||
*org_i = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* modes with capi: prefix */
|
||||
i = sscanf(c_dm, "capi:%" CAPIS "[^-]-%" CLENS "s", tmp, iv);
|
||||
if (i != 2)
|
||||
return -EINVAL;
|
||||
|
||||
len = strlen(tmp);
|
||||
if (len < 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (tmp[len-1] == ')')
|
||||
tmp[len-1] = '\0';
|
||||
|
||||
if (sscanf(tmp, "rfc4309(%" CAPIS "s", capi) == 1) {
|
||||
if (!(*org_i = strdup("aead")))
|
||||
return -ENOMEM;
|
||||
} else if (sscanf(tmp, "rfc7539(%" CAPIS "[^,],%" CLENS "s", capi, auth) == 2) {
|
||||
if (!(*org_i = strdup(auth)))
|
||||
return -ENOMEM;
|
||||
} else if (sscanf(tmp, "authenc(%" CLENS "[^,],%" CAPIS "s", auth, capi) == 2) {
|
||||
if (!(*org_i = strdup(auth)))
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
if (i_dm) {
|
||||
if (!(*org_i = strdup(i_dm)))
|
||||
return -ENOMEM;
|
||||
} else
|
||||
*org_i = NULL;
|
||||
strncpy(capi, tmp, sizeof(capi));
|
||||
}
|
||||
|
||||
i = sscanf(capi, "%" CLENS "[^(](%" CLENS "[^)])", mode, cipher);
|
||||
if (i == 2)
|
||||
snprintf(tmp, sizeof(tmp), "%s-%s-%s", cipher, mode, iv);
|
||||
else
|
||||
snprintf(tmp, sizeof(tmp), "%s-%s", capi, iv);
|
||||
|
||||
if (!(*org_c = strdup(tmp))) {
|
||||
free(*org_i);
|
||||
*org_i = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt */
|
||||
static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t flags)
|
||||
{
|
||||
int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0;
|
||||
char *params, *hexkey;
|
||||
char features[256];
|
||||
char sector_feature[32], features[256], integrity_dm[256], cipher_dm[256];
|
||||
|
||||
if (!dmd)
|
||||
return NULL;
|
||||
|
||||
r = cipher_c2dm(dmd->u.crypt.cipher, dmd->u.crypt.integrity, dmd->u.crypt.tag_size,
|
||||
cipher_dm, sizeof(cipher_dm), integrity_dm, sizeof(integrity_dm));
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
if (flags & CRYPT_ACTIVATE_ALLOW_DISCARDS)
|
||||
num_options++;
|
||||
if (flags & CRYPT_ACTIVATE_SAME_CPU_CRYPT)
|
||||
num_options++;
|
||||
if (flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)
|
||||
num_options++;
|
||||
if (dmd->u.crypt.integrity)
|
||||
num_options++;
|
||||
|
||||
if (num_options)
|
||||
snprintf(features, sizeof(features)-1, " %d%s%s%s", num_options,
|
||||
if (dmd->u.crypt.sector_size != SECTOR_SIZE) {
|
||||
num_options++;
|
||||
snprintf(sector_feature, sizeof(sector_feature), " sector_size:%u", dmd->u.crypt.sector_size);
|
||||
} else
|
||||
*sector_feature = '\0';
|
||||
|
||||
if (num_options) {
|
||||
snprintf(features, sizeof(features)-1, " %d%s%s%s%s%s", num_options,
|
||||
(flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) ? " allow_discards" : "",
|
||||
(flags & CRYPT_ACTIVATE_SAME_CPU_CRYPT) ? " same_cpu_crypt" : "",
|
||||
(flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) ? " submit_from_crypt_cpus" : "");
|
||||
else
|
||||
(flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) ? " submit_from_crypt_cpus" : "",
|
||||
sector_feature, integrity_dm);
|
||||
} else
|
||||
*features = '\0';
|
||||
|
||||
if (!strncmp(dmd->u.crypt.cipher, "cipher_null-", 12))
|
||||
if (!strncmp(cipher_dm, "cipher_null-", 12))
|
||||
null_cipher = 1;
|
||||
|
||||
if (dmd->u.crypt.key_in_keyring) {
|
||||
keystr_len = strlen(dmd->u.crypt.key_description) + get_key_size_strlen(dmd->u.crypt.vk->keylength) + 9;
|
||||
if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
|
||||
keystr_len = strlen(dmd->u.crypt.vk->key_description) + get_key_size_strlen(dmd->u.crypt.vk->keylength) + 9;
|
||||
hexkey = crypt_safe_alloc(keystr_len);
|
||||
} else
|
||||
hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1));
|
||||
@@ -421,8 +566,8 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
|
||||
|
||||
if (null_cipher)
|
||||
strncpy(hexkey, "-", 2);
|
||||
else if (dmd->u.crypt.key_in_keyring) {
|
||||
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.key_description);
|
||||
else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
|
||||
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key_description);
|
||||
if (r < 0 || r >= keystr_len) {
|
||||
params = NULL;
|
||||
goto out;
|
||||
@@ -430,7 +575,7 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
|
||||
} else
|
||||
hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key);
|
||||
|
||||
max_size = strlen(hexkey) + strlen(dmd->u.crypt.cipher) +
|
||||
max_size = strlen(hexkey) + strlen(cipher_dm) +
|
||||
strlen(device_block_path(dmd->data_device)) +
|
||||
strlen(features) + 64;
|
||||
params = crypt_safe_alloc(max_size);
|
||||
@@ -438,7 +583,7 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
|
||||
goto out;
|
||||
|
||||
r = snprintf(params, max_size, "%s %s %" PRIu64 " %s %" PRIu64 "%s",
|
||||
dmd->u.crypt.cipher, hexkey, dmd->u.crypt.iv_offset,
|
||||
cipher_dm, hexkey, dmd->u.crypt.iv_offset,
|
||||
device_block_path(dmd->data_device), dmd->u.crypt.offset,
|
||||
features);
|
||||
if (r < 0 || r >= max_size) {
|
||||
@@ -852,6 +997,31 @@ static int dm_prepare_uuid(const char *name, const char *type, const char *uuid,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lookup_dm_dev_by_uuid(const char *uuid, const char *type)
|
||||
{
|
||||
int r;
|
||||
char *c;
|
||||
char dev_uuid[DM_UUID_LEN + DM_BY_ID_PREFIX_LEN] = DM_BY_ID_PREFIX;
|
||||
|
||||
if (!dm_prepare_uuid("", type, uuid, dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN))
|
||||
return -EINVAL;
|
||||
|
||||
c = strrchr(dev_uuid, '-');
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
|
||||
/* cut of dm name */
|
||||
*c = '\0';
|
||||
|
||||
r = lookup_by_disk_id(dev_uuid);
|
||||
if (r == -ENOENT) {
|
||||
log_dbg("Search by disk id not available. Using sysfs instead.");
|
||||
r = lookup_by_sysfs_uuid_field(dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _dm_create_device(const char *name, const char *type,
|
||||
struct device *device, uint32_t flags,
|
||||
const char *uuid, uint64_t size,
|
||||
@@ -980,6 +1150,14 @@ static int check_retry(uint32_t *dmd_flags, uint32_t dmt_flags)
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
/* If kernel keyring is not supported load key directly in dm-crypt */
|
||||
if ((*dmd_flags & CRYPT_ACTIVATE_KEYRING_KEY) &&
|
||||
!(dmt_flags & DM_KERNEL_KEYRING_SUPPORTED)) {
|
||||
log_dbg("dm-crypt doesn't suport kernel keyring");
|
||||
*dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_KEYRING_KEY;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1037,6 +1215,13 @@ int dm_create_device(struct crypt_device *cd, const char *name,
|
||||
if (r == -EINVAL && dmd->target == DM_VERITY && dmd->u.verity.fec_device &&
|
||||
!(dmt_flags & DM_VERITY_FEC_SUPPORTED))
|
||||
log_err(cd, _("Requested dm-verity FEC options are not supported.\n"));
|
||||
|
||||
if (r == -EINVAL && dmd->target == DM_CRYPT) {
|
||||
if (dmd->u.crypt.integrity && !(dmt_flags & DM_INTEGRITY_SUPPORTED))
|
||||
log_err(cd, _("Requested data integrity options are not supported.\n"));
|
||||
if (dmd->u.crypt.sector_size != SECTOR_SIZE && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED))
|
||||
log_err(cd, _("Requested sector_size option is not supported.\n"));
|
||||
}
|
||||
out:
|
||||
crypt_safe_free(table_params);
|
||||
dm_exit_context();
|
||||
@@ -1169,23 +1354,22 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
struct crypt_dm_active_device *dmd)
|
||||
{
|
||||
uint64_t val64;
|
||||
char *rcipher, *key_, *rdevice, *endp, buffer[3], *arg;
|
||||
unsigned int i;
|
||||
char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc;
|
||||
unsigned int i, val;
|
||||
int r;
|
||||
size_t key_size;
|
||||
struct device *data_device = NULL;
|
||||
char *cipher = NULL;
|
||||
char *cipher = NULL, *integrity = NULL;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
memset(dmd, 0, sizeof(*dmd));
|
||||
dmd->target = DM_CRYPT;
|
||||
dmd->u.crypt.sector_size = SECTOR_SIZE;
|
||||
|
||||
r = -EINVAL;
|
||||
|
||||
rcipher = strsep(¶ms, " ");
|
||||
/* cipher */
|
||||
if (get_flags & DM_ACTIVE_CRYPT_CIPHER)
|
||||
cipher = strdup(rcipher);
|
||||
rintegrity = NULL;
|
||||
|
||||
/* skip */
|
||||
key_ = strsep(¶ms, " ");
|
||||
@@ -1216,6 +1400,8 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
val64 = strtoull(params, ¶ms, 10);
|
||||
dmd->u.crypt.offset = val64;
|
||||
|
||||
dmd->u.crypt.tag_size = 0;
|
||||
|
||||
/* Features section, available since crypt target version 1.11 */
|
||||
if (*params) {
|
||||
if (*params != ' ')
|
||||
@@ -1238,7 +1424,14 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
dmd->flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT;
|
||||
else if (!strcasecmp(arg, "submit_from_crypt_cpus"))
|
||||
dmd->flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
|
||||
else /* unknown option */
|
||||
else if (sscanf(arg, "integrity:%u:", &val) == 1) {
|
||||
dmd->u.crypt.tag_size = val;
|
||||
rintegrity = strchr(arg + strlen("integrity:"), ':') + 1;
|
||||
if (!rintegrity)
|
||||
goto err;
|
||||
} else if (sscanf(arg, "sector_size:%u", &val) == 1) {
|
||||
dmd->u.crypt.sector_size = val;
|
||||
} else /* unknown option */
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1247,6 +1440,17 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* cipher */
|
||||
if (get_flags & DM_ACTIVE_CRYPT_CIPHER) {
|
||||
r = cipher_dm2c(CONST_CAST(char**)&cipher,
|
||||
CONST_CAST(char**)&integrity,
|
||||
rcipher, rintegrity);
|
||||
if (r < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = -EINVAL;
|
||||
|
||||
/* Never allow to return empty key */
|
||||
if ((get_flags & DM_ACTIVE_CRYPT_KEY) && dmi->suspended) {
|
||||
log_dbg("Cannot read volume key while suspended.");
|
||||
@@ -1254,7 +1458,7 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
}
|
||||
|
||||
if (key_[0] == ':')
|
||||
dmd->u.crypt.key_in_keyring = 1;
|
||||
dmd->flags |= CRYPT_ACTIVATE_KEYRING_KEY;
|
||||
|
||||
if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
|
||||
/* we will trust kernel the key_string is in expected format */
|
||||
@@ -1272,11 +1476,12 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
|
||||
if (get_flags & DM_ACTIVE_CRYPT_KEY) {
|
||||
if (key_[0] == ':') {
|
||||
dmd->u.crypt.key_description = strdup(strpbrk(strpbrk(key_ + 1, ":") + 1, ":") + 1);
|
||||
if (!dmd->u.crypt.key_description) {
|
||||
key_desc = strdup(strpbrk(strpbrk(key_ + 1, ":") + 1, ":") + 1);
|
||||
if (!key_desc) {
|
||||
r = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
crypt_volume_key_set_description(vk, key_desc);
|
||||
} else {
|
||||
buffer[2] = '\0';
|
||||
for(i = 0; i < vk->keylength; i++) {
|
||||
@@ -1294,6 +1499,8 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
|
||||
if (cipher)
|
||||
dmd->u.crypt.cipher = cipher;
|
||||
if (integrity)
|
||||
dmd->u.crypt.integrity = integrity;
|
||||
if (data_device)
|
||||
dmd->data_device = data_device;
|
||||
if (vk)
|
||||
@@ -1301,6 +1508,7 @@ static int _dm_query_crypt(uint32_t get_flags,
|
||||
return 0;
|
||||
err:
|
||||
free(cipher);
|
||||
free(integrity);
|
||||
device_free(data_device);
|
||||
crypt_free_volume_key(vk);
|
||||
return r;
|
||||
@@ -1543,9 +1751,9 @@ static int _dm_query_integrity(uint32_t get_flags,
|
||||
uint32_t val32;
|
||||
uint64_t val64;
|
||||
char c, *str, *str2, *arg;
|
||||
unsigned int features, val;
|
||||
unsigned int i, features, val;
|
||||
ssize_t len;
|
||||
int i, r;
|
||||
int r;
|
||||
struct device *data_device = NULL;
|
||||
char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL;
|
||||
struct volume_key *vk = NULL;
|
||||
@@ -1830,7 +2038,7 @@ out:
|
||||
}
|
||||
|
||||
int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
|
||||
size_t key_size, const char *key, unsigned key_in_keyring)
|
||||
const struct volume_key *vk)
|
||||
{
|
||||
uint32_t dmt_flags;
|
||||
int msg_size;
|
||||
@@ -1843,10 +2051,10 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
|
||||
if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED))
|
||||
goto out;
|
||||
|
||||
if (key_in_keyring)
|
||||
msg_size = strlen(key) + get_key_size_strlen(key_size) + 17;
|
||||
if (vk->key_description)
|
||||
msg_size = strlen(vk->key_description) + get_key_size_strlen(vk->keylength) + 17;
|
||||
else
|
||||
msg_size = key_size * 2 + 10; // key set <key>
|
||||
msg_size = vk->keylength * 2 + 10; // key set <key>
|
||||
|
||||
msg = crypt_safe_alloc(msg_size);
|
||||
if (!msg) {
|
||||
@@ -1855,10 +2063,10 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
|
||||
}
|
||||
|
||||
strcpy(msg, "key set ");
|
||||
if (key_in_keyring)
|
||||
snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", key_size, key);
|
||||
if (vk->key_description)
|
||||
snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", vk->keylength, vk->key_description);
|
||||
else
|
||||
hex_key(&msg[8], key_size, key);
|
||||
hex_key(&msg[8], vk->keylength, vk->key);
|
||||
|
||||
if (!_dm_message(name, msg, dmt_flags) ||
|
||||
!_dm_simple(DM_DEVICE_RESUME, name, 1)) {
|
||||
|
||||
Reference in New Issue
Block a user