Files
s390-tools/zdev/src/blkinfo.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
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>
2017-08-21 10:55:40 +02:00

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);
}