Coverage Report

Created: 2026-02-22 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/utils_loop.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * loopback block device utilities
4
 *
5
 * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2009-2025 Milan Broz
7
 */
8
9
#include <stdlib.h>
10
#include <string.h>
11
#include <stdio.h>
12
#include <unistd.h>
13
#include <fcntl.h>
14
#include <errno.h>
15
#include <limits.h>
16
#include <sys/ioctl.h>
17
#include <sys/stat.h>
18
#if HAVE_SYS_SYSMACROS_H
19
# include <sys/sysmacros.h>     /* for major, minor */
20
#endif
21
#include <linux/types.h>
22
#include <linux/loop.h>
23
24
#include "utils_loop.h"
25
#include "libcryptsetup_macros.h"
26
27
0
#define LOOP_DEV_MAJOR 7
28
29
#ifndef LO_FLAGS_AUTOCLEAR
30
0
#define LO_FLAGS_AUTOCLEAR 4
31
#endif
32
33
#ifndef LOOP_CTL_GET_FREE
34
#define LOOP_CTL_GET_FREE 0x4C82
35
#endif
36
37
#ifndef LOOP_SET_CAPACITY
38
#define LOOP_SET_CAPACITY 0x4C07
39
#endif
40
41
#ifndef LOOP_SET_BLOCK_SIZE
42
#define LOOP_SET_BLOCK_SIZE 0x4C09
43
#endif
44
45
#ifndef LOOP_CONFIGURE
46
0
#define LOOP_CONFIGURE 0x4C0A
47
struct loop_config {
48
  __u32 fd;
49
  __u32 block_size;
50
  struct loop_info64 info;
51
  __u64 __reserved[8];
52
};
53
#endif
54
55
static char *crypt_loop_get_device_old(void)
56
0
{
57
0
  char dev[64];
58
0
  int i, loop_fd;
59
0
  struct loop_info64 lo64 = {0};
60
61
0
  for (i = 0; i < 256; i++) {
62
0
    sprintf(dev, "/dev/loop%d", i);
63
64
0
    loop_fd = open(dev, O_RDONLY);
65
0
    if (loop_fd < 0)
66
0
      return NULL;
67
68
0
    if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) &&
69
0
        errno == ENXIO) {
70
0
      close(loop_fd);
71
0
      return strdup(dev);
72
0
    }
73
0
    close(loop_fd);
74
0
  }
75
76
0
  return NULL;
77
0
}
78
79
static char *crypt_loop_get_device(void)
80
0
{
81
0
  char dev[64];
82
0
  int i, loop_fd;
83
0
  struct stat st;
84
85
0
  loop_fd = open("/dev/loop-control", O_RDONLY);
86
0
  if (loop_fd < 0)
87
0
    return crypt_loop_get_device_old();
88
89
0
  i = ioctl(loop_fd, LOOP_CTL_GET_FREE);
90
0
  if (i < 0) {
91
0
    close(loop_fd);
92
0
    return NULL;
93
0
  }
94
0
  close(loop_fd);
95
96
0
  if (sprintf(dev, "/dev/loop%d", i) < 0)
97
0
    return NULL;
98
99
0
  if (stat(dev, &st) || !S_ISBLK(st.st_mode))
100
0
    return NULL;
101
102
0
  return strdup(dev);
103
0
}
104
105
int crypt_loop_attach(char **loop, const char *file, int offset,
106
          int autoclear, int *readonly, size_t blocksize)
107
0
{
108
0
  struct loop_config config = {0};
109
0
  char *lo_file_name;
110
0
  int loop_fd = -1, file_fd = -1, r = 1;
111
0
  int fallback = 0;
112
113
0
  *loop = NULL;
114
115
0
  file_fd = open(file, *readonly ? O_RDONLY : O_RDWR);
116
0
  if (file_fd < 0 && (errno == EROFS || errno == EACCES) && !*readonly) {
117
0
    *readonly = 1;
118
0
    file_fd = open(file, O_RDONLY);
119
0
  }
120
0
  if (file_fd < 0)
121
0
    goto out;
122
123
0
  config.fd = file_fd;
124
125
0
  lo_file_name = (char*)config.info.lo_file_name;
126
0
  lo_file_name[LO_NAME_SIZE-1] = '\0';
127
0
  strncpy(lo_file_name, file, LO_NAME_SIZE-1);
128
0
  config.info.lo_offset = offset;
129
0
  if (autoclear)
130
0
    config.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
131
0
  if (blocksize > SECTOR_SIZE)
132
0
    config.block_size = blocksize;
133
134
0
  while (loop_fd < 0) {
135
0
    *loop = crypt_loop_get_device();
136
0
    if (!*loop)
137
0
      goto out;
138
139
0
    loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR);
140
0
    if (loop_fd < 0)
141
0
      goto out;
142
0
    if (ioctl(loop_fd, LOOP_CONFIGURE, &config) < 0) {
143
0
      if (errno == EINVAL || errno == ENOTTY) {
144
0
        free(*loop);
145
0
        *loop = NULL;
146
147
0
        close(loop_fd);
148
0
        loop_fd = -1;
149
150
        /* kernel doesn't support LOOP_CONFIGURE */
151
0
        fallback = 1;
152
0
        break;
153
0
      }
154
0
      if (errno != EBUSY)
155
0
        goto out;
156
0
      free(*loop);
157
0
      *loop = NULL;
158
159
0
      close(loop_fd);
160
0
      loop_fd = -1;
161
0
    }
162
0
  }
163
164
0
  if (fallback) {
165
0
    while (loop_fd < 0) {
166
0
      *loop = crypt_loop_get_device();
167
0
      if (!*loop)
168
0
        goto out;
169
170
0
      loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR);
171
0
      if (loop_fd < 0)
172
0
        goto out;
173
0
      if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) {
174
0
        if (errno != EBUSY)
175
0
          goto out;
176
0
        free(*loop);
177
0
        *loop = NULL;
178
179
0
        close(loop_fd);
180
0
        loop_fd = -1;
181
0
      }
182
0
    }
183
184
0
    if (blocksize > SECTOR_SIZE)
185
0
      (void)ioctl(loop_fd, LOOP_SET_BLOCK_SIZE, (unsigned long)blocksize);
186
187
0
    if (ioctl(loop_fd, LOOP_SET_STATUS64, &config.info) < 0) {
188
0
      (void)ioctl(loop_fd, LOOP_CLR_FD, 0);
189
0
      goto out;
190
0
    }
191
0
  }
192
193
  /* Verify that autoclear is really set */
194
0
  if (autoclear) {
195
0
    memset(&config.info, 0, sizeof(config.info));
196
0
    if (ioctl(loop_fd, LOOP_GET_STATUS64, &config.info) < 0 ||
197
0
       !(config.info.lo_flags & LO_FLAGS_AUTOCLEAR)) {
198
0
    (void)ioctl(loop_fd, LOOP_CLR_FD, 0);
199
0
      goto out;
200
0
    }
201
0
  }
202
203
0
  r = 0;
204
0
out:
205
0
  if (r && loop_fd >= 0)
206
0
    close(loop_fd);
207
0
  if (file_fd >= 0)
208
0
    close(file_fd);
209
0
  if (r && *loop) {
210
0
    free(*loop);
211
0
    *loop = NULL;
212
0
  }
213
0
  return r ? -1 : loop_fd;
214
0
}
215
216
int crypt_loop_detach(const char *loop)
217
0
{
218
0
  int loop_fd = -1, r = 1;
219
220
0
  loop_fd = open(loop, O_RDONLY);
221
0
  if (loop_fd < 0)
222
0
                return 1;
223
224
0
  if (!ioctl(loop_fd, LOOP_CLR_FD, 0))
225
0
    r = 0;
226
227
0
  close(loop_fd);
228
0
  return r;
229
0
}
230
231
int crypt_loop_resize(const char *loop)
232
0
{
233
0
  int loop_fd = -1, r = 1;
234
235
0
  loop_fd = open(loop, O_RDONLY);
236
0
  if (loop_fd < 0)
237
0
                return 1;
238
239
0
  if (!ioctl(loop_fd, LOOP_SET_CAPACITY, 0))
240
0
    r = 0;
241
242
0
  close(loop_fd);
243
0
  return r;
244
0
}
245
246
static char *_ioctl_backing_file(const char *loop)
247
0
{
248
0
  struct loop_info64 lo64 = {0};
249
0
  int loop_fd;
250
251
0
  loop_fd = open(loop, O_RDONLY);
252
0
  if (loop_fd < 0)
253
0
    return NULL;
254
255
0
  if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) < 0) {
256
0
    close(loop_fd);
257
0
    return NULL;
258
0
  }
259
260
0
  lo64.lo_file_name[LO_NAME_SIZE-2] = '*';
261
0
  lo64.lo_file_name[LO_NAME_SIZE-1] = 0;
262
263
0
  close(loop_fd);
264
265
0
  return strdup((char*)lo64.lo_file_name);
266
0
}
267
268
static char *_sysfs_backing_file(const char *loop)
269
0
{
270
0
  struct stat st;
271
0
  char buf[PATH_MAX];
272
0
  ssize_t len;
273
0
  int fd;
274
275
0
  if (stat(loop, &st) || !S_ISBLK(st.st_mode))
276
0
    return NULL;
277
278
0
  if (snprintf(buf, sizeof(buf), "/sys/dev/block/%d:%d/loop/backing_file",
279
0
         major(st.st_rdev), minor(st.st_rdev)) < 0)
280
0
    return NULL;
281
282
0
  fd = open(buf, O_RDONLY);
283
0
  if (fd < 0)
284
0
    return NULL;
285
286
0
  len = read(fd, buf, PATH_MAX);
287
0
  close(fd);
288
0
  if (len < 2)
289
0
    return NULL;
290
291
0
  buf[len - 1] = '\0';
292
0
  return strdup(buf);
293
0
}
294
295
char *crypt_loop_backing_file(const char *loop)
296
0
{
297
0
  char *bf;
298
299
0
  if (!crypt_loop_device(loop))
300
0
    return NULL;
301
302
0
  bf = _sysfs_backing_file(loop);
303
0
  return bf ?: _ioctl_backing_file(loop);
304
0
}
305
306
int crypt_loop_device(const char *loop)
307
0
{
308
0
  struct stat st;
309
310
0
  if (!loop)
311
0
    return 0;
312
313
0
  if (stat(loop, &st) || !S_ISBLK(st.st_mode) ||
314
0
      major(st.st_rdev) != LOOP_DEV_MAJOR)
315
0
    return 0;
316
317
0
  return 1;
318
0
}