Coverage Report

Created: 2026-01-07 06:10

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-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2016-2025 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
10.7k
  ((buf1).st_ino == (buf2).st_ino && \
28
10.7k
   (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
10.7k
{
134
10.7k
  char res[PATH_MAX];
135
10.7k
  int dev_fd, fd;
136
10.7k
  struct stat st;
137
138
10.7k
  dev_fd = open(device_path(device), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
139
10.7k
  if (dev_fd < 0)
140
0
    return -EINVAL;
141
142
10.7k
  if (fstat(dev_fd, &st)) {
143
0
    close(dev_fd);
144
0
    return -EINVAL;
145
0
  }
146
147
10.7k
  if (S_ISBLK(st.st_mode)) {
148
0
    if (resource_by_devno(res, sizeof(res), st.st_rdev, 0)) {
149
0
      close(dev_fd);
150
0
      return -EINVAL;
151
0
    }
152
153
0
    fd = open_resource(cd, res);
154
0
    close(dev_fd);
155
0
    if (fd < 0)
156
0
      return fd;
157
158
0
    h->flock_fd = fd;
159
0
    h->u.bdev.devno = st.st_rdev;
160
0
    h->mode = DEV_LOCK_BDEV;
161
10.7k
  } else if (S_ISREG(st.st_mode)) {
162
    /* workaround for nfsv4 */
163
10.7k
    fd = open(device_path(device), O_RDWR | O_NONBLOCK | O_CLOEXEC);
164
10.7k
    if (fd < 0)
165
0
      h->flock_fd = dev_fd;
166
10.7k
    else {
167
10.7k
      h->flock_fd = fd;
168
10.7k
      close(dev_fd);
169
10.7k
    }
170
10.7k
    h->mode = DEV_LOCK_FILE;
171
10.7k
  } else {
172
    /* Wrong device type */
173
0
    close(dev_fd);
174
0
    return -EINVAL;
175
0
  }
176
177
10.7k
  return 0;
178
10.7k
}
179
180
static int acquire_lock_handle_by_name(struct crypt_device *cd, const char *name, struct crypt_lock_handle *h)
181
0
{
182
0
  char res[PATH_MAX];
183
0
  int fd;
184
185
0
  h->u.name.name = strdup(name);
186
0
  if (!h->u.name.name)
187
0
    return -ENOMEM;
188
189
0
  if (resource_by_name(res, sizeof(res), name, false)) {
190
0
    free(h->u.name.name);
191
0
    return -EINVAL;
192
0
  }
193
194
0
  fd = open_resource(cd, res);
195
0
  if (fd < 0) {
196
0
    free(h->u.name.name);
197
0
    return fd;
198
0
  }
199
200
0
  h->flock_fd = fd;
201
0
  h->mode = DEV_LOCK_NAME;
202
203
0
  return 0;
204
0
}
205
206
static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
207
10.7k
{
208
10.7k
  char res[PATH_MAX];
209
10.7k
  struct stat buf_a, buf_b;
210
211
10.7k
  if ((h->mode == DEV_LOCK_NAME) && /* was it name lock */
212
0
      !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
213
0
      !resource_by_name(res, sizeof(res), h->u.name.name, true) && /* acquire lock resource name */
214
0
      !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
215
0
      !stat(res, &buf_b) && /* does path file still exist? */
216
0
      same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
217
    /* coverity[toctou] */
218
0
    if (unlink(res)) /* yes? unlink the file. lgtm[cpp/toctou-race-condition] */
219
0
      log_dbg(cd, "Failed to unlink resource file: %s", res);
220
0
  }
221
222
10.7k
  if ((h->mode == DEV_LOCK_BDEV) && /* was it block device */
223
0
      !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
224
0
      !resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1) && /* acquire lock resource name */
225
0
      !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
226
0
      !stat(res, &buf_b) && /* does path file still exist? */
227
0
      same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
228
    /* coverity[toctou] */
229
0
    if (unlink(res)) /* yes? unlink the file. lgtm[cpp/toctou-race-condition] */
230
0
      log_dbg(cd, "Failed to unlink resource file: %s", res);
231
0
  }
232
233
10.7k
  if (h->mode == DEV_LOCK_NAME)
234
0
    free(h->u.name.name);
235
236
10.7k
  if (close(h->flock_fd))
237
0
    log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
238
10.7k
}
239
240
int device_locked(struct crypt_lock_handle *h)
241
100k
{
242
100k
  return (h && (h->type == DEV_LOCK_READ || h->type == DEV_LOCK_WRITE));
243
100k
}
244
245
int device_locked_readonly(struct crypt_lock_handle *h)
246
17.3k
{
247
17.3k
  return (h && h->type == DEV_LOCK_READ);
248
17.3k
}
249
250
static int verify_lock_handle(struct crypt_lock_handle *h)
251
10.7k
{
252
10.7k
  char res[PATH_MAX];
253
10.7k
  struct stat lck_st, res_st;
254
255
  /* we locked a regular file, check during device_open() instead. No reason to check now */
256
10.7k
  if (h->mode == DEV_LOCK_FILE)
257
10.7k
    return 0;
258
259
0
  if (h->mode == DEV_LOCK_NAME) {
260
0
    if (resource_by_name(res, sizeof(res), h->u.name.name, true))
261
0
      return -EINVAL;
262
0
  } else if (h->mode == DEV_LOCK_BDEV) {
263
0
    if (resource_by_devno(res, sizeof(res), h->u.bdev.devno, true))
264
0
      return -EINVAL;
265
0
  } else
266
0
    return -EINVAL;
267
268
0
  if (fstat(h->flock_fd, &lck_st))
269
0
    return -EINVAL;
270
271
0
  return (stat(res, &res_st) || !same_inode(lck_st, res_st)) ? -EAGAIN : 0;
272
0
}
273
274
static unsigned device_lock_inc(struct crypt_lock_handle *h)
275
0
{
276
0
  return ++h->refcnt;
277
0
}
278
279
static unsigned device_lock_dec(struct crypt_lock_handle *h)
280
10.7k
{
281
10.7k
  assert(h->refcnt);
282
283
10.7k
  return --h->refcnt;
284
10.7k
}
285
286
static int acquire_and_verify(struct crypt_device *cd, struct device *device, const char *resource, int flock_op, struct crypt_lock_handle **lock)
287
10.7k
{
288
10.7k
  int r;
289
10.7k
  struct crypt_lock_handle *h;
290
291
10.7k
  if (device && resource)
292
0
    return -EINVAL;
293
294
10.7k
  if (!(h = malloc(sizeof(*h))))
295
0
    return -ENOMEM;
296
297
10.7k
  do {
298
10.7k
    r = device ? acquire_lock_handle(cd, device, h) : acquire_lock_handle_by_name(cd, resource, h);
299
10.7k
    if (r < 0)
300
0
      break;
301
302
10.7k
    if (flock(h->flock_fd, flock_op)) {
303
0
      log_dbg(cd, "Flock on fd %d failed with errno %d.", h->flock_fd, errno);
304
0
      r = (errno == EWOULDBLOCK) ? -EBUSY : -EINVAL;
305
0
      release_lock_handle(cd, h);
306
0
      break;
307
0
    }
308
309
10.7k
    log_dbg(cd, "Verifying lock handle for %s.", device ? device_path(device) : resource);
310
311
    /*
312
     * check whether another libcryptsetup process removed resource file before this
313
     * one managed to flock() it. See release_lock_handle() for details
314
     */
315
10.7k
    r = verify_lock_handle(h);
316
10.7k
    if (r < 0) {
317
0
      if (flock(h->flock_fd, LOCK_UN))
318
0
        log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
319
0
      release_lock_handle(cd, h);
320
0
      log_dbg(cd, "Lock handle verification failed.");
321
0
    }
322
10.7k
  } while (r == -EAGAIN);
323
324
10.7k
  if (r < 0) {
325
0
    free(h);
326
0
    return r;
327
0
  }
328
329
10.7k
  *lock = h;
330
331
10.7k
  return 0;
332
10.7k
}
333
334
int device_read_lock_internal(struct crypt_device *cd, struct device *device)
335
8.57k
{
336
8.57k
  int r;
337
8.57k
  struct crypt_lock_handle *h;
338
339
8.57k
  if (!device)
340
0
    return -EINVAL;
341
342
8.57k
  h = device_get_lock_handle(device);
343
344
8.57k
  if (device_locked(h)) {
345
0
    device_lock_inc(h);
346
0
    log_dbg(cd, "Device %s READ lock (or higher) already held.", device_path(device));
347
0
    return 0;
348
0
  }
349
350
8.57k
  log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));
351
352
8.57k
  r = acquire_and_verify(cd, device, NULL, LOCK_SH, &h);
353
8.57k
  if (r < 0)
354
0
    return r;
355
356
8.57k
  h->type = DEV_LOCK_READ;
357
8.57k
  h->refcnt = 1;
358
8.57k
  device_set_lock_handle(device, h);
359
360
8.57k
  log_dbg(cd, "Device %s READ lock taken.", device_path(device));
361
362
8.57k
  return 0;
363
8.57k
}
364
365
int device_write_lock_internal(struct crypt_device *cd, struct device *device)
366
2.19k
{
367
2.19k
  int r;
368
2.19k
  struct crypt_lock_handle *h;
369
370
2.19k
  if (!device)
371
0
    return -EINVAL;
372
373
2.19k
  h = device_get_lock_handle(device);
374
375
2.19k
  if (device_locked(h)) {
376
0
    log_dbg(cd, "Device %s WRITE lock already held.", device_path(device));
377
0
    return device_lock_inc(h);
378
0
  }
379
380
2.19k
  log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
381
382
2.19k
  r = acquire_and_verify(cd, device, NULL, LOCK_EX, &h);
383
2.19k
  if (r < 0)
384
0
    return r;
385
386
2.19k
  h->type = DEV_LOCK_WRITE;
387
2.19k
  h->refcnt = 1;
388
2.19k
  device_set_lock_handle(device, h);
389
390
2.19k
  log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
391
392
2.19k
  return 1;
393
2.19k
}
394
395
int crypt_write_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
396
0
{
397
0
  int r;
398
0
  struct crypt_lock_handle *h;
399
400
0
  if (!resource)
401
0
    return -EINVAL;
402
403
0
  log_dbg(cd, "Acquiring %sblocking write lock for resource %s.", blocking ? "" : "non", resource);
404
405
0
  r = acquire_and_verify(cd, NULL, resource, LOCK_EX | (blocking ? 0 : LOCK_NB), &h);
406
0
  if (r < 0)
407
0
    return r;
408
409
0
  h->type = DEV_LOCK_WRITE;
410
0
  h->refcnt = 1;
411
412
0
  log_dbg(cd, "WRITE lock for resource %s taken.", resource);
413
414
0
  *lock = h;
415
416
0
  return 0;
417
0
}
418
419
static void unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
420
10.7k
{
421
10.7k
  if (flock(h->flock_fd, LOCK_UN))
422
0
    log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
423
10.7k
  release_lock_handle(cd, h);
424
10.7k
  free(h);
425
10.7k
}
426
427
void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
428
0
{
429
0
  if (!h)
430
0
    return;
431
432
  /* nested locks are illegal */
433
0
  assert(!device_lock_dec(h));
434
435
0
  log_dbg(cd, "Unlocking %s lock for resource %s.",
436
0
    device_locked_readonly(h) ? "READ" : "WRITE", h->u.name.name);
437
438
0
  unlock_internal(cd, h);
439
0
}
440
441
void device_unlock_internal(struct crypt_device *cd, struct device *device)
442
10.7k
{
443
10.7k
  bool readonly;
444
10.7k
  struct crypt_lock_handle *h = device_get_lock_handle(device);
445
10.7k
  unsigned u = device_lock_dec(h);
446
447
10.7k
  if (u)
448
0
    return;
449
450
10.7k
  readonly = device_locked_readonly(h);
451
452
10.7k
  unlock_internal(cd, h);
453
454
10.7k
  log_dbg(cd, "Device %s %s lock released.", device_path(device),
455
10.7k
    readonly ? "READ" : "WRITE");
456
457
10.7k
  device_set_lock_handle(device, NULL);
458
10.7k
}
459
460
int device_locked_verify(struct crypt_device *cd, int dev_fd, struct crypt_lock_handle *h)
461
10.7k
{
462
10.7k
  char res[PATH_MAX];
463
10.7k
  struct stat dev_st, lck_st, st;
464
465
10.7k
  if (fstat(dev_fd, &dev_st) || fstat(h->flock_fd, &lck_st))
466
0
    return 1;
467
468
  /* if device handle is regular file the handle must match the lock handle */
469
10.7k
  if (S_ISREG(dev_st.st_mode)) {
470
10.7k
    log_dbg(cd, "Verifying locked device handle (regular file)");
471
10.7k
    if (!same_inode(dev_st, lck_st))
472
0
      return 1;
473
10.7k
  } else if (S_ISBLK(dev_st.st_mode)) {
474
0
    log_dbg(cd, "Verifying locked device handle (bdev)");
475
0
    if (resource_by_devno(res, sizeof(res), dev_st.st_rdev, 1) ||
476
0
        stat(res, &st) ||
477
0
        !same_inode(lck_st, st))
478
0
      return 1;
479
0
  } else
480
0
    return 1;
481
482
10.7k
  return 0;
483
10.7k
}