"Fossies" - the Fresh Open Source Software Archive

Member "cryptsetup-2.4.3/lib/utils_device_locking.c" (13 Jan 2022, 13385 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "utils_device_locking.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.4.1_vs_2.4.2.

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