Skip to content

Commit c7e4e85

Browse files
michaelolbrichjluebbe
authored andcommitted
src/nbd: avoid race with udev during setup
With the NBD_CFLAG_DISCONNECT_ON_CLOSE flag set, the nbd device will be automatically removed when there are no more users after it was was opened the first time. The idea here is, that the nbd device is created, and then immediately mounted (possibly with dm-verity or dm-crypt in between). Wenn the bundle ist later unmounted then the nbd device is automatically removed. Unfortunately this is racy: udev will see the new device and open it immediately. If it closes the device before it is mounted (or dm-verity or dm-crypt is configured) then the nbd device is removed immediately and mounting will fail. So instead create the nbd device without NBD_CFLAG_DISCONNECT_ON_CLOSE, open it to keep it busy and then reconfigure the device to set NBD_CFLAG_DISCONNECT_ON_CLOSE. The file descriptor is kept open until the bundle is mounted. Note that with this change, the nbd device will not be fully removed if rauc dies between creating the nbd device and reconfiguring it, or if reconfiguring fails. Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de> Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
1 parent 5ec5cd9 commit c7e4e85

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

include/nbd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(RaucNBDServer, r_nbd_free_server);
6666
*
6767
* @return TRUE on success, FALSE if an error occurred
6868
*/
69-
gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, GError **error);
69+
gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, int *devicefd, GError **error);
7070

7171
/**
7272
* Remove a previously configured NBD device from the kernel.

src/bundle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3094,7 +3094,7 @@ gboolean mount_bundle(RaucBundle *bundle, GError **error)
30943094
bundle->nbd_dev->data_size = bundle->size;
30953095
bundle->nbd_dev->sock = bundle->nbd_srv->sock;
30963096
bundle->nbd_srv->sock = -1;
3097-
res = r_nbd_setup_device(bundle->nbd_dev, &ierror);
3097+
res = r_nbd_setup_device(bundle->nbd_dev, &devicefd, &ierror);
30983098
if (!res) {
30993099
/* The setup failed, so the socket still belongs to the nbd_srv. */
31003100
bundle->nbd_srv->sock = bundle->nbd_dev->sock;

src/nbd.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ static struct nl_sock *netlink_connect(int *driver_id, GError **error)
142142
return nl;
143143
}
144144

145-
gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, GError **error)
145+
gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, int *devicefd, GError **error)
146146
{
147147
GError *ierror = NULL;
148148
gboolean res = FALSE;
@@ -151,8 +151,11 @@ gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, GError **error)
151151
struct nl_msg *msg = NULL;
152152
struct nlattr *attr_sockets = NULL;
153153
struct nlattr *attr_item = NULL;
154+
g_autofree gchar *device_path = NULL;
155+
g_auto(filedesc) idevicefd = -1;
154156

155157
g_return_val_if_fail(nbd_dev != NULL, FALSE);
158+
g_return_val_if_fail(devicefd != NULL && *devicefd == -1, FALSE);
156159
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
157160

158161
g_assert(nbd_dev->data_size % 4096 == 0);
@@ -177,9 +180,6 @@ gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, GError **error)
177180
NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, nbd_dev->data_size);
178181
NLA_PUT_U64(msg, NBD_ATTR_BLOCK_SIZE_BYTES, 4096);
179182
NLA_PUT_U64(msg, NBD_ATTR_SERVER_FLAGS, 0);
180-
NLA_PUT_U64(msg, NBD_ATTR_CLIENT_FLAGS,
181-
NBD_CFLAG_DISCONNECT_ON_CLOSE
182-
);
183183
NLA_PUT_U64(msg, NBD_ATTR_TIMEOUT, 300);
184184

185185
attr_sockets = nla_nest_start(msg, NBD_ATTR_SOCKETS);
@@ -201,11 +201,39 @@ gboolean r_nbd_setup_device(RaucNBDDevice *nbd_dev, GError **error)
201201
if (!nbd_dev->index_valid)
202202
g_error("failed to create nbd device");
203203

204-
nbd_dev->dev = g_strdup_printf("/dev/nbd%"G_GUINT32_FORMAT, nbd_dev->index);
204+
device_path = g_strdup_printf("/dev/nbd%"G_GUINT32_FORMAT, nbd_dev->index);
205+
206+
idevicefd = g_open(device_path, O_RDONLY | O_CLOEXEC);
207+
if (idevicefd < 0) {
208+
int err = errno;
209+
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(err), "failed to open %s: %s", device_path, g_strerror(err));
210+
res = FALSE;
211+
goto out;
212+
}
213+
214+
msg = nlmsg_alloc();
215+
if (!msg)
216+
g_error("failed to allocate netlink message");
217+
218+
if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, driver_id, 0, 0, NBD_CMD_RECONFIGURE, 0))
219+
g_error("failed to add generic netlink headers to message");
220+
221+
NLA_PUT_U32(msg, NBD_ATTR_INDEX, nbd_dev->index);
222+
NLA_PUT_U64(msg, NBD_ATTR_CLIENT_FLAGS, NBD_CFLAG_DISCONNECT_ON_CLOSE);
223+
224+
nl_socket_modify_cb(nl, NL_CB_VALID, NL_CB_CUSTOM, NULL, NULL);
225+
if (nl_send_sync(nl, msg) < 0) {
226+
res = FALSE;
227+
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "netlink send_sync failed");
228+
goto out;
229+
}
205230

206231
g_message("setup done for %s", nbd_dev->dev);
207232

208233
res = TRUE;
234+
nbd_dev->dev = g_steal_pointer(&device_path);
235+
*devicefd = idevicefd;
236+
idevicefd = -1;
209237
goto out;
210238

211239
/* This label is used by the NLA_PUT macros. */

0 commit comments

Comments
 (0)