1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2010 ProFUSION embedded systems
6 #include <linux/major.h>
7 #include <linux/raid/md_u.h>
11 #include "sd-device.h"
13 #include "alloc-util.h"
14 #include "blockdev-util.h"
15 #include "detach-md.h"
16 #include "device-util.h"
17 #include "devnum-util.h"
18 #include "errno-util.h"
20 #include "string-util.h"
22 typedef struct RaidDevice
{
25 LIST_FIELDS(struct RaidDevice
, raid_device
);
28 static void raid_device_free(RaidDevice
**head
, RaidDevice
*m
) {
32 LIST_REMOVE(raid_device
, *head
, m
);
38 static void raid_device_list_free(RaidDevice
**head
) {
42 raid_device_free(head
, *head
);
45 static int md_list_get(RaidDevice
**head
) {
46 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
51 r
= sd_device_enumerator_new(&e
);
55 r
= sd_device_enumerator_allow_uninitialized(e
);
59 r
= sd_device_enumerator_add_match_subsystem(e
, "block", true);
63 r
= sd_device_enumerator_add_match_sysname(e
, "md*");
67 /* Filter out partitions. */
68 r
= sd_device_enumerator_add_match_property(e
, "DEVTYPE", "disk");
72 FOREACH_DEVICE(e
, d
) {
73 _cleanup_free_
char *p
= NULL
;
74 const char *dn
, *md_level
;
78 if (sd_device_get_devnum(d
, &devnum
) < 0 ||
79 sd_device_get_devname(d
, &dn
) < 0)
82 r
= sd_device_get_property_value(d
, "MD_LEVEL", &md_level
);
84 log_warning_errno(r
, "Failed to get MD_LEVEL property for %s, ignoring: %m", dn
);
88 /* MD "containers" are a special type of MD devices, used for external metadata. Since it
89 * doesn't provide RAID functionality in itself we don't need to stop it. */
90 if (streq(md_level
, "container"))
97 m
= new(RaidDevice
, 1);
106 LIST_PREPEND(raid_device
, *head
, m
);
112 static int delete_md(RaidDevice
*m
) {
113 _cleanup_close_
int fd
= -EBADF
;
116 assert(major(m
->devnum
) != 0);
119 fd
= open(m
->path
, O_RDONLY
|O_CLOEXEC
|O_EXCL
);
124 log_debug_errno(errno
, "Failed to sync MD block device %s, ignoring: %m", m
->path
);
126 return RET_NERRNO(ioctl(fd
, STOP_ARRAY
, NULL
));
129 static int md_points_list_detach(RaidDevice
**head
, bool *changed
, bool last_try
) {
131 dev_t rootdev
= 0, usrdev
= 0;
136 (void) get_block_device("/", &rootdev
);
137 (void) get_block_device("/usr", &usrdev
);
139 LIST_FOREACH(raid_device
, m
, *head
) {
140 if ((major(rootdev
) != 0 && rootdev
== m
->devnum
) ||
141 (major(usrdev
) != 0 && usrdev
== m
->devnum
)) {
142 log_debug("Not detaching MD %s that backs the OS itself, skipping.", m
->path
);
147 log_info("Stopping MD %s (" DEVNUM_FORMAT_STR
").", m
->path
, DEVNUM_FORMAT_VAL(m
->devnum
));
150 log_full_errno(last_try
? LOG_ERR
: LOG_INFO
, r
, "Could not stop MD %s: %m", m
->path
);
156 raid_device_free(head
, m
);
162 int md_detach_all(bool *changed
, bool last_try
) {
163 _cleanup_(raid_device_list_free
) LIST_HEAD(RaidDevice
, md_list_head
);
168 LIST_HEAD_INIT(md_list_head
);
170 r
= md_list_get(&md_list_head
);
174 return md_points_list_detach(&md_list_head
, changed
, last_try
);