393 lines
12 KiB
C
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 */
|