utils_device_locking.c 13.1 KB
Newer Older
1 2 3
/*
 * Metadata on-disk locking for processes serialization
 *
Milan Broz's avatar
Milan Broz committed
4 5
 * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved.
 * Copyright (C) 2016-2019 Ondrej Kozina
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>     /* for major, minor */
#endif
#include <libgen.h>
36
#include <assert.h>
37 38 39 40 41 42 43 44 45 46 47 48 49

#include "internal.h"
#include "utils_device_locking.h"

#define same_inode(buf1, buf2) \
	((buf1).st_ino == (buf2).st_ino && \
	 (buf1).st_dev == (buf2).st_dev)

enum lock_type {
	DEV_LOCK_READ = 0,
	DEV_LOCK_WRITE
};

50 51
enum lock_mode {
	DEV_LOCK_FILE = 0,
52 53
	DEV_LOCK_BDEV,
	DEV_LOCK_NAME
54 55
};

56
struct crypt_lock_handle {
57
	unsigned refcnt;
58 59
	int flock_fd;
	enum lock_type type;
60 61 62 63 64
	enum lock_mode mode;
	union {
	struct {
		dev_t devno;
	} bdev;
65 66 67
	struct {
		char *name;
	} name;
68
	} u;
69 70
};

71 72 73 74 75 76 77 78 79 80 81 82
static int resource_by_name(char *res, size_t res_size, const char *name, bool fullpath)
{
	int r;

	if (fullpath)
		r = snprintf(res, res_size, "%s/LN_%s", DEFAULT_LUKS2_LOCK_PATH, name);
	else
		r = snprintf(res, res_size, "LN_%s", name);

	return (r < 0 || (size_t)r >= res_size) ? -EINVAL : 0;
}

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
static int resource_by_devno(char *res, size_t res_size, dev_t devno, unsigned fullpath)
{
	int r;

	if (fullpath)
		r = snprintf(res, res_size, "%s/L_%d:%d", DEFAULT_LUKS2_LOCK_PATH, major(devno), minor(devno));
	else
		r = snprintf(res, res_size, "L_%d:%d", major(devno), minor(devno));

	return (r < 0 || (size_t)r >= res_size) ? -EINVAL : 0;
}

static int open_lock_dir(struct crypt_device *cd, const char *dir, const char *base)
{
	int dirfd, lockdfd;

99
	dirfd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
100
	if (dirfd < 0) {
101
		log_dbg(cd, "Failed to open directory %s: (%d: %s).", dir, errno, strerror(errno));
102
		if (errno == ENOTDIR || errno == ENOENT)
103
			log_err(cd, _("Locking aborted. The locking path %s/%s is unusable (not a directory or missing)."), dir, base);
104 105 106 107 108 109 110 111 112 113
		return -EINVAL;
	}

	lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
	if (lockdfd < 0) {
		if (errno == ENOENT) {
			log_std(cd, _("WARNING: Locking directory %s/%s is missing!\n"), dir, base);

			/* success or failure w/ errno == EEXIST either way just try to open the 'base' directory again */
			if (mkdirat(dirfd, base, DEFAULT_LUKS2_LOCK_DIR_PERMS) && errno != EEXIST)
114
				log_dbg(cd, "Failed to create directory %s in %s (%d: %s).", base, dir, errno, strerror(errno));
115 116
			else
				lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
117
		} else {
118
			log_dbg(cd, "Failed to open directory %s/%s: (%d: %s)", dir, base, errno, strerror(errno));
119
			if (errno == ENOTDIR || errno == ELOOP)
120
				log_err(cd, _("Locking aborted. The locking path %s/%s is unusable (%s is not a directory)."), dir, base, base);
121
		}
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	}

	close(dirfd);
	return lockdfd >= 0 ? lockdfd : -EINVAL;
}

static int open_resource(struct crypt_device *cd, const char *res)
{
	int err, lockdir_fd, r;
	char dir[] = DEFAULT_LUKS2_LOCK_PATH,
	     base[] = DEFAULT_LUKS2_LOCK_PATH;

	lockdir_fd = open_lock_dir(cd, dirname(dir), basename(base));
	if (lockdir_fd < 0)
		return -EINVAL;

138
	log_dbg(cd, "Opening lock resource file %s/%s", DEFAULT_LUKS2_LOCK_PATH, res);
139 140 141 142 143 144 145 146
	r = openat(lockdir_fd, res, O_CREAT | O_NOFOLLOW | O_RDWR | O_CLOEXEC, 0777);
	err = errno;

	close(lockdir_fd);

	return r < 0 ? -err : r;
}

Ondrej Kozina's avatar
Ondrej Kozina committed
147
static int acquire_lock_handle(struct crypt_device *cd, struct device *device, struct crypt_lock_handle *h)
148 149 150 151 152
{
	char res[PATH_MAX];
	int dev_fd, fd;
	struct stat st;

Ondrej Kozina's avatar
Ondrej Kozina committed
153
	dev_fd = open(device_path(device), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
	if (dev_fd < 0)
		return -EINVAL;

	if (fstat(dev_fd, &st)) {
		close(dev_fd);
		return -EINVAL;
	}

	if (S_ISBLK(st.st_mode)) {
		if (resource_by_devno(res, sizeof(res), st.st_rdev, 0)) {
			close(dev_fd);
			return -EINVAL;
		}

		fd = open_resource(cd, res);
		close(dev_fd);
		if (fd < 0)
			return fd;

		h->flock_fd = fd;
174 175
		h->u.bdev.devno = st.st_rdev;
		h->mode = DEV_LOCK_BDEV;
176 177
	} else if (S_ISREG(st.st_mode)) {
		// FIXME: workaround for nfsv4
Ondrej Kozina's avatar
Ondrej Kozina committed
178
		fd = open(device_path(device), O_RDWR | O_NONBLOCK | O_CLOEXEC);
179 180 181 182 183 184
		if (fd < 0)
			h->flock_fd = dev_fd;
		else {
			h->flock_fd = fd;
			close(dev_fd);
		}
185
		h->mode = DEV_LOCK_FILE;
186 187 188 189 190 191 192 193 194
	} else {
		/* Wrong device type */
		close(dev_fd);
		return -EINVAL;
	}

	return 0;
}

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
static int acquire_lock_handle_by_name(struct crypt_device *cd, const char *name, struct crypt_lock_handle *h)
{
	char res[PATH_MAX];
	int fd;

	h->u.name.name = strdup(name);
	if (!h->u.name.name)
		return -ENOMEM;

	if (resource_by_name(res, sizeof(res), name, false)) {
		free(h->u.name.name);
		return -EINVAL;
	}

	fd = open_resource(cd, res);
	if (fd < 0) {
		free(h->u.name.name);
		return fd;
	}

	h->flock_fd = fd;
	h->mode = DEV_LOCK_NAME;

	return 0;
}

221
static void release_lock_handle(struct crypt_device *cd, struct crypt_lock_handle *h)
222 223 224 225
{
	char res[PATH_MAX];
	struct stat buf_a, buf_b;

226 227 228 229 230 231 232 233 234 235 236
	if ((h->mode == DEV_LOCK_NAME) && /* was it name lock */
	    !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
	    !resource_by_name(res, sizeof(res), h->u.name.name, true) && /* acquire lock resource name */
	    !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
	    !stat(res, &buf_b) && /* does path file still exist? */
	    same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
		/* coverity[toctou] */
		if (unlink(res)) /* yes? unlink the file */
			log_dbg(cd, "Failed to unlink resource file: %s", res);
	}

237
	if ((h->mode == DEV_LOCK_BDEV) && /* was it block device */
238
	    !flock(h->flock_fd, LOCK_EX | LOCK_NB) && /* lock to drop the file */
239
	    !resource_by_devno(res, sizeof(res), h->u.bdev.devno, 1) && /* acquire lock resource name */
Andrea Gelmini's avatar
Andrea Gelmini committed
240
	    !fstat(h->flock_fd, &buf_a) && /* read inode id referred by fd */
Rafael Fontenelle's avatar
Rafael Fontenelle committed
241
	    !stat(res, &buf_b) && /* does path file still exist? */
242 243
	    same_inode(buf_a, buf_b)) { /* is it same id as the one referenced by fd? */
		/* coverity[toctou] */
244
		if (unlink(res)) /* yes? unlink the file */
245
			log_dbg(cd, "Failed to unlink resource file: %s", res);
246
	}
247

248 249 250
	if (h->mode == DEV_LOCK_NAME)
		free(h->u.name.name);

251
	if (close(h->flock_fd))
252
		log_dbg(cd, "Failed to close lock resource fd (%d).", h->flock_fd);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
}

int device_locked(struct crypt_lock_handle *h)
{
	return (h && (h->type == DEV_LOCK_READ || h->type == DEV_LOCK_WRITE));
}

int device_locked_readonly(struct crypt_lock_handle *h)
{
	return (h && h->type == DEV_LOCK_READ);
}

static int verify_lock_handle(const char *device_path, struct crypt_lock_handle *h)
{
	char res[PATH_MAX];
	struct stat lck_st, res_st;

	/* we locked a regular file, check during device_open() instead. No reason to check now */
271
	if (h->mode == DEV_LOCK_FILE)
272 273
		return 0;

274 275 276 277 278 279 280
	if (h->mode == DEV_LOCK_NAME) {
		if (resource_by_name(res, sizeof(res), h->u.name.name, true))
			return -EINVAL;
	} else if (h->mode == DEV_LOCK_BDEV) {
		if (resource_by_devno(res, sizeof(res), h->u.bdev.devno, true))
			return -EINVAL;
	} else
281 282 283 284 285 286 287 288
		return -EINVAL;

	if (fstat(h->flock_fd, &lck_st))
		return -EINVAL;

	return (stat(res, &res_st) || !same_inode(lck_st, res_st)) ? -EAGAIN : 0;
}

289 290 291 292 293 294 295 296 297 298 299 300
static void device_lock_inc(struct crypt_lock_handle *h)
{
	h->refcnt++;
}

static unsigned device_lock_dec(struct crypt_lock_handle *h)
{
	assert(h->refcnt);

	return --h->refcnt;
}

301
static int acquire_and_verify(struct crypt_device *cd, struct device *device, const char *resource, int flock_op, struct crypt_lock_handle **lock)
302 303
{
	int r;
304
	struct crypt_lock_handle *h;
305

306 307 308
	if (device && resource)
		return -EINVAL;

309
	if (!(h = malloc(sizeof(*h))))
310
		return -ENOMEM;
311 312

	do {
313
		r = device ? acquire_lock_handle(cd, device, h) : acquire_lock_handle_by_name(cd, resource, h);
314 315 316
		if (r)
			break;

Ondrej Kozina's avatar
Ondrej Kozina committed
317 318
		if (flock(h->flock_fd, flock_op)) {
			log_dbg(cd, "Flock on fd %d failed with errno %d.", h->flock_fd, errno);
319
			r = (errno == EWOULDBLOCK) ? -EBUSY : -EINVAL;
320
			release_lock_handle(cd, h);
321 322 323
			break;
		}

324
		log_dbg(cd, "Verifying lock handle for %s.", device ? device_path(device) : resource);
325 326 327 328 329

		/*
		 * check whether another libcryptsetup process removed resource file before this
		 * one managed to flock() it. See release_lock_handle() for details
		 */
330
		r = verify_lock_handle(device_path(device), h);
331
		if (r) {
332 333
			if (flock(h->flock_fd, LOCK_UN))
				log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
334
			release_lock_handle(cd, h);
Ondrej Kozina's avatar
Ondrej Kozina committed
335
			log_dbg(cd, "Lock handle verification failed.");
336 337 338 339 340
		}
	} while (r == -EAGAIN);

	if (r) {
		free(h);
341
		return -EINVAL;
342 343
	}

Ondrej Kozina's avatar
Ondrej Kozina committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
	*lock = h;

	return 0;
}

int device_read_lock_internal(struct crypt_device *cd, struct device *device)
{
	int r;
	struct crypt_lock_handle *h;

	if (!device)
		return -EINVAL;

	h = device_get_lock_handle(device);

	if (device_locked(h)) {
		device_lock_inc(h);
		log_dbg(cd, "Device %s READ lock (or higher) already held.", device_path(device));
		return 0;
	}

	log_dbg(cd, "Acquiring read lock for device %s.", device_path(device));

367
	r = acquire_and_verify(cd, device, NULL, LOCK_SH, &h);
Ondrej Kozina's avatar
Ondrej Kozina committed
368 369 370
	if (r < 0)
		return r;

371
	h->type = DEV_LOCK_READ;
372 373
	h->refcnt = 1;
	device_set_lock_handle(device, h);
374

375 376 377
	log_dbg(cd, "Device %s READ lock taken.", device_path(device));

	return 0;
378 379
}

380
int device_write_lock_internal(struct crypt_device *cd, struct device *device)
381 382
{
	int r;
383 384 385 386
	struct crypt_lock_handle *h;

	if (!device)
		return -EINVAL;
387

388 389 390 391 392 393 394 395
	h = device_get_lock_handle(device);

	if (device_locked(h)) {
		device_lock_inc(h);
		log_dbg(cd, "Device %s WRITE lock already held.", device_path(device));
		return 0;
	}

Ondrej Kozina's avatar
Ondrej Kozina committed
396
	log_dbg(cd, "Acquiring write lock for device %s.", device_path(device));
397

398
	r = acquire_and_verify(cd, device, NULL, LOCK_EX, &h);
Ondrej Kozina's avatar
Ondrej Kozina committed
399
	if (r < 0)
400
		return r;
401 402

	h->type = DEV_LOCK_WRITE;
403 404 405 406
	h->refcnt = 1;
	device_set_lock_handle(device, h);

	log_dbg(cd, "Device %s WRITE lock taken.", device_path(device));
407

408
	return 0;
409 410
}

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
int crypt_read_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
{
	int r;
	struct crypt_lock_handle *h;

	if (!resource)
		return -EINVAL;

	log_dbg(cd, "Acquiring %sblocking read lock for resource %s.", blocking ? "" : "non", resource);

	r = acquire_and_verify(cd, NULL, resource, LOCK_SH | (blocking ? 0 : LOCK_NB), &h);
	if (r < 0)
		return r;

	h->type = DEV_LOCK_READ;
	h->refcnt = 1;

	log_dbg(cd, "READ lock for resource %s taken.", resource);

	*lock = h;

	return 0;
}

int crypt_write_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
{
	int r;
	struct crypt_lock_handle *h;

	if (!resource)
		return -EINVAL;

	log_dbg(cd, "Acquiring %sblocking write lock for resource %s.", blocking ? "" : "non", resource);

	r = acquire_and_verify(cd, NULL, resource, LOCK_EX | (blocking ? 0 : LOCK_NB), &h);
	if (r < 0)
		return r;

	h->type = DEV_LOCK_WRITE;
	h->refcnt = 1;

	log_dbg(cd, "WRITE lock for resource %s taken.", resource);

	*lock = h;

	return 0;
}

static void unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
{
	if (flock(h->flock_fd, LOCK_UN))
		log_dbg(cd, "flock on fd %d failed.", h->flock_fd);
	release_lock_handle(cd, h);
	free(h);
}

void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h)
{
	if (!h)
		return;

	/* nested locks are illegal */
	assert(!device_lock_dec(h));

	log_dbg(cd, "Unlocking %s lock for resource %s.",
		device_locked_readonly(h) ? "READ" : "WRITE", h->u.name.name);

	unlock_internal(cd, h);
}

481
void device_unlock_internal(struct crypt_device *cd, struct device *device)
482
{
483
	bool readonly;
484 485 486 487 488 489
	struct crypt_lock_handle *h = device_get_lock_handle(device);
	unsigned u = device_lock_dec(h);

	if (u)
		return;

490
	readonly = device_locked_readonly(h);
491

492
	unlock_internal(cd, h);
493

494
	log_dbg(cd, "Device %s %s lock released.", device_path(device),
495
		readonly ? "READ" : "WRITE");
496 497

	device_set_lock_handle(device, NULL);
498 499
}

500
int device_locked_verify(struct crypt_device *cd, int dev_fd, struct crypt_lock_handle *h)
501 502 503 504 505 506 507 508 509
{
	char res[PATH_MAX];
	struct stat dev_st, lck_st, st;

	if (fstat(dev_fd, &dev_st) || fstat(h->flock_fd, &lck_st))
		return 1;

	/* if device handle is regular file the handle must match the lock handle */
	if (S_ISREG(dev_st.st_mode)) {
510
		log_dbg(cd, "Veryfing locked device handle (regular file)");
511 512 513
		if (!same_inode(dev_st, lck_st))
			return 1;
	} else if (S_ISBLK(dev_st.st_mode)) {
514
		log_dbg(cd, "Veryfing locked device handle (bdev)");
515 516 517 518 519 520 521 522 523
		if (resource_by_devno(res, sizeof(res), dev_st.st_rdev, 1) ||
		    stat(res, &st) ||
		    !same_inode(lck_st, st))
			return 1;
	} else
		return 1;

	return 0;
}