Imported Upstream version 2.3.3
[platform/upstream/cryptsetup.git] / lib / utils_devpath.c
index 963785a..b6c8975 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * devname - search for device name
  *
- * Copyright (C) 2004, Jana Saout <jana@saout.de>
- * Copyright (C) 2004-2007, Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2013, Milan Broz
+ * Copyright (C) 2004 Jana Saout <jana@saout.de>
+ * Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2009-2020 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2020 Milan Broz
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <stdlib.h>
 #include <unistd.h>
 #include <dirent.h>
-#include <fcntl.h>
 #include <errno.h>
 #include <limits.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h>     /* for major, minor */
+#endif
 #include "internal.h"
 
 static char *__lookup_dev(char *path, dev_t dev, int dir_level, const int max_level)
@@ -108,7 +110,7 @@ static char *lookup_dev_old(int major, int minor)
                return result;
 
        /* If it is dm, try DM dir  */
-       if (dm_is_dm_device(major, minor)) {
+       if (dm_is_dm_device(major)) {
                strncpy(buf, dm_get_dir(), PATH_MAX);
                if ((result = __lookup_dev(buf, dev, 0, 0)))
                        return result;
@@ -276,24 +278,30 @@ char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t
                major(st.st_rdev), minor(st.st_rdev)) < 0)
                return NULL;
 
+       dir = opendir(path);
+       if (!dir)
+               return NULL;
+
        len = readlink(path, link, sizeof(link) - 1);
-       if (len < 0)
+       if (len < 0) {
+               closedir(dir);
                return NULL;
+       }
 
        /* Get top level disk name for sysfs search */
        link[len] = '\0';
        devname = strrchr(link, '/');
-       if (!devname)
+       if (!devname) {
+               closedir(dir);
                return NULL;
+       }
        devname++;
 
        /* DM devices do not use kernel partitions. */
-       if (dm_is_dm_kernel_name(devname))
-               return NULL;
-
-       dir = opendir(path);
-       if (!dir)
+       if (dm_is_dm_kernel_name(devname)) {
+               closedir(dir);
                return NULL;
+       }
 
        devname_len = strlen(devname);
        while((entry = readdir(dir))) {
@@ -362,3 +370,88 @@ char *crypt_get_base_device(const char *dev_path)
        snprintf(part_path, sizeof(part_path), "/dev/%s", devname);
        return strdup(part_path);
 }
+
+int lookup_by_disk_id(const char *dm_uuid)
+{
+       struct dirent *entry;
+       struct stat st;
+       int r = 0; /* not found */
+       DIR *dir = opendir("/dev/disk/by-id");
+
+       if (!dir)
+               /* map ENOTDIR to ENOENT we'll handle both errors same */
+               return errno == ENOTDIR ? -ENOENT : -errno;
+
+       while ((entry = readdir(dir))) {
+               if (entry->d_name[0] == '.' ||
+                   !strncmp(entry->d_name, "..", 2))
+                       continue;
+
+               if (fstatat(dirfd(dir), entry->d_name, &st, AT_SYMLINK_NOFOLLOW)) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+                       continue;
+
+               if (!strncmp(entry->d_name, dm_uuid, strlen(dm_uuid))) {
+                       r = 1;
+                       break;
+               }
+       }
+
+       closedir(dir);
+
+       return r;
+}
+
+int lookup_by_sysfs_uuid_field(const char *dm_uuid, size_t max_len)
+{
+       struct dirent *entry;
+       char subpath[PATH_MAX], uuid[max_len];
+       ssize_t s;
+       struct stat st;
+       int fd, len, r = 0; /* not found */
+       DIR *dir = opendir("/sys/block/");
+
+       if (!dir)
+               /* map ENOTDIR to ENOENT we'll handle both errors same */
+               return errno == ENOTDIR ? -ENOENT : -errno;
+
+       while (r != 1 && (entry = readdir(dir))) {
+               if (entry->d_name[0] == '.' ||
+                   !strncmp(entry->d_name, "..", 2))
+                       continue;
+
+               len = snprintf(subpath, PATH_MAX, "%s/%s", entry->d_name, "dm/uuid");
+               if (len < 0 || len >= PATH_MAX) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               /* looking for dm-X/dm/uuid file, symlinks are fine */
+               fd = openat(dirfd(dir), subpath, O_RDONLY | O_CLOEXEC);
+               if (fd < 0)
+                       continue;
+
+               if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
+                       close(fd);
+                       continue;
+               }
+
+               /* reads binary data */
+               s = read_buffer(fd, uuid, max_len - 1);
+               if (s > 0) {
+                       uuid[s] = '\0';
+                       if (!strncmp(uuid, dm_uuid, strlen(dm_uuid)))
+                               r = 1;
+               }
+
+               close(fd);
+       }
+
+       closedir(dir);
+
+       return r;
+}