Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle missing /etc/mtab and modify output #1514

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 58 additions & 45 deletions casadm/cas_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@

#define CORE_ADD_MAX_TIMEOUT 30

int is_cache_mounted(int cache_id);
int is_core_mounted(int cache_id, int core_id);
bool device_mounts_detected(const char *pattern, int cmplen);
void print_mounted_devices(const char *pattern, int cmplen);

/* KCAS_IOCTL_CACHE_CHECK_DEVICE wrapper */
int _check_cache_device(const char *device_path,
Expand Down Expand Up @@ -1119,8 +1119,16 @@ int stop_cache(uint16_t cache_id, int flush)
int status;

/* Don't stop instance with mounted filesystem */
if (is_cache_mounted(cache_id) == FAILURE)
int cmplen = 0;
char pattern[80];

/* verify if any core (or core partition) for this cache is mounted */
cmplen = snprintf(pattern, sizeof(pattern), "/dev/cas%d-", cache_id) - 1;
rafalste marked this conversation as resolved.
Show resolved Hide resolved
if (device_mounts_detected(pattern, cmplen)) {
cas_printf(LOG_ERR, "Can't stop cache instance %d due to mounted devices:\n", cache_id);
print_mounted_devices(pattern, cmplen);
return FAILURE;
}

fd = open_ctrl_device();
if (fd == -1)
Expand Down Expand Up @@ -1803,58 +1811,52 @@ int add_core(unsigned int cache_id, unsigned int core_id, const char *core_devic
return SUCCESS;
}

int _check_if_mounted(int cache_id, int core_id)
bool device_mounts_detected(const char *pattern, int cmplen)
{
FILE *mtab;
struct mntent *mstruct;
char dev_buf[80];
int difference = 0, error = 0;
if (core_id >= 0) {
/* verify if specific core is mounted */
snprintf(dev_buf, sizeof(dev_buf), "/dev/cas%d-%d", cache_id, core_id);
} else {
/* verify if any core from given cache is mounted */
snprintf(dev_buf, sizeof(dev_buf), "/dev/cas%d-", cache_id);
}
int no_match = 0, error = 0;

mtab = setmntent("/etc/mtab", "r");
if (!mtab)
{
cas_printf(LOG_ERR, "Error while accessing /etc/mtab\n");
return FAILURE;
if (!mtab) {
/* if /etc/mtab not found then the kernel will check for mounts */
return false;
rafalste marked this conversation as resolved.
Show resolved Hide resolved
}

while ((mstruct = getmntent(mtab)) != NULL) {
error = strcmp_s(mstruct->mnt_fsname, PATH_MAX, dev_buf, &difference);
error = strcmp_s(mstruct->mnt_fsname, cmplen, pattern, &no_match);
/* mstruct->mnt_fsname is /dev/... block device path, not a mountpoint */
if (error != EOK)
return FAILURE;
if (!difference) {
if (core_id<0) {
cas_printf(LOG_ERR,
"Can't stop cache instance %d. Device %s is mounted!\n",
cache_id, mstruct->mnt_fsname);
} else {
cas_printf(LOG_ERR,
"Can't remove core %d from cache %d."
" Device %s is mounted!\n",
core_id, cache_id, mstruct->mnt_fsname);
}
return FAILURE;
}
return false;
rafalste marked this conversation as resolved.
Show resolved Hide resolved
if (no_match)
continue;

return true;
}
return SUCCESS;

return false;
}

int is_cache_mounted(int cache_id)
void print_mounted_devices(const char *pattern, int cmplen)
{
return _check_if_mounted(cache_id, -1);
}
FILE *mtab;
struct mntent *mstruct;
int no_match = 0, error = 0;

int is_core_mounted(int cache_id, int core_id)
{
return _check_if_mounted(cache_id, core_id);
mtab = setmntent("/etc/mtab", "r");
if (!mtab) {
/* should exist, but if /etc/mtab not found we cannot print mounted devices */
return;
}

while ((mstruct = getmntent(mtab)) != NULL) {
error = strcmp_s(mstruct->mnt_fsname, cmplen, pattern, &no_match);
/* mstruct->mnt_fsname is /dev/... block device path, not a mountpoint */
if (error != EOK || no_match)
rafalste marked this conversation as resolved.
Show resolved Hide resolved
continue;

cas_printf(LOG_ERR, "%s\n", mstruct->mnt_fsname);
}
}

int remove_core(unsigned int cache_id, unsigned int core_id,
Expand All @@ -1864,7 +1866,23 @@ int remove_core(unsigned int cache_id, unsigned int core_id,
struct kcas_remove_core cmd;

/* don't even attempt ioctl if filesystem is mounted */
if (SUCCESS != is_core_mounted(cache_id, core_id)) {
bool mounts_detected = false;
int cmplen = 0;
char pattern[80];

/* verify if specific core is mounted */
cmplen = snprintf(pattern, sizeof(pattern), "/dev/cas%d-%d", cache_id, core_id);
mounts_detected = device_mounts_detected(pattern, cmplen)
if (!mounts_detected) {
/* verify if any partition of the core is mounted */
cmplen = snprintf(pattern, sizeof(pattern), "/dev/cas%d-%dp", cache_id, core_id) - 1;
mounts_detected = device_mounts_detected(pattern, cmplen);
}
if (mounts_detected) {
cas_printf(LOG_ERR, "Can't remove core %d from "
"cache %d due to mounted devices:\n",
core_id, cache_id);
print_mounted_devices(pattern, cmplen);
return FAILURE;
}

Expand Down Expand Up @@ -1929,11 +1947,6 @@ int remove_inactive_core(unsigned int cache_id, unsigned int core_id,
int fd = 0;
struct kcas_remove_inactive cmd;

/* don't even attempt ioctl if filesystem is mounted */
if (SUCCESS != is_core_mounted(cache_id, core_id)) {
return FAILURE;
}

rafalste marked this conversation as resolved.
Show resolved Hide resolved
fd = open_ctrl_device();
if (fd == -1)
return FAILURE;
Expand Down
17 changes: 13 additions & 4 deletions test/functional/api/cas/cli_messages.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# Copyright(c) 2019-2022 Intel Corporation
# Copyright(c) 2024 Huawei Technologies Co., Ltd.
# Copyright(c) 2024 Huawei Technologies
# SPDX-License-Identifier: BSD-3-Clause
#

Expand Down Expand Up @@ -84,11 +84,20 @@
]

remove_mounted_core = [
r"Can\'t remove core \d+ from cache \d+\. Device /dev/cas\d+-\d+ is mounted\!"
r"Can\'t remove core \d+ from cache \d+ due to mounted devices:"
]

remove_mounted_core_kernel = [
r"Error while removing core device \d+ from cache instance \d+",
r"Device opens or mount are pending to this cache",
]

stop_cache_mounted_core = [
r"Error while removing cache \d+",
r"Can\'t stop cache instance \d+ due to mounted devices:"
]

stop_cache_mounted_core_kernel = [
r"Error while stopping cache \d+",
r"Device opens or mount are pending to this cache",
]

Expand Down Expand Up @@ -242,7 +251,7 @@ def __check_string_msg(text: str, expected_messages, negate=False):
msg_ok = False
elif matches and negate:
TestRun.LOGGER.error(
f"Message is incorrect, expected to not find: {msg}\n " f"actual: {text}."
f"Message is incorrect, expected to not find: {msg}\n actual: {text}."
)
msg_ok = False
return msg_ok
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#
# Copyright(c) 2019-2022 Intel Corporation
# Copyright(c) 2024 Huawei Technologies
# SPDX-License-Identifier: BSD-3-Clause
#

Expand All @@ -8,15 +9,16 @@
from api.cas import casadm, casadm_parser, cli, cli_messages
from core.test_run import TestRun
from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan
from test_tools import fs_utils
from test_tools import fs_utils, disk_utils
from test_tools.disk_utils import Filesystem
from test_utils.filesystem.file import File
from test_utils.filesystem.symlink import Symlink
from test_utils.size import Size, Unit

mount_point = "/mnt/cas"
mount_point, mount_point2 = "/mnt/cas", "/mnt/cas2"
test_file_path = f"{mount_point}/test_file"



@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
def test_load_cache_with_mounted_core():
Expand Down Expand Up @@ -79,6 +81,7 @@ def test_load_cache_with_mounted_core():

@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
@pytest.mark.require_disk("core2", DiskTypeLowerThan("cache"))
def test_stop_cache_with_mounted_partition():
"""
title: Fault injection test for removing core and stopping cache with mounted core.
Expand All @@ -89,6 +92,83 @@ def test_stop_cache_with_mounted_partition():
- No system crash.
- Unable to stop cache when partition is mounted.
- Unable to remove core when partition is mounted.
- casadm displays proper message.
"""
with TestRun.step("Prepare cache device."):
cache_dev = TestRun.disks['cache']
cache_dev.create_partitions([Size(1, Unit.GibiByte)])
cache_part = cache_dev.partitions[0]

with TestRun.step("Prepare 2 core devices."):
core_dev, core_dev2 = TestRun.disks['core'], TestRun.disks['core2']

with TestRun.step("Start cache."):
cache = casadm.start_cache(cache_part, force=True)

with TestRun.step("Add core devices to cache."):
core = cache.add_core(core_dev)
core2 = cache.add_core(core_dev2)

with TestRun.step("Create partitions on one exported object."):
core.block_size = Size(disk_utils.get_block_size(core.get_device_id()))
disk_utils.create_partitions(core, 2 * [Size(4, Unit.GibiByte)])
fs_part = core.partitions[0]

with TestRun.step("Create xfs filesystems on one exported object partition "
"and on the non-partitioned exported object."):
fs_part.create_filesystem(Filesystem.xfs)
core2.create_filesystem(Filesystem.xfs)

with TestRun.step("Mount created filesystems."):
fs_part.mount(mount_point)
core2.mount(mount_point2)

with TestRun.step("Ensure /etc/mtab exists."):
if not fs_utils.check_if_file_exists("/etc/mtab"):
Symlink.create_symlink("/proc/self/mounts", "/etc/mtab")
rafalste marked this conversation as resolved.
Show resolved Hide resolved

with TestRun.step("Try to remove the core with partitions from cache."):
output = TestRun.executor.run_expect_fail(cli.remove_core_cmd(cache_id=str(cache.cache_id),
core_id=str(core.core_id)))
messages = cli_messages.remove_mounted_core.copy()
messages.append(fs_part.path)
cli_messages.check_stderr_msg(output, messages)

with TestRun.step("Try to remove the core without partitions from cache."):
output = TestRun.executor.run_expect_fail(cli.remove_core_cmd(cache_id=str(cache.cache_id),
core_id=str(core2.core_id)))
messages = cli_messages.remove_mounted_core.copy()
messages.append(core2.path)
cli_messages.check_stderr_msg(output, messages)

with TestRun.step("Try to stop CAS."):
output = TestRun.executor.run_expect_fail(cli.stop_cmd(cache_id=str(cache.cache_id)))
messages = cli_messages.stop_cache_mounted_core.copy()
messages.append(fs_part.path)
messages.append(core2.path)
cli_messages.check_stderr_msg(output, messages)

with TestRun.step("Unmount core devices."):
fs_part.unmount()
core2.unmount()

with TestRun.step("Stop cache."):
casadm.stop_all_caches()


@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand]))
@pytest.mark.require_disk("core", DiskTypeLowerThan("cache"))
def test_stop_cache_with_mounted_partition_no_mtab():
"""
title: Test for removing core and stopping cache when casadm is unable to check mounts.
description: |
Negative test of the ability of CAS to remove core and stop cache while core
is still mounted and casadm is unable to check mounts.
pass_criteria:
- No system crash.
- Unable to stop cache when partition is mounted.
- Unable to remove core when partition is mounted.
- casadm displays proper message informing that mount check was performed by kernel module
"""
with TestRun.step("Prepare cache and core devices. Start CAS."):
cache_dev = TestRun.disks['cache']
Expand All @@ -104,18 +184,32 @@ def test_stop_cache_with_mounted_partition():
core = cache.add_core(core_part)
core.mount(mount_point)

with TestRun.step("Move /etc/mtab"):
if fs_utils.check_if_file_exists("/etc/mtab"):
mtab = File("/etc/mtab")
else:
mtab = Symlink.create_symlink("/proc/self/mounts", "/etc/mtab")
mtab.move("/tmp")

with TestRun.step("Try to remove core from cache."):
output = TestRun.executor.run_expect_fail(cli.remove_core_cmd(cache_id=str(cache.cache_id),
core_id=str(core.core_id)))
cli_messages.check_stderr_msg(output, cli_messages.remove_mounted_core)
cli_messages.check_stderr_msg(output, cli_messages.remove_mounted_core_kernel)

with TestRun.step("Try to stop CAS."):
output = TestRun.executor.run_expect_fail(cli.stop_cmd(cache_id=str(cache.cache_id)))
cli_messages.check_stderr_msg(output, cli_messages.stop_cache_mounted_core)
cli_messages.check_stderr_msg(output, cli_messages.stop_cache_mounted_core_kernel)

with TestRun.step("Unmount core device."):
core.unmount()

with TestRun.step("Remove core."):
core.remove_core()

with TestRun.step("Re-add core."):
cache.add_core(core_part)

with TestRun.step("Stop cache."):
casadm.stop_all_caches()
cache.stop()

mtab.move("/etc")