Files
bacula/vchanger/src/vchanger/src/uuidlookup.c
2023-10-14 17:36:14 +02:00

393 lines
12 KiB
C

/* uuidlookup.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2018 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
* GNU General Public License version 2, as published by the Free
* Software Foundation.
*
* vchanger 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 vchanger. See the file "COPYING". If not,
* write to: The Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "uuidlookup.h"
#include "loghandler.h"
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include <winioctl.h>
#include "win32_util.h"
/*
* Locates disk partition containing file system with volume serial number
* given by 'uuid_str'. If found, and the filesystem is mounted, returns the
* first mountpoint found in 'mountp'. On success, returns zero. On error,
* returns negative value :
* -1 system error
* -2 parameter error
* -3 volume with given uuid not found
* -4 'mountp' buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
HANDLE hnd, hFile;
wchar_t volname[2048];
wchar_t pname[2048];
char tmp[128], *utf8name = NULL;
BOOL rc;
DWORD volser, count, eov, uuid;
size_t n;
/* Convert uuid_str to binary. Note that win32 volume serial number is not
* in standard UUID format, but is the form xxxx-xxxx, where x is a hex digit.
* Win32 returns volume serial number as 32-bit unsigned binary.
*/
mountp[0] = '\0';
strncpy(tmp, uuid_str, sizeof(tmp));
if (strlen(uuid_str) != 9 || tmp[4] != '-') {
/* invalid volume serial number */
return -2;
}
for (n = 4; n < 9; n++) {
tmp[n] = tmp[n+1];
}
uuid = strtoul(tmp, NULL, 16);
/* Enumerate volumes on this system looking for matching volume serial number.
* Skip unmounted volumes.
*/
hnd = FindFirstVolumeW(volname, 2048);
if (hnd == INVALID_HANDLE_VALUE) {
/* out of memory?? permissions problem?? */
return -1;
}
rc = TRUE;
while (rc) {
/* make sure it's a proper volume name */
if (volname[0] == L'\\' && volname[1] == L'\\' && volname[2] == L'?'
&& volname[3] == L'\\' && volname[eov] == L'\\') {
/* Get volume's first mountpoint */
if (GetVolumePathNamesForVolumeNameW(volname, pname, 2048, &count)) {
/* retrieve serial from volume information */
if (GetVolumeInformationW(pname, NULL, 0, &volser, NULL, NULL, NULL, 0)) {
/* see if this volume matches requested UUID */
if (volser == uuid) {
/* found matching volume. UTF16 mountpoint path is in pname. */
break;
}
}
}
}
/* get next volume name */
rc = FindNextVolumeW(hnd, volname, 2048);
}
FindVolumeClose(hnd);
if (!rc) {
return -3; /* volume not found */
}
/* Convert UTF16 mountpoint path to UTF8 */
UTF16ToAnsi(pname, &utf8name, &n);
if (n > mountp_sz) {
free(utf8name);
return -4; /* mountp buffer too small */
}
strncpy(mountp, utf8name, mountp_sz);
free(utf8name);
return 0;
}
#else
#ifdef HAVE_MNTENT_H
#include <mntent.h>
/*
* Lookup mount point for device 'devname' and place in 'mountp'
* using SunOS 4.1.3+ function getmntent().
* On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -4 devname not mounted
* -5 mountp buffer too small
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
size_t n;
FILE *fs;
struct mntent *ent;
int rc;
mountp[0] = '\0';
if (!mountp_sz || !devname || !strlen(devname)) return -2;
/* Read either /proc/mounts or /etc/mtab depending on build system's glibc */
fs = setmntent(_PATH_MOUNTED, "r");
if (fs == NULL) return -1; /* unknown non-POSIX system?? */
rc = -4;
ent = getmntent(fs);
while (ent)
{
if (strcasecmp(devname, ent->mnt_fsname) == 0) {
n = strlen(ent->mnt_dir);
if (n >= mountp_sz) {
rc = -5;
break;
}
memmove(mountp, ent->mnt_dir, n);
mountp[n] = 0;
rc = 0;
break;
}
ent = getmntent(fs);
}
endmntent(fs);
return rc;
}
#else
#ifdef HAVE_GETFSSTAT
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#ifdef HAVE_SYS_UCRED_H
#include <sys/ucred.h>
#endif
/*
* Lookup mount point for device 'devname' and place in 'mountp'
* using BSD4.4+ function getfsstat().
* On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -4 devname not mounted
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
struct statfs *fs;
int mcount, fs_size, n, rc = -4;
mountp[0] = '\0';
if (!devname || !strlen(devname)) return -2;
mcount = getfsstat(NULL, 0, MNT_WAIT);
if (mcount < 1) return -1;
fs_size = (mcount + 1) * sizeof(struct statfs);
fs = (struct statfs*)malloc(fs_size);
if (!fs) return -1;
mcount = getfsstat(fs, fs_size, MNT_NOWAIT);
for (n = 0; n < mcount; n++)
{
if (strcasecmp(devname, fs[n].f_mntfromname) == 0) {
strncpy(mountp, fs[n].f_mntonname, mountp_sz);
rc = 0;
}
}
free(fs);
return rc;
}
#else
/*
* System has neither getmntent() nor getfsstat(). Return system error.
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
LogHandler_write(LOG_ERROR, "build does not support getmntent() or getfsstat() calls");
return -1;
}
#endif /* HAVE_GETFSSTAT */
#endif /* HAVE_MNTENT_H */
#ifdef HAVE_LIBUDEV_H
#include <libudev.h>
/*
* Locates disk partition containing file system with UUID given by 'uuid_str'.
* If found, and the filesystem is mounted, returns the first mountpoint found in
* string 'mountp'. On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -3 filesystem with given uuid not found
* -4 filesystem not mounted
* -5 mountp buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
int rc = -3;
const char *dev_name, *dev_links, *path, *uuid;
size_t n, pos, dev_links_len;
char devlink[4096];
if (!mountp || !mountp_sz) return -2;
if (!uuid_str || !strlen(uuid_str)) return -2;
/* Enumerate devices with a filesystem UUID property */
udev = udev_new();
if (!udev) return -1;
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_property(enumerate, "ID_FS_UUID", uuid_str);
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
/* Find device with specified UUID */
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
uuid = udev_device_get_property_value(dev, "ID_FS_UUID");
if (uuid && strcasecmp(uuid, uuid_str) == 0) {
/* Found device with this UUID, so get its kernel device node */
dev_name = udev_device_get_property_value(dev, "DEVNAME");
if (dev_name == NULL) {
/* Failed to get kernel device node */
LogHandler_write(LOG_DEBUG, "filesystem %s has no udev assigned device node",
uuid_str);
break;
}
LogHandler_write(LOG_DEBUG, "filesystem %s has udev assigned device %s",
uuid_str, dev_name);
/* Lookup mountpoint of the kernel device node */
rc = GetDevMountpoint(mountp, mountp_sz, dev_name);
if (rc == 0) {
/* Found mountpoint */
LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) mounted at %s", uuid_str, dev_name, mountp);
break;
}
if (rc == -4) {
/* If not mounted as the DEVNAME, also check if mounted as
* a device alias name from DEVLINKS */
dev_links = udev_device_get_property_value(dev, "DEVLINKS");
if (dev_links == NULL) {
/* No device alias links found */
break;
}
LogHandler_write(LOG_DEBUG, "device %s not found in system mounts, searching all udev device aliases",
dev_name);
/* For each device alias, look for a mountpoint */
dev_links_len = strlen(dev_links);
pos = 0;
while (rc == -4 && pos < dev_links_len) {
for (n = pos; n < dev_links_len && !isblank(dev_links[n]); n++) ;
n -= pos;
memmove(devlink, dev_links + pos, n);
devlink[n] = 0;
rc = GetDevMountpoint(mountp, mountp_sz, devlink);
if (rc == 0) {
/* Device alias is mounted */
LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) mounted at %s", uuid_str, devlink,
mountp);
break;
}
rc = -4; /* Ignore other errors from attempt to get alias's mountpoint */
pos += n;
while (pos < dev_links_len && isblank(dev_links[pos])) ++pos;
}
}
if (rc == -4) LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) not mounted", uuid_str, dev_name);
break;
}
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return rc;
}
#else
#ifdef HAVE_BLKID_BLKID_H
#include <blkid/blkid.h>
/*
* Locates disk partition containing file system with UUID given by 'uuid_str'.
* If found, and the filesystem is mounted, returns the first mountpoint found in
* string 'mountp'. On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -3 volume with given uuid not found
* -4 volume not mounted
* -5 mountp buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
int rc;
char *dev_name;
if (!mountp || !mountp_sz) return -2;
if (!uuid_str || !strlen(uuid_str)) return -2;
/* Get device with requested UUID from libblkid */
#ifdef HAVE_BLKID_EVALUATE_TAG
dev_name = blkid_evaluate_tag("UUID", uuid_str, NULL);
#else
dev_name = blkid_get_devname(NULL, "UUID", uuid_str);
#endif
if (!dev_name) {
LogHandler_write(LOG_DEBUG, "filesystem %s not found", uuid_str);
return -3;
}
LogHandler_write(LOG_DEBUG, "libblkid found filesystem %s at device %s", uuid_str, dev_name);
/* find mount point for device */
rc = GetDevMountpoint(mountp, mountp_sz, dev_name);
free(dev_name);
return rc;
}
#else
/*
* If built without libudev or libblkid support, then UUID lookup is a system error
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
LogHandler_write(LOG_DEBUG, "GetMountpointFromUUID: UUID lookups not supported by this build");
return -1;
}
#endif /* HAVE_BLKID_BLKID_H */
#endif /* HAVE_LIBUDEV_H */
#endif /* HAVE_WINDOWS_H */