mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
443 lines
10 KiB
C
443 lines
10 KiB
C
/*
|
|
* zdev - Modify and display the persistent configuration of devices
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "blkinfo.h"
|
|
#include "devnode.h"
|
|
#include "misc.h"
|
|
|
|
#define LSBLK_CMDLINE "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINT,PKNAME 2>/dev/null"
|
|
|
|
struct blkinfo {
|
|
struct devnode *devnode;
|
|
char *fstype;
|
|
char *uuid;
|
|
char *mountpoint;
|
|
char *parent;
|
|
};
|
|
|
|
static struct util_list *cached_blkinfos;
|
|
|
|
static struct blkinfo *blkinfo_new(const char *name, const char *majmin,
|
|
const char *fstype, const char *uuid,
|
|
const char *mountpoint, const char *parent)
|
|
{
|
|
struct blkinfo *blkinfo;
|
|
unsigned int major, minor;
|
|
|
|
blkinfo = misc_malloc(sizeof(struct blkinfo));
|
|
if (name && majmin) {
|
|
if (sscanf(majmin, "%u:%u", &major, &minor) == 2) {
|
|
blkinfo->devnode = devnode_new(BLOCKDEV, major, minor,
|
|
name);
|
|
}
|
|
}
|
|
if (fstype && *fstype)
|
|
blkinfo->fstype = misc_strdup(fstype);
|
|
if (uuid && *uuid)
|
|
blkinfo->uuid = misc_strdup(uuid);
|
|
if (mountpoint && *mountpoint)
|
|
blkinfo->mountpoint = misc_strdup(mountpoint);
|
|
if (parent && *parent)
|
|
blkinfo->parent = misc_strdup(parent);
|
|
|
|
return blkinfo;
|
|
}
|
|
|
|
static void blkinfo_free(struct blkinfo *blkinfo)
|
|
{
|
|
if (!blkinfo)
|
|
return;
|
|
free(blkinfo->devnode);
|
|
free(blkinfo->fstype);
|
|
free(blkinfo->uuid);
|
|
free(blkinfo->mountpoint);
|
|
free(blkinfo->parent);
|
|
free(blkinfo);
|
|
}
|
|
|
|
/* Used for debugging. */
|
|
void blkinfo_print(struct blkinfo *blkinfo, int level)
|
|
{
|
|
printf("%*sblkinfo at %p\n", level, "", (void *) blkinfo);
|
|
level += 2;
|
|
if (blkinfo->devnode)
|
|
devnode_print(blkinfo->devnode, level);
|
|
if (blkinfo->fstype)
|
|
printf("%*sfstype=%s\n", level, "", blkinfo->fstype);
|
|
if (blkinfo->uuid)
|
|
printf("%*suuid=%s\n", level, "", blkinfo->uuid);
|
|
if (blkinfo->mountpoint)
|
|
printf("%*smountpoint=%s\n", level, "", blkinfo->mountpoint);
|
|
if (blkinfo->parent)
|
|
printf("%*sparent=%s\n", level, "", blkinfo->parent);
|
|
}
|
|
|
|
static char *isolate_keyword(char **line_ptr, const char *keyword)
|
|
{
|
|
char *start, *end;
|
|
|
|
start = strstr(*line_ptr, keyword);
|
|
if (!start)
|
|
return NULL;
|
|
start += strlen(keyword);
|
|
end = start;
|
|
while (*end && *end != '"')
|
|
end++;
|
|
if (*end) {
|
|
*end = 0;
|
|
*line_ptr = end + 1;
|
|
} else
|
|
*line_ptr = end;
|
|
|
|
return start;
|
|
}
|
|
|
|
static struct blkinfo *blkinfo_from_line(char *line)
|
|
{
|
|
char *name, *majmin, *fstype, *uuid, *mountpoint, *parent;
|
|
|
|
name = isolate_keyword(&line, "NAME=\"");
|
|
majmin = isolate_keyword(&line, "MAJ:MIN=\"");
|
|
fstype = isolate_keyword(&line, "FSTYPE=\"");
|
|
uuid = isolate_keyword(&line, "UUID=\"");
|
|
mountpoint = isolate_keyword(&line, "MOUNTPOINT=\"");
|
|
parent = isolate_keyword(&line, "PKNAME=\"");
|
|
|
|
return blkinfo_new(name, majmin, fstype, uuid, mountpoint, parent);
|
|
}
|
|
|
|
static struct util_list *blkinfos_read(void)
|
|
{
|
|
char *output, *curr, *next;
|
|
struct util_list *blkinfos;
|
|
struct blkinfo *blkinfo;
|
|
|
|
if (cached_blkinfos)
|
|
return cached_blkinfos;
|
|
|
|
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
|
if (!output)
|
|
return NULL;
|
|
|
|
blkinfos = ptrlist_new();
|
|
|
|
/* Iterate over each line. */
|
|
next = output;
|
|
while ((curr = strsep(&next, "\n"))) {
|
|
blkinfo = blkinfo_from_line(curr);
|
|
if (blkinfo)
|
|
ptrlist_add(blkinfos, blkinfo);
|
|
}
|
|
|
|
free(output);
|
|
|
|
cached_blkinfos = blkinfos;
|
|
|
|
return blkinfos;
|
|
}
|
|
|
|
static void blkinfos_free(struct util_list *blkinfos)
|
|
{
|
|
struct ptrlist_node *p, *n;
|
|
|
|
if (!blkinfos)
|
|
return;
|
|
|
|
util_list_iterate_safe(blkinfos, p, n) {
|
|
util_list_remove(blkinfos, p);
|
|
blkinfo_free(p->ptr);
|
|
free(p);
|
|
}
|
|
free(blkinfos);
|
|
}
|
|
|
|
/* Used for debugging. */
|
|
void blkinfos_print(struct util_list *blkinfos, int level)
|
|
{
|
|
struct ptrlist_node *p;
|
|
|
|
printf("%*sblkinfos at %p\n", level, "", (void *) blkinfos);
|
|
if (!blkinfos)
|
|
return;
|
|
level += 2;
|
|
util_list_iterate(blkinfos, p)
|
|
blkinfo_print(p->ptr, level);
|
|
}
|
|
|
|
/* Find a blkinfo for the specified devnode or NULL of none was found. */
|
|
static struct blkinfo *blkinfo_get_by_devnode(struct devnode *devnode)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
/* Get UUID for the specified devnode. */
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (b->devnode && devnode_cmp(b->devnode, devnode) == 0)
|
|
return b;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Find a blkinfo for the specified device name or NULL if none was found. */
|
|
static struct blkinfo *blkinfo_get_by_name(const char *name)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
/* Get UUID for the specified devnode. */
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (b->devnode && strcmp(b->devnode->name, name) == 0)
|
|
return b;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Find a blkinfo for the specified major+minor or NULL if none was found. */
|
|
static struct blkinfo *blkinfo_get_by_majmin(unsigned int major,
|
|
unsigned int minor)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
/* Get UUID for the specified devnode. */
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (b->devnode && b->devnode->major == major &&
|
|
b->devnode->minor == minor)
|
|
return b;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return a newly allocated ptrlist of devnodes of block devices which are
|
|
* ancestors of the specified block device or NULL if no ancestors are found.
|
|
* An ancestor is a block device which is the parent of another block device
|
|
* according to lsblk data. */
|
|
struct util_list *blkinfo_get_ancestor_devnodes(struct devnode *devnode)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct util_list *todos, *result;
|
|
struct util_list *done;
|
|
struct devnode *curr, *p_devnode;
|
|
struct blkinfo *blkinfo, *parent;
|
|
struct ptrlist_node *p_curr, *p;
|
|
int num_parents;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
todos = ptrlist_new();
|
|
result = ptrlist_new();
|
|
done = strlist_new();
|
|
|
|
/* Repeat the resolution process until only devices with no parent
|
|
* remain. */
|
|
ptrlist_add(todos, devnode_copy(devnode));
|
|
while ((p_curr = util_list_start(todos))) {
|
|
util_list_remove(todos, p_curr);
|
|
curr = p_curr->ptr;
|
|
free(p_curr);
|
|
|
|
/* Check for parents. */
|
|
num_parents = 0;
|
|
util_list_iterate(blkinfos, p) {
|
|
blkinfo = p->ptr;
|
|
if (!blkinfo->devnode || !blkinfo->parent)
|
|
continue;
|
|
if (devnode_cmp(blkinfo->devnode, curr) != 0)
|
|
continue;
|
|
parent = blkinfo_get_by_name(blkinfo->parent);
|
|
if (parent && parent->devnode)
|
|
goto add;
|
|
|
|
/* Try to resolve name to major:minor. */
|
|
p_devnode = devnode_from_devfile(NULL, blkinfo->parent,
|
|
BLOCKDEV);
|
|
if (!p_devnode)
|
|
continue;
|
|
parent = blkinfo_get_by_majmin(p_devnode->major,
|
|
p_devnode->minor);
|
|
free(p_devnode);
|
|
if (!parent || !parent->devnode)
|
|
continue;
|
|
|
|
add:
|
|
ptrlist_add(todos, devnode_copy(parent->devnode));
|
|
num_parents++;
|
|
}
|
|
|
|
if (num_parents == 0 && devnode_cmp(curr, devnode) != 0 &&
|
|
!strlist_find(done, curr->name)) {
|
|
/* Add device with no parent to results (except for
|
|
* the initially specified device). */
|
|
ptrlist_add(result, curr);
|
|
strlist_add(done, curr->name);
|
|
} else
|
|
free(curr);
|
|
}
|
|
ptrlist_free(todos, 0);
|
|
strlist_free(done);
|
|
|
|
/* Check if any ancestors were found. */
|
|
if (util_list_is_empty(result)) {
|
|
ptrlist_free(result, 0);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Return a newly allocated ptrlist of devnodes of block devices which have
|
|
* the same file system UUID and type or NULL if no such devices are found. */
|
|
struct util_list *blkinfo_get_same_uuid_devnodes(struct devnode *devnode)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct util_list *devnodes;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
char *uuid;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
b = blkinfo_get_by_devnode(devnode);
|
|
if (!b)
|
|
return NULL;
|
|
|
|
uuid = b->uuid;
|
|
if (!uuid)
|
|
return NULL;
|
|
|
|
/* Get all devnodes of devices with the same UUID. */
|
|
devnodes = ptrlist_new();
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (!b->devnode || !b->uuid)
|
|
continue;
|
|
if (strcmp(b->uuid, uuid) != 0)
|
|
continue;
|
|
if (devnode_cmp(b->devnode, devnode) == 0)
|
|
continue;
|
|
ptrlist_add(devnodes, devnode_copy(b->devnode));
|
|
}
|
|
|
|
if (util_list_is_empty(devnodes)) {
|
|
strlist_free(devnodes);
|
|
devnodes = NULL;
|
|
}
|
|
|
|
return devnodes;
|
|
}
|
|
|
|
/* Check mountpoints in blkinfo for an entry that provides path. */
|
|
struct devnode *blkinfo_get_devnode_by_path(const char *path)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b, *match = NULL;
|
|
size_t blen, mlen = 0;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return NULL;
|
|
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (!b->devnode || !b->mountpoint)
|
|
continue;
|
|
blen = strlen(b->mountpoint);
|
|
|
|
/* path: /path/to/file
|
|
* mountpoint: /path */
|
|
if (strncmp(path, b->mountpoint, blen) != 0)
|
|
continue;
|
|
if (path[blen] && path[blen] != '/')
|
|
continue;
|
|
|
|
/* Ensure longest match. */
|
|
if (!match || blen > mlen) {
|
|
match = b;
|
|
mlen = blen;
|
|
}
|
|
}
|
|
|
|
return match ? devnode_copy(match->devnode) : NULL;
|
|
}
|
|
|
|
/* Add list of mountpoints for mounted file systems to strlist @list. */
|
|
void blkinfo_add_mountpoints(struct util_list *list)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return;
|
|
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (!b->devnode || !b->mountpoint || *b->mountpoint != '/')
|
|
continue;
|
|
strlist_add_unique(list, b->mountpoint);
|
|
}
|
|
}
|
|
|
|
/* Add list of newly allocated devnodes for active swap devices to ptrlist
|
|
* @list. */
|
|
void blkinfo_add_swap_devnodes(struct util_list *list)
|
|
{
|
|
struct util_list *blkinfos;
|
|
struct ptrlist_node *p;
|
|
struct blkinfo *b;
|
|
|
|
blkinfos = blkinfos_read();
|
|
if (!blkinfos)
|
|
return;
|
|
|
|
util_list_iterate(blkinfos, p) {
|
|
b = p->ptr;
|
|
if (!b->devnode || !b->mountpoint ||
|
|
strcmp(b->mountpoint, "[SWAP]") != 0)
|
|
continue;
|
|
ptrlist_add(list, devnode_copy(b->devnode));
|
|
}
|
|
}
|
|
|
|
void blkinfo_exit(void)
|
|
{
|
|
blkinfos_free(cached_blkinfos);
|
|
}
|