Coverage Report

Created: 2026-09-13 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/utils_device_locking.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Metadata on-disk locking for processes serialization
4
 *
5
 * Copyright (C) 2016-2026 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2016-2026 Ondrej Kozina
7
 */
8
9
#include <errno.h>
10
#include <linux/limits.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <sys/file.h>
15
#include <sys/stat.h>
16
#include <sys/types.h>
17
#include <unistd.h>
18
#if HAVE_SYS_SYSMACROS_H
19
# include <sys/sysmacros.h>     /* for major, minor */
20
#endif
21
#include <libgen.h>
22
23
#include "internal.h"
24
#include "utils_device_locking.h"
25
26
#define same_inode(buf1, buf2) \
27
1.77k
  ((buf1).st_ino == (buf2).st_ino && \
28
1.77k
   (buf1).st_dev == (buf2).st_dev)
29
30
enum lock_type {
31
  DEV_LOCK_READ = 0,
32
  DEV_LOCK_WRITE
33
};
34
35
enum lock_mode {
36
  DEV_LOCK_FILE = 0,
37
  DEV_LOCK_BDEV,
38
  DEV_LOCK_NAME
39
};
40
41
struct crypt_lock_handle {
42
  unsigned refcnt;
43
  int flock_fd;
44
  enum lock_type type;
45
  enum lock_mode mode;
46
  union {
47
  struct {
48
    dev_t devno;
49
  } bdev;
50
  struct {
51
    char *name;
52
  } name;
53
  } u;
54
};
55
56
static int resource_by_name(char *res, size_t res_size, const char *name, bool fullpath)
57
0
{
58
0
  int r;
59
60
0
  if (fullpath)
61
0
    r = snprintf(res, res_size, "%s/LN_%s", DEFAULT_LUKS2_LOCK_PATH, name);
62
0
  else
63
0
    r = snprintf(res, res_size, "LN_%s", name);
64
65
0
  return (r < 0 || (size_t)r >= res_size) ? -EINVAL : 0;
66
0
}
67
68
static int resource_by_devno(char *res, size_t res_size, dev_t devno, unsigned fullpath)
69
0
{
70
0
  int r;
71
72
0
  if (fullpath)
73
0
    r = snprintf(res, res_size, "%s/L_%d:%d", DEFAULT_LUKS2_LOCK_PATH, major(devno), minor(devno));
74
0
  else
75
0
    r = snprintf(res, res_size, "L_%d:%d", major(devno), minor(devno));
76
77
0
  return (r < 0 || (size_t)r >= res_size) ? -EINVAL : 0;
78
0
}
79
80
static int open_lock_dir(struct crypt_device *cd, const char *dir, const char *base)
81
0
{
82
0
  int dirfd, lockdfd;
83
84
0
  dirfd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
85
0
  if (dirfd < 0) {
86
0
    log_dbg(cd, "Failed to open directory %s: (%d: %s).", dir, errno, strerror(errno));
87
0
    if (errno == ENOTDIR || errno == ENOENT)
88
0
      log_err(cd, _("Locking aborted. The locking path %s/%s is unusable (not a directory or missing)."), dir, base);
89
0
    return -EINVAL;
90
0
  }
91
92
0
  lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
93
0
  if (lockdfd < 0) {
94
0
    if (errno == ENOENT) {
95
0
      log_dbg(cd, "Locking directory %s/%s will be created with default compiled-in permissions.", dir, base);
96
97
      /* success or failure w/ errno == EEXIST either way just try to open the 'base' directory again */
98
0
      if (mkdirat(dirfd, base, DEFAULT_LUKS2_LOCK_DIR_PERMS) && errno != EEXIST)
99
0
        log_dbg(cd, "Failed to create directory %s in %s (%d: %s).", base, dir, errno, strerror(errno));
100
0
      else
101
0
        lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
102
0
    } else {
103
0
      log_dbg(cd, "Failed to open directory %s/%s: (%d: %s)", dir, base, errno, strerror(errno));
104
0
      if (errno == ENOTDIR || errno == ELOOP)
105
0
        log_err(cd, _("Locking aborted. The locking path %s/%s is unusable (%s is not a directory)."), dir, base, base);
106
0
    }
107
0
  }
108
109
0
  close(dirfd);
110
0
  return lockdfd >= 0 ? lockdfd : -EINVAL;
111
0
}
112
113
static int open_resource(struct crypt_device *cd, const char *res)
114
0
{
115
0
  int err, lockdir_fd, r;
116
0
  char dir[] = DEFAULT_LUKS2_LOCK_PATH,
117
0
       base[] = DEFAULT_LUKS2_LOCK_PATH;
118
119
0
  lockdir_fd = open_lock_dir(cd, dirname(dir), basename(base));
120
0
  if (lockdir_fd < 0)
121
0
    return -EINVAL;
122
123
0
  log_dbg(cd, "Opening lock resource file %s/%s", DEFAULT_LUKS2_LOCK_PATH, res);
124
0
  r = openat(lockdir_fd, res, O_CREAT|O_NOFOLLOW|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
125
0
  err = errno;
126
127
0
  close(lockdir_fd);
128
129
0
  return r < 0 ? -err : r;
130
0
}
131
132
static int acquire_lock_handle(struct crypt_device *cd, struct device *device, struct crypt_lock_handle *h)
133
7.07k
{
134
7.07k
  char res[PATH_MAX];
135
7.07k
  int dev_fd, fd;
136
7.07k
  struct stat st;
137
138
7.07k
  assert(device);
139
7.07k
  assert(h);
140
141
7.07k
  dev_fd = open(device_path(device), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
142
7.07k
  if (dev_fd < 0)
143
0
    return -EINVAL;
144
145
7.07k
  if (fstat(dev_fd, &st)) {
146
0
    close(dev_fd);
147
0
    return -EINVAL;
148
0
  }
149
150
7.07k
  if (S_ISBLK(st.st_mode)) {
151
0
    if (resource_by_devno(res, sizeof(res), st.st_rdev, 0)) {
152
0
      close(dev_fd);
153
0
      return -EINVAL;
154
0
    }
155
156
0
    fd = open_resource(cd, res);
157
0
    close(dev_fd);
158
0
    if (fd < 0)
159
0
      return fd;
160
161
0
    h->flock_fd = fd;
162
0
    h->u.bdev.devno = st.st_rdev;
163
0
    h->mode = DEV_LOCK_BDEV;
164
7.07k
  } else if (S_ISREG(st.st_mode)) {
165
    /* workaround for nfsv4 */
166
7.07k
    fd = open(device_path(device), O_RDWR | O_NONBLOCK | O_CLOEXEC);
167
7.07k
    if (fd < 0)
168
0
      h->flock_fd = dev_fd;
169
7.07k
    else {
170
7.07k
      h->flock_fd = fd;
171
7.07k
      close(dev_fd);
172
7.07k
    }
173
7.07k
    h->mode = DEV_LOCK_FILE;
174
7.07k
  } else {
175
    /* Wrong device type */
176
0
    close(dev_fd);
177
0
    return -EINVAL;
178
0
  }
179
180
7.07k
  return 0;
181
7.07k
}
182
183
static int acquire_lock_handle_by_name(struct crypt_device *cd, const char *name, struct crypt_lock_handle *h)
184
0
{
185
0
  char res[PATH_MAX];
186
0
  int fd;
187
188
0
  assert(name);
189
0
  assert(h);
190
191
0
  h->u.name.name = strdup(name);
192
0
  if (!h->u.name.name)
193
0
    return -ENOMEM;
194
195
0
  if (resource_by_name(res, sizeof(res), name, false)) {
196
0
    free(h->u.name.name);
197
0
    return -EINVAL;
198
0
  }
199
200
0
  fd = open_resource(cd, res);
201
0
  if (fd < 0) {
202
0
    free(h->u.name.name);
203
0
    return fd;
204
0
  }
205
206
0
  h->flock_fd = fd;
207
0
  h->mode = DEV_LOCK_NAME;
208
209
0
  return 0;
210
0
}
211
212
static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
213
7.07k
{
214
7.07k
  char res[PATH_MAX];
215
7.07k
  struct stat buf_a, buf_b;
216
217
7.07k
  assert(h);
218
219
7.07k
  if ((h->mode == DEV_LOCK_NAME) && /* was it name lock */
220
0
      !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
221
0
      !resource_by_name(res, sizeof(res), h->u.name.name, true) && /* acquire lock resource name */
222
0
      !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
223
0
      !stat(res, &buf_b) && /* does path file still exist? */
224
0
      same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
225
    /* coverity[toctou] */
226
0
    if (unlink(res)) /* yes? unlink the file. lgtm[cpp/toctou-race-condition] */
227
0
      log_dbg(cd, "Failed to unlink resource file: %s", res);
228
0
  }
229
230
7.07k
  if ((h->mode == DEV_LOCK_BDEV) && /* was it block device */
231
0
      !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
232
0
      !resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1) && /* acquire lock resource name */
233
0
      !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
234
0
      !stat(res, &buf_b) && /* does path file still exist? */
235
0
      same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
236
    /* coverity[toctou] */
237
0
    if (unlink(res)) /* yes? unlink the file. lgtm[cpp/toctou-race-condition] */
238
0
      log_dbg(cd, "Failed to unlink resource file: %s", res);
239
0
  }
240
241
7.07k
  if (h->mode == DEV_LOCK_NAME)
242
0
    free(h->u.name.name);
243
244
7.07k
  if (close(h->flock_fd))
245
0
    log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
246
7.07k
}
247
248
int device_locked(struct crypt_lock_handle *h)
249
84.7k
{
250
84.7k
  return (h && (h->type == DEV_LOCK_READ || h->type == DEV_LOCK_WRITE));
251
84.7k
}
252
253
int device_locked_readonly(struct crypt_lock_handle *h)
254
12.4k
{
255
12.4k
  return (h && h->type == DEV_LOCK_READ);
256
12.4k
}
257
258
static int verify_lock_handle(struct crypt_lock_handle *h)
259
7.07k
{
260
7.07k
  char res[PATH_MAX];
261
7.07k
  struct stat lck_st, res_st;
262
263
7.07k
  assert(h);
264
265
  /* we locked a regular file, check during device_open() instead. No reason to check now */
266
7.07k
  if (h->mode == DEV_LOCK_FILE)
267
7.07k
    return 0;
268
269
0
  if (h->mode == DEV_LOCK_NAME) {
270
0
    if (resource_by_name(res, sizeof(res), h->u.name.name, true))
271
0
      return -EINVAL;
272
0
  } else if (h->mode == DEV_LOCK_BDEV) {
273
0
    if (resource_by_devno(res, sizeof(res), h->u.bdev.devno, true))
274
0
      return -EINVAL;
275
0
  } else
276
0
    return -EINVAL;
277
278
0
  if (fstat(h->flock_fd, &lck_st))
279
0
    return -EINVAL;
280
281
0
  return (stat(res, &res_st) || !same_inode(lck_st, res_st)) ? -EAGAIN : 0;
282
0
}
283
284
static unsigned device_lock_inc(struct crypt_lock_handle *h)
285
0
{
286
0
  return ++h->refcnt;
287
0
}
288
289
static unsigned device_lock_dec(struct crypt_lock_handle *h)
290
7.07k
{
291
7.07k
  assert(h->refcnt);
292
293
7.07k
  return --h->refcnt;
294
7.07k
}
295
296
static int acquire_and_verify(struct crypt_device *cd, struct device *device, const char *resource, int flock_op, struct crypt_lock_handle **lock)
297
7.07k
{
298
7.07k
  int r;
299
7.07k
  struct crypt_lock_handle *h;
300
301
7.07k
  if (device && resource)
302
0
    return -EINVAL;
303
304
7.07k
  if (!(h = malloc(sizeof(*h))))
305
0
    return -ENOMEM;
306
307
7.07k
  do {
308
7.07k
    r = device ? acquire_lock_handle(cd, device, h) : acquire_lock_handle_by_name(cd, resource, h);
309
7.07k
    if (r < 0)
310
0
      break;
311
312
7.07k
    if (flock(h->flock_fd, flock_op)) {
313
0
      log_dbg(cd, "Flock on fd %d failed with errno %d.", h->flock_fd, errno);
314
0
      r = (errno == EWOULDBLOCK) ? -EBUSY : -EINVAL;
315
0
      release_lock_handle(cd, h);
316
0
      break;
317
0
    }
318
319
7.07k
    log_dbg(cd, "Verifying lock handle for %s.", device ? device_path(device) : resource);
320
321
    /*
322
     * check whether another libcryptsetup process removed resource file before this
323
     * one managed to flock() it. See release_lock_handle() for details
324
     */
325
7.07k
    r = verify_lock_handle(h);
326
7.07k
    if (r < 0) {
327
0
      if (flock(h->flock_fd, LOCK_UN))
328
0
        log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
329
0
      release_lock_handle(cd, h);
330
0
      log_dbg(cd, "Lock handle verification failed.");
331
0
    }
332
7.07k
  } while (r == -EAGAIN);
333
334
7.07k
  if (r < 0) {
335
0
    free(h);
336
0
    return r;
337
0
  }
338
339
7.07k
  *lock = h;
340
341
7.07k
  return 0;
342
7.07k
}
343
344
int device_read_lock_internal(struct crypt_device *cd, struct device *device)
345
5.29k
{
346
5.29k
  int r;
347
5.29k
  struct crypt_lock_handle *h;
348
349
5.29k
  if (!device)
350
0
    return -EINVAL;
351
352
5.29k
  h = device_get_lock_handle(device);
353
354
5.29k
  if (device_locked(h)) {
355
0
    device_lock_inc(h);
356
0
    log_dbg(cd, "Device %s READ lock (or higher) already held.", device_path(device));
357
0
    return 0;
358
0
  }
359
360
5.29k
  log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));
361
362
5.29k
  r = acquire_and_verify(cd, device, NULL, LOCK_SH, &h);
363
5.29k
  if (r < 0)
364
0
    return r;
365
366
5.29k
  h->type = DEV_LOCK_READ;
367
5.29k
  h->refcnt = 1;
368
5.29k
  device_set_lock_handle(device, h);
369
370
5.29k
  log_dbg(cd, "Device %s READ lock taken.", device_path(device));
371
372
5.29k
  return 0;
373
5.29k
}
374
375
int device_write_lock_internal(struct crypt_device *cd, struct device *device)
376
1.77k
{
377
1.77k
  int r;
378
1.77k
  struct crypt_lock_handle *h;
379
380
1.77k
  if (!device)
381
0
    return -EINVAL;
382
383
1.77k
  h = device_get_lock_handle(device);
384
385
1.77k
  if (device_locked(h)) {
386
0
    log_dbg(cd, "Device %s WRITE lock already held.", device_path(device));
387
0
    return device_lock_inc(h);
388
0
  }
389
390
1.77k
  log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
391
392
1.77k
  r = acquire_and_verify(cd, device, NULL, LOCK_EX, &h);
393
1.77k
  if (r < 0)
394
0
    return r;
395
396
1.77k
  h->type = DEV_LOCK_WRITE;
397
1.77k
  h->refcnt = 1;
398
1.77k
  device_set_lock_handle(device, h);
399
400
1.77k
  log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
401
402
1.77k
  return 1;
403
1.77k
}
404
405
int crypt_write_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
406
0
{
407
0
  int r;
408
0
  struct crypt_lock_handle *h;
409
410
0
  if (!resource)
411
0
    return -EINVAL;
412
413
0
  log_dbg(cd, "Acquiring %sblocking write lock for resource %s.", blocking ? "" : "non", resource);
414
415
0
  r = acquire_and_verify(cd, NULL, resource, LOCK_EX | (blocking ? 0 : LOCK_NB), &h);
416
0
  if (r < 0)
417
0
    return r;
418
419
0
  h->type = DEV_LOCK_WRITE;
420
0
  h->refcnt = 1;
421
422
0
  log_dbg(cd, "WRITE lock for resource %s taken.", resource);
423
424
0
  *lock = h;
425
426
0
  return 0;
427
0
}
428
429
static void unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
430
7.07k
{
431
7.07k
  if (flock(h->flock_fd, LOCK_UN))
432
0
    log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
433
7.07k
  release_lock_handle(cd, h);
434
7.07k
  free(h);
435
7.07k
}
436
437
void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
438
0
{
439
0
  if (!h)
440
0
    return;
441
442
  /* nested locks are illegal */
443
0
  assert(!device_lock_dec(h));
444
445
0
  log_dbg(cd, "Unlocking %s lock for resource %s.",
446
0
    device_locked_readonly(h) ? "READ" : "WRITE", h->u.name.name);
447
448
0
  unlock_internal(cd, h);
449
0
}
450
451
void device_unlock_internal(struct crypt_device *cd, struct device *device)
452
7.07k
{
453
7.07k
  bool readonly;
454
7.07k
  struct crypt_lock_handle *h = device_get_lock_handle(device);
455
7.07k
  unsigned u = device_lock_dec(h);
456
457
7.07k
  if (u)
458
0
    return;
459
460
7.07k
  readonly = device_locked_readonly(h);
461
462
7.07k
  unlock_internal(cd, h);
463
464
7.07k
  log_dbg(cd, "Device %s %s lock released.", device_path(device),
465
7.07k
    readonly ? "READ" : "WRITE");
466
467
7.07k
  device_set_lock_handle(device, NULL);
468
7.07k
}
469
470
int device_locked_verify(struct crypt_device *cd, int dev_fd, struct crypt_lock_handle *h)
471
1.77k
{
472
1.77k
  char res[PATH_MAX];
473
1.77k
  struct stat dev_st, lck_st, st;
474
475
1.77k
  if (fstat(dev_fd, &dev_st) || fstat(h->flock_fd, &lck_st))
476
0
    return 1;
477
478
  /* if device handle is regular file the handle must match the lock handle */
479
1.77k
  if (S_ISREG(dev_st.st_mode)) {
480
1.77k
    log_dbg(cd, "Verifying locked device handle (regular file)");
481
1.77k
    if (!same_inode(dev_st, lck_st))
482
0
      return 1;
483
1.77k
  } else if (S_ISBLK(dev_st.st_mode)) {
484
0
    log_dbg(cd, "Verifying locked device handle (bdev)");
485
0
    if (resource_by_devno(res, sizeof(res), dev_st.st_rdev, 1) ||
486
0
        stat(res, &st) ||
487
0
        !same_inode(lck_st, st))
488
0
      return 1;
489
0
  } else
490
0
    return 1;
491
492
1.77k
  return 0;
493
1.77k
}