1780 lines
40 KiB
C
1780 lines
40 KiB
C
/*
|
|
* Low-level libblkid probing API
|
|
*
|
|
* Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
|
|
*
|
|
* This file may be redistributed under the terms of the
|
|
* GNU Lesser General Public License.
|
|
*/
|
|
|
|
/**
|
|
* SECTION: lowprobe
|
|
* @title: Low-level probing
|
|
* @short_description: low-level prober initialization
|
|
*
|
|
* The low-level probing routines always and directly read information from
|
|
* the selected (see blkid_probe_set_device()) device.
|
|
*
|
|
* The probing routines are grouped together into separate chains. Currently,
|
|
* the library provides superblocks, partitions and topology chains.
|
|
*
|
|
* The probing routines is possible to filter (enable/disable) by type (e.g.
|
|
* fstype "vfat" or partype "gpt") or by usage flags (e.g. BLKID_USAGE_RAID).
|
|
* These filters are per-chain. Note that always when you touch the chain
|
|
* filter the current probing position is reset and probing starts from
|
|
* scratch. It means that the chain filter should not be modified during
|
|
* probing, for example in loop where you call blkid_do_probe().
|
|
*
|
|
* For more details see the chain specific documentation.
|
|
*
|
|
* The low-level API provides two ways how access to probing results.
|
|
*
|
|
* 1. The NAME=value (tag) interface. This interface is older and returns all data
|
|
* as strings. This interface is generic for all chains.
|
|
*
|
|
* 2. The binary interfaces. These interfaces return data in the native formats.
|
|
* The interface is always specific to the probing chain.
|
|
*
|
|
* Note that the previous probing result (binary or NAME=value) is always
|
|
* zeroized when a chain probing function is called. For example:
|
|
*
|
|
* <informalexample>
|
|
* <programlisting>
|
|
* blkid_probe_enable_partitions(pr, TRUE);
|
|
* blkid_probe_enable_superblocks(pr, FALSE);
|
|
*
|
|
* blkid_do_safeprobe(pr);
|
|
* </programlisting>
|
|
* </informalexample>
|
|
*
|
|
* overwrites the previous probing result for the partitions chain, the superblocks
|
|
* result is not modified.
|
|
*/
|
|
|
|
/**
|
|
* SECTION: lowprobe-tags
|
|
* @title: Low-level tags
|
|
* @short_description: generic NAME=value interface.
|
|
*
|
|
* The probing routines inside the chain are mutually exclusive by default --
|
|
* only few probing routines are marked as "tolerant". The "tolerant" probing
|
|
* routines are used for filesystem which can share the same device with any
|
|
* other filesystem. The blkid_do_safeprobe() checks for the "tolerant" flag.
|
|
*
|
|
* The SUPERBLOCKS chain is enabled by default. The all others chains is
|
|
* necessary to enable by blkid_probe_enable_'CHAINNAME'(). See chains specific
|
|
* documentation.
|
|
*
|
|
* The blkid_do_probe() function returns a result from only one probing
|
|
* routine, and the next call from the next probing routine. It means you need
|
|
* to call the function in loop, for example:
|
|
*
|
|
* <informalexample>
|
|
* <programlisting>
|
|
* while((blkid_do_probe(pr) == 0)
|
|
* ... use result ...
|
|
* </programlisting>
|
|
* </informalexample>
|
|
*
|
|
* The blkid_do_safeprobe() is the same as blkid_do_probe(), but returns only
|
|
* first probing result for every enabled chain. This function checks for
|
|
* ambivalent results (e.g. more "intolerant" filesystems superblocks on the
|
|
* device).
|
|
*
|
|
* The probing result is set of NAME=value pairs (the NAME is always unique).
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_LINUX_CDROM_H
|
|
#include <linux/cdrom.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef HAVE_LIBUUID
|
|
# include <uuid.h>
|
|
#endif
|
|
|
|
#include "blkidP.h"
|
|
#include "all-io.h"
|
|
|
|
/* chains */
|
|
extern const struct blkid_chaindrv superblocks_drv;
|
|
extern const struct blkid_chaindrv topology_drv;
|
|
extern const struct blkid_chaindrv partitions_drv;
|
|
|
|
/*
|
|
* All supported chains
|
|
*/
|
|
static const struct blkid_chaindrv *chains_drvs[] = {
|
|
[BLKID_CHAIN_SUBLKS] = &superblocks_drv,
|
|
[BLKID_CHAIN_TOPLGY] = &topology_drv,
|
|
[BLKID_CHAIN_PARTS] = &partitions_drv
|
|
};
|
|
|
|
static void blkid_probe_reset_vals(blkid_probe pr);
|
|
static void blkid_probe_reset_buffer(blkid_probe pr);
|
|
|
|
/**
|
|
* blkid_new_probe:
|
|
*
|
|
* Returns: a pointer to the newly allocated probe struct or NULL in case of error.
|
|
*/
|
|
blkid_probe blkid_new_probe(void)
|
|
{
|
|
int i;
|
|
blkid_probe pr;
|
|
|
|
blkid_init_debug(0);
|
|
pr = calloc(1, sizeof(struct blkid_struct_probe));
|
|
if (!pr)
|
|
return NULL;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("allocate a new probe %p\n", pr));
|
|
|
|
/* initialize chains */
|
|
for (i = 0; i < BLKID_NCHAINS; i++) {
|
|
pr->chains[i].driver = chains_drvs[i];
|
|
pr->chains[i].flags = chains_drvs[i]->dflt_flags;
|
|
pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
|
|
}
|
|
INIT_LIST_HEAD(&pr->buffers);
|
|
return pr;
|
|
}
|
|
|
|
/*
|
|
* Clone @parent, the new clone shares all, but except:
|
|
*
|
|
* - probing result
|
|
* - bufferes if another device (or offset) is set to the prober
|
|
*/
|
|
blkid_probe blkid_clone_probe(blkid_probe parent)
|
|
{
|
|
blkid_probe pr;
|
|
|
|
if (!parent)
|
|
return NULL;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("allocate a probe clone\n"));
|
|
|
|
pr = blkid_new_probe();
|
|
if (!pr)
|
|
return NULL;
|
|
|
|
pr->fd = parent->fd;
|
|
pr->off = parent->off;
|
|
pr->size = parent->size;
|
|
pr->devno = parent->devno;
|
|
pr->disk_devno = parent->disk_devno;
|
|
pr->blkssz = parent->blkssz;
|
|
pr->flags = parent->flags;
|
|
pr->parent = parent;
|
|
|
|
pr->flags &= ~BLKID_FL_PRIVATE_FD;
|
|
|
|
return pr;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* blkid_new_probe_from_filename:
|
|
* @filename: device or regular file
|
|
*
|
|
* This function is same as call open(filename), blkid_new_probe() and
|
|
* blkid_probe_set_device(pr, fd, 0, 0).
|
|
*
|
|
* The @filename is closed by blkid_free_probe() or by the
|
|
* blkid_probe_set_device() call.
|
|
*
|
|
* Returns: a pointer to the newly allocated probe struct or NULL in case of
|
|
* error.
|
|
*/
|
|
blkid_probe blkid_new_probe_from_filename(const char *filename)
|
|
{
|
|
int fd = -1;
|
|
blkid_probe pr = NULL;
|
|
|
|
if (!filename)
|
|
return NULL;
|
|
|
|
fd = open(filename, O_RDONLY|O_CLOEXEC);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
pr = blkid_new_probe();
|
|
if (!pr)
|
|
goto err;
|
|
|
|
if (blkid_probe_set_device(pr, fd, 0, 0))
|
|
goto err;
|
|
|
|
pr->flags |= BLKID_FL_PRIVATE_FD;
|
|
return pr;
|
|
err:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
blkid_free_probe(pr);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* blkid_free_probe:
|
|
* @pr: probe
|
|
*
|
|
* Deallocates the probe struct, buffers and all allocated
|
|
* data that are associated with this probing control struct.
|
|
*/
|
|
void blkid_free_probe(blkid_probe pr)
|
|
{
|
|
int i;
|
|
|
|
if (!pr)
|
|
return;
|
|
|
|
for (i = 0; i < BLKID_NCHAINS; i++) {
|
|
struct blkid_chain *ch = &pr->chains[i];
|
|
|
|
if (ch->driver->free_data)
|
|
ch->driver->free_data(pr, ch->data);
|
|
free(ch->fltr);
|
|
}
|
|
|
|
if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
|
|
close(pr->fd);
|
|
blkid_probe_reset_buffer(pr);
|
|
blkid_free_probe(pr->disk_probe);
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("free probe %p\n", pr));
|
|
free(pr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Removes chain values from probing result.
|
|
*/
|
|
void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
|
|
{
|
|
int nvals = pr->nvals;
|
|
int i, x;
|
|
|
|
for (x = 0, i = 0; i < pr->nvals; i++) {
|
|
struct blkid_prval *v = &pr->vals[i];
|
|
|
|
if (v->chain != chn && x == i) {
|
|
x++;
|
|
continue;
|
|
}
|
|
if (v->chain == chn) {
|
|
--nvals;
|
|
continue;
|
|
}
|
|
memcpy(&pr->vals[x++], v, sizeof(struct blkid_prval));
|
|
}
|
|
pr->nvals = nvals;
|
|
}
|
|
|
|
static void blkid_probe_chain_reset_position(struct blkid_chain *chn)
|
|
{
|
|
if (chn)
|
|
chn->idx = -1;
|
|
}
|
|
|
|
/*
|
|
* Copies chain values from probing result to @vals, the max size of @vals is
|
|
* @nvals and returns real number of values.
|
|
*/
|
|
int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn,
|
|
struct blkid_prval *vals, int nvals)
|
|
{
|
|
int i, x;
|
|
|
|
for (x = 0, i = 0; i < pr->nvals && x < nvals; i++) {
|
|
struct blkid_prval *v = &pr->vals[i];
|
|
|
|
if (v->chain != chn)
|
|
continue;
|
|
memcpy(&vals[x++], v, sizeof(struct blkid_prval));
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
* Appends values from @vals to the probing result
|
|
*/
|
|
void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < nvals && pr->nvals < BLKID_NVALS) {
|
|
memcpy(&pr->vals[pr->nvals++], &vals[i++],
|
|
sizeof(struct blkid_prval));
|
|
}
|
|
}
|
|
|
|
static void blkid_probe_reset_vals(blkid_probe pr)
|
|
{
|
|
memset(pr->vals, 0, sizeof(pr->vals));
|
|
pr->nvals = 0;
|
|
}
|
|
|
|
struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
|
|
{
|
|
return pr->cur_chain;
|
|
}
|
|
|
|
void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
|
|
{
|
|
int rc, org_prob_flags;
|
|
struct blkid_chain *org_chn;
|
|
|
|
if (!pr || !chn)
|
|
return NULL;
|
|
|
|
/* save the current setting -- the binary API has to be completely
|
|
* independent on the current probing status
|
|
*/
|
|
org_chn = pr->cur_chain;
|
|
org_prob_flags = pr->prob_flags;
|
|
|
|
pr->cur_chain = chn;
|
|
pr->prob_flags = 0;
|
|
chn->binary = TRUE;
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
rc = chn->driver->probe(pr, chn);
|
|
|
|
chn->binary = FALSE;
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
/* restore the original setting
|
|
*/
|
|
pr->cur_chain = org_chn;
|
|
pr->prob_flags = org_prob_flags;
|
|
|
|
if (rc != 0)
|
|
return NULL;
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("returning %s binary data\n", chn->driver->name));
|
|
return chn->data;
|
|
}
|
|
|
|
|
|
/**
|
|
* blkid_reset_probe:
|
|
* @pr: probe
|
|
*
|
|
* Zeroize probing results and resets the current probing (this has impact to
|
|
* blkid_do_probe() only). This function does not touch probing filters and
|
|
* keeps assigned device.
|
|
*/
|
|
void blkid_reset_probe(blkid_probe pr)
|
|
{
|
|
int i;
|
|
|
|
if (!pr)
|
|
return;
|
|
|
|
blkid_probe_reset_vals(pr);
|
|
blkid_probe_set_wiper(pr, 0, 0);
|
|
|
|
pr->cur_chain = NULL;
|
|
|
|
for (i = 0; i < BLKID_NCHAINS; i++)
|
|
blkid_probe_chain_reset_position(&pr->chains[i]);
|
|
}
|
|
|
|
/***
|
|
static int blkid_probe_dump_filter(blkid_probe pr, int chain)
|
|
{
|
|
struct blkid_chain *chn;
|
|
int i;
|
|
|
|
if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
|
|
return -1;
|
|
|
|
chn = &pr->chains[chain];
|
|
|
|
if (!chn->fltr)
|
|
return -1;
|
|
|
|
for (i = 0; i < chn->driver->nidinfos; i++) {
|
|
const struct blkid_idinfo *id = chn->driver->idinfos[i];
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("%d: %s: %s\n",
|
|
i,
|
|
id->name,
|
|
blkid_bmp_get_item(chn->fltr, i)
|
|
? "disabled" : "enabled <--"));
|
|
}
|
|
return 0;
|
|
}
|
|
***/
|
|
|
|
/*
|
|
* Returns properly initialized chain filter
|
|
*/
|
|
unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
|
|
{
|
|
struct blkid_chain *chn;
|
|
|
|
if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
|
|
return NULL;
|
|
|
|
chn = &pr->chains[chain];
|
|
|
|
/* always when you touch the chain filter all indexes are reset and
|
|
* probing starts from scratch
|
|
*/
|
|
blkid_probe_chain_reset_position(chn);
|
|
pr->cur_chain = NULL;
|
|
|
|
if (!chn->driver->has_fltr || (!chn->fltr && !create))
|
|
return NULL;
|
|
|
|
if (!chn->fltr)
|
|
chn->fltr = calloc(1, blkid_bmp_nbytes(chn->driver->nidinfos));
|
|
else
|
|
memset(chn->fltr, 0, blkid_bmp_nbytes(chn->driver->nidinfos));
|
|
|
|
/* blkid_probe_dump_filter(pr, chain); */
|
|
return chn->fltr;
|
|
}
|
|
|
|
/*
|
|
* Generic private functions for filter setting
|
|
*/
|
|
int __blkid_probe_invert_filter(blkid_probe pr, int chain)
|
|
{
|
|
size_t i;
|
|
struct blkid_chain *chn;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
chn = &pr->chains[chain];
|
|
|
|
if (!chn->driver->has_fltr || !chn->fltr)
|
|
return -1;
|
|
|
|
for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++)
|
|
chn->fltr[i] = ~chn->fltr[i];
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
|
|
/* blkid_probe_dump_filter(pr, chain); */
|
|
return 0;
|
|
}
|
|
|
|
int __blkid_probe_reset_filter(blkid_probe pr, int chain)
|
|
{
|
|
return blkid_probe_get_filter(pr, chain, FALSE) ? 0 : -1;
|
|
}
|
|
|
|
int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[])
|
|
{
|
|
unsigned long *fltr;
|
|
struct blkid_chain *chn;
|
|
size_t i;
|
|
|
|
fltr = blkid_probe_get_filter(pr, chain, TRUE);
|
|
if (!fltr)
|
|
return -1;
|
|
|
|
chn = &pr->chains[chain];
|
|
|
|
for (i = 0; i < chn->driver->nidinfos; i++) {
|
|
int has = 0;
|
|
const struct blkid_idinfo *id = chn->driver->idinfos[i];
|
|
char **n;
|
|
|
|
for (n = names; *n; n++) {
|
|
if (!strcmp(id->name, *n)) {
|
|
has = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (flag & BLKID_FLTR_ONLYIN) {
|
|
if (!has)
|
|
blkid_bmp_set_item(fltr, i);
|
|
} else if (flag & BLKID_FLTR_NOTIN) {
|
|
if (has)
|
|
blkid_bmp_set_item(fltr, i);
|
|
}
|
|
}
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("%s: a new probing type-filter initialized\n",
|
|
chn->driver->name));
|
|
/* blkid_probe_dump_filter(pr, chain); */
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *blkid_probe_get_buffer(blkid_probe pr,
|
|
blkid_loff_t off, blkid_loff_t len)
|
|
{
|
|
struct list_head *p;
|
|
struct blkid_bufinfo *bf = NULL;
|
|
|
|
if (pr->size <= 0)
|
|
return NULL;
|
|
|
|
if (pr->parent &&
|
|
pr->parent->devno == pr->devno &&
|
|
pr->parent->off <= pr->off &&
|
|
pr->parent->off + pr->parent->size >= pr->off + pr->size) {
|
|
/*
|
|
* This is a cloned prober and points to the same area as
|
|
* parent. Let's use parent's buffers.
|
|
*
|
|
* Note that pr->off (and pr->parent->off) is always from the
|
|
* beginig of the device.
|
|
*/
|
|
return blkid_probe_get_buffer(pr->parent,
|
|
pr->off + off - pr->parent->off, len);
|
|
}
|
|
|
|
list_for_each(p, &pr->buffers) {
|
|
struct blkid_bufinfo *x =
|
|
list_entry(p, struct blkid_bufinfo, bufs);
|
|
|
|
if (x->off <= off && off + len <= x->off + x->len) {
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("\treuse buffer: off=%jd len=%jd pr=%p\n",
|
|
x->off, x->len, pr));
|
|
bf = x;
|
|
break;
|
|
}
|
|
}
|
|
if (!bf) {
|
|
ssize_t ret;
|
|
|
|
if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
|
|
return NULL;
|
|
|
|
/* allocate info and space for data by why call */
|
|
bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
|
|
if (!bf)
|
|
return NULL;
|
|
|
|
bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
|
|
bf->len = len;
|
|
bf->off = off;
|
|
INIT_LIST_HEAD(&bf->bufs);
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("\tbuffer read: off=%jd len=%jd pr=%p\n",
|
|
off, len, pr));
|
|
|
|
ret = read(pr->fd, bf->data, len);
|
|
if (ret != (ssize_t) len) {
|
|
free(bf);
|
|
return NULL;
|
|
}
|
|
list_add_tail(&bf->bufs, &pr->buffers);
|
|
}
|
|
|
|
return off ? bf->data + (off - bf->off) : bf->data;
|
|
}
|
|
|
|
|
|
static void blkid_probe_reset_buffer(blkid_probe pr)
|
|
{
|
|
uint64_t read_ct = 0, len_ct = 0;
|
|
|
|
if (!pr || list_empty(&pr->buffers))
|
|
return;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("reseting probing buffers pr=%p\n", pr));
|
|
|
|
while (!list_empty(&pr->buffers)) {
|
|
struct blkid_bufinfo *bf = list_entry(pr->buffers.next,
|
|
struct blkid_bufinfo, bufs);
|
|
read_ct++;
|
|
len_ct += bf->len;
|
|
list_del(&bf->bufs);
|
|
free(bf);
|
|
}
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("buffers summary: %"PRIu64" bytes "
|
|
"by %"PRIu64" read() call(s)\n",
|
|
len_ct, read_ct));
|
|
|
|
INIT_LIST_HEAD(&pr->buffers);
|
|
}
|
|
|
|
/*
|
|
* Small devices need a special care.
|
|
*/
|
|
int blkid_probe_is_tiny(blkid_probe pr)
|
|
{
|
|
return pr && (pr->flags & BLKID_FL_TINY_DEV);
|
|
}
|
|
|
|
/*
|
|
* CDROMs may fail when probed for RAID (last sector problem)
|
|
*/
|
|
int blkid_probe_is_cdrom(blkid_probe pr)
|
|
{
|
|
return pr && (pr->flags & BLKID_FL_CDROM_DEV);
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_set_device:
|
|
* @pr: probe
|
|
* @fd: device file descriptor
|
|
* @off: begin of probing area
|
|
* @size: size of probing area (zero means whole device/file)
|
|
*
|
|
* Assigns the device to probe control struct, resets internal buffers and
|
|
* resets the current probing.
|
|
*
|
|
* Returns: -1 in case of failure, or 0 on success.
|
|
*/
|
|
int blkid_probe_set_device(blkid_probe pr, int fd,
|
|
blkid_loff_t off, blkid_loff_t size)
|
|
{
|
|
struct stat sb;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
blkid_reset_probe(pr);
|
|
blkid_probe_reset_buffer(pr);
|
|
|
|
if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
|
|
close(pr->fd);
|
|
|
|
pr->flags &= ~BLKID_FL_PRIVATE_FD;
|
|
pr->flags &= ~BLKID_FL_TINY_DEV;
|
|
pr->flags &= ~BLKID_FL_CDROM_DEV;
|
|
pr->prob_flags = 0;
|
|
pr->fd = fd;
|
|
pr->off = off;
|
|
pr->size = 0;
|
|
pr->devno = 0;
|
|
pr->disk_devno = 0;
|
|
pr->mode = 0;
|
|
pr->blkssz = 0;
|
|
pr->wipe_off = 0;
|
|
pr->wipe_size = 0;
|
|
pr->wipe_chain = NULL;
|
|
|
|
#if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE)
|
|
/* Disable read-ahead */
|
|
posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
|
|
#endif
|
|
if (fstat(fd, &sb))
|
|
goto err;
|
|
|
|
if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode) && !S_ISREG(sb.st_mode))
|
|
goto err;
|
|
|
|
pr->mode = sb.st_mode;
|
|
if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
|
|
pr->devno = sb.st_rdev;
|
|
|
|
if (size)
|
|
pr->size = size;
|
|
else {
|
|
if (S_ISBLK(sb.st_mode)) {
|
|
if (blkdev_get_size(fd, (unsigned long long *) &pr->size)) {
|
|
DBG(DEBUG_LOWPROBE, printf(
|
|
"failed to get device size\n"));
|
|
goto err;
|
|
}
|
|
} else if (S_ISCHR(sb.st_mode))
|
|
pr->size = 1; /* UBI devices are char... */
|
|
else if (S_ISREG(sb.st_mode))
|
|
pr->size = sb.st_size; /* regular file */
|
|
|
|
if (pr->off > pr->size)
|
|
goto err;
|
|
|
|
/* The probing area cannot be larger than whole device, pr->off
|
|
* is offset within the device */
|
|
pr->size -= pr->off;
|
|
}
|
|
|
|
if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
|
|
pr->flags |= BLKID_FL_TINY_DEV;
|
|
|
|
#ifdef CDROM_GET_CAPABILITY
|
|
if (S_ISBLK(sb.st_mode) &&
|
|
!blkid_probe_is_tiny(pr) &&
|
|
blkid_probe_is_wholedisk(pr) &&
|
|
ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
|
|
pr->flags |= BLKID_FL_CDROM_DEV;
|
|
#endif
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n",
|
|
pr->off, pr->size));
|
|
DBG(DEBUG_LOWPROBE, printf("whole-disk: %s, regfile: %s\n",
|
|
blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
|
|
S_ISREG(pr->mode) ? "YES" : "NO"));
|
|
|
|
return 0;
|
|
err:
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("failed to prepare a device for low-probing\n"));
|
|
return -1;
|
|
|
|
}
|
|
|
|
int blkid_probe_get_dimension(blkid_probe pr,
|
|
blkid_loff_t *off, blkid_loff_t *size)
|
|
{
|
|
if (!pr)
|
|
return -1;
|
|
|
|
*off = pr->off;
|
|
*size = pr->size;
|
|
return 0;
|
|
}
|
|
|
|
int blkid_probe_set_dimension(blkid_probe pr,
|
|
blkid_loff_t off, blkid_loff_t size)
|
|
{
|
|
if (!pr)
|
|
return -1;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf(
|
|
"changing probing area pr=%p: size=%llu, off=%llu "
|
|
"-to-> size=%llu, off=%llu\n",
|
|
pr,
|
|
(unsigned long long) pr->size,
|
|
(unsigned long long) pr->off,
|
|
(unsigned long long) size,
|
|
(unsigned long long) off));
|
|
|
|
pr->off = off;
|
|
pr->size = size;
|
|
pr->flags &= ~BLKID_FL_TINY_DEV;
|
|
|
|
if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode))
|
|
pr->flags |= BLKID_FL_TINY_DEV;
|
|
|
|
blkid_probe_reset_buffer(pr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
|
|
blkid_loff_t *offset, const struct blkid_idmag **res)
|
|
{
|
|
const struct blkid_idmag *mag = NULL;
|
|
blkid_loff_t off = 0;
|
|
|
|
if (id)
|
|
mag = &id->magics[0];
|
|
if (res)
|
|
*res = NULL;
|
|
|
|
/* try to detect by magic string */
|
|
while(mag && mag->magic) {
|
|
unsigned char *buf;
|
|
|
|
off = (mag->kboff + (mag->sboff >> 10)) << 10;
|
|
buf = blkid_probe_get_buffer(pr, off, 1024);
|
|
|
|
if (buf && !memcmp(mag->magic,
|
|
buf + (mag->sboff & 0x3ff), mag->len)) {
|
|
DBG(DEBUG_LOWPROBE, printf(
|
|
"\tmagic sboff=%u, kboff=%ld\n",
|
|
mag->sboff, mag->kboff));
|
|
if (offset)
|
|
*offset = off + (mag->sboff & 0x3ff);
|
|
if (res)
|
|
*res = mag;
|
|
return 0;
|
|
}
|
|
mag++;
|
|
}
|
|
|
|
if (id && id->magics[0].magic)
|
|
/* magic string(s) defined, but not found */
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void blkid_probe_start(blkid_probe pr)
|
|
{
|
|
if (pr) {
|
|
DBG(DEBUG_LOWPROBE, printf("%p: start probe\n", pr));
|
|
pr->cur_chain = NULL;
|
|
pr->prob_flags = 0;
|
|
blkid_probe_set_wiper(pr, 0, 0);
|
|
}
|
|
}
|
|
|
|
static inline void blkid_probe_end(blkid_probe pr)
|
|
{
|
|
if (pr) {
|
|
DBG(DEBUG_LOWPROBE, printf("%p: end probe\n", pr));
|
|
pr->cur_chain = NULL;
|
|
pr->prob_flags = 0;
|
|
blkid_probe_set_wiper(pr, 0, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* blkid_do_probe:
|
|
* @pr: prober
|
|
*
|
|
* Calls probing functions in all enabled chains. The superblocks chain is
|
|
* enabled by default. The blkid_do_probe() stores result from only one
|
|
* probing function. It's necessary to call this routine in a loop to get
|
|
* results from all probing functions in all chains. The probing is reset
|
|
* by blkid_reset_probe() or by filter functions.
|
|
*
|
|
* This is string-based NAME=value interface only.
|
|
*
|
|
* <example>
|
|
* <title>basic case - use the first result only</title>
|
|
* <programlisting>
|
|
*
|
|
* if (blkid_do_probe(pr) == 0) {
|
|
* int nvals = blkid_probe_numof_values(pr);
|
|
* for (n = 0; n < nvals; n++) {
|
|
* if (blkid_probe_get_value(pr, n, &name, &data, &len) == 0)
|
|
* printf("%s = %s\n", name, data);
|
|
* }
|
|
* }
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* <example>
|
|
* <title>advanced case - probe for all signatures</title>
|
|
* <programlisting>
|
|
*
|
|
* while (blkid_do_probe(pr) == 0) {
|
|
* int nvals = blkid_probe_numof_values(pr);
|
|
* ...
|
|
* }
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* See also blkid_reset_probe().
|
|
*
|
|
* Returns: 0 on success, 1 when probing is done and -1 in case of error.
|
|
*/
|
|
int blkid_do_probe(blkid_probe pr)
|
|
{
|
|
int rc = 1;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
do {
|
|
struct blkid_chain *chn = pr->cur_chain;
|
|
|
|
if (!chn) {
|
|
blkid_probe_start(pr);
|
|
chn = pr->cur_chain = &pr->chains[0];
|
|
}
|
|
/* we go to the next chain only when the previous probing
|
|
* result was nothing (rc == 1) and when the current chain is
|
|
* disabled or we are at end of the current chain (chain->idx +
|
|
* 1 == sizeof chain) or the current chain bailed out right at
|
|
* the start (chain->idx == -1)
|
|
*/
|
|
else if (rc == 1 && (chn->enabled == FALSE ||
|
|
chn->idx + 1 == (int) chn->driver->nidinfos ||
|
|
chn->idx == -1)) {
|
|
|
|
size_t idx = chn->driver->id + 1;
|
|
|
|
if (idx < BLKID_NCHAINS)
|
|
chn = pr->cur_chain = &pr->chains[idx];
|
|
else {
|
|
blkid_probe_end(pr);
|
|
return 1; /* all chains already probed */
|
|
}
|
|
}
|
|
|
|
chn->binary = FALSE; /* for sure... */
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("chain probe %s %s (idx=%d)\n",
|
|
chn->driver->name,
|
|
chn->enabled? "ENABLED" : "DISABLED",
|
|
chn->idx));
|
|
|
|
if (!chn->enabled)
|
|
continue;
|
|
|
|
/* rc: -1 = error, 0 = success, 1 = no result */
|
|
rc = chn->driver->probe(pr, chn);
|
|
|
|
} while (rc == 1);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* blkid_do_wipe:
|
|
* @pr: prober
|
|
* @dryrun: if TRUE then don't touch the device.
|
|
*
|
|
* This function erases the current signature detected by @pr. The @pr has to
|
|
* be open in O_RDWR mode, BLKID_SUBLKS_MAGIC or/and BLKID_PARTS_MAGIC flags
|
|
* has to be enabled.
|
|
*
|
|
* After successful signature removing the @pr prober will be moved one step
|
|
* back and the next blkid_do_probe() call will again call previously called
|
|
* probing function.
|
|
*
|
|
* <example>
|
|
* <title>wipe all filesystems or raids from the device</title>
|
|
* <programlisting>
|
|
* fd = open(devname, O_RDWR|O_CLOEXEC);
|
|
* blkid_probe_set_device(pr, fd, 0, 0);
|
|
*
|
|
* blkid_probe_enable_superblocks(pr, 1);
|
|
* blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
|
|
*
|
|
* while (blkid_do_probe(pr) == 0)
|
|
* blkid_do_wipe(pr, FALSE);
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* See also blkid_probe_step_back() if you cannot use this build-in wipe
|
|
* function, but you want to use libblkid probing as a source for wiping.
|
|
*
|
|
* Returns: 0 on success, and -1 in case of error.
|
|
*/
|
|
int blkid_do_wipe(blkid_probe pr, int dryrun)
|
|
{
|
|
const char *off = NULL;
|
|
size_t len = 0;
|
|
loff_t offset, l;
|
|
char buf[BUFSIZ];
|
|
int fd, rc = 0;
|
|
struct blkid_chain *chn;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
chn = pr->cur_chain;
|
|
if (!chn)
|
|
return -1;
|
|
|
|
switch (chn->driver->id) {
|
|
case BLKID_CHAIN_SUBLKS:
|
|
rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
|
|
if (!rc)
|
|
rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
|
|
break;
|
|
case BLKID_CHAIN_PARTS:
|
|
rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
|
|
if (!rc)
|
|
rc = blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (rc || len == 0 || off == NULL)
|
|
return 0;
|
|
|
|
offset = strtoll(off, NULL, 10);
|
|
fd = blkid_probe_get_fd(pr);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
if (len > sizeof(buf))
|
|
len = sizeof(buf);
|
|
|
|
DBG(DEBUG_LOWPROBE, printf(
|
|
"do_wipe [offset=0x%jx, len=%zd, chain=%s, idx=%d, dryrun=%s]\n",
|
|
offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not"));
|
|
|
|
l = lseek(fd, offset, SEEK_SET);
|
|
if (l == (off_t) -1)
|
|
return -1;
|
|
|
|
memset(buf, 0, len);
|
|
|
|
if (!dryrun && len) {
|
|
if (write_all(fd, buf, len))
|
|
return -1;
|
|
fsync(fd);
|
|
return blkid_probe_step_back(pr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_step_back():
|
|
* @pr: prober
|
|
*
|
|
* This function move pointer to the probing chain one step back -- it means
|
|
* that the previously used probing function will be called again in the next
|
|
* blkid_do_probe() call.
|
|
*
|
|
* This is necessary for example if you erase or modify on-disk superblock
|
|
* according to the current libblkid probing result.
|
|
*
|
|
* <example>
|
|
* <title>wipe all superblock, but use libblkid only for probing</title>
|
|
* <programlisting>
|
|
* pr = blkid_new_probe_from_filename(devname);
|
|
*
|
|
* blkid_probe_enable_superblocks(pr, 1);
|
|
* blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
|
|
*
|
|
* while (blkid_do_probe(pr) == 0) {
|
|
* const char *ostr = NULL;
|
|
* size_t len = 0;
|
|
*
|
|
* // superblocks
|
|
* if (blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &ostr, NULL) == 0)
|
|
* blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
|
|
*
|
|
* // partition tables
|
|
* if (len == 0 && blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &ostr, NULL) == 0)
|
|
* blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
|
|
*
|
|
* if (!len || !str)
|
|
* continue;
|
|
*
|
|
* // convert ostr to the real offset by off = strtoll(ostr, NULL, 10);
|
|
* // use your stuff to errase @len bytes at the @off
|
|
* ....
|
|
*
|
|
* // retry the last probing to check for backup superblocks ..etc.
|
|
* blkid_probe_step_back(pr);
|
|
* }
|
|
* </programlisting>
|
|
* </example>
|
|
*
|
|
* Returns: 0 on success, and -1 in case of error.
|
|
*/
|
|
int blkid_probe_step_back(blkid_probe pr)
|
|
{
|
|
struct blkid_chain *chn;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
chn = pr->cur_chain;
|
|
if (!chn)
|
|
return -1;
|
|
|
|
blkid_probe_reset_buffer(pr);
|
|
|
|
if (chn->idx >= 0) {
|
|
chn->idx--;
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("step back: moving %s chain index to %d\n",
|
|
chn->driver->name,
|
|
chn->idx));
|
|
}
|
|
|
|
if (chn->idx == -1) {
|
|
/* blkid_do_probe() goes to the next chain if the index
|
|
* of the current chain is -1, so we have to set the
|
|
* chain pointer to the previous chain.
|
|
*/
|
|
size_t idx = chn->driver->id > 0 ? chn->driver->id - 1 : 0;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("step back: moving to previous chain\n"));
|
|
|
|
if (idx > 0)
|
|
pr->cur_chain = &pr->chains[idx];
|
|
else if (idx == 0)
|
|
pr->cur_chain = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blkid_do_safeprobe:
|
|
* @pr: prober
|
|
*
|
|
* This function gathers probing results from all enabled chains and checks
|
|
* for ambivalent results (e.g. more filesystems on the device).
|
|
*
|
|
* This is string-based NAME=value interface only.
|
|
*
|
|
* Note about suberblocks chain -- the function does not check for filesystems
|
|
* when a RAID signature is detected. The function also does not check for
|
|
* collision between RAIDs. The first detected RAID is returned. The function
|
|
* checks for collision between partition table and RAID signature -- it's
|
|
* recommended to enable partitions chain together with superblocks chain.
|
|
*
|
|
* Returns: 0 on success, 1 if nothing is detected, -2 if ambivalen result is
|
|
* detected and -1 on case of error.
|
|
*/
|
|
int blkid_do_safeprobe(blkid_probe pr)
|
|
{
|
|
int i, count = 0, rc = 0;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
blkid_probe_start(pr);
|
|
|
|
pr->prob_flags |= BLKID_PROBE_FL_IGNORE_BACKUP;
|
|
|
|
for (i = 0; i < BLKID_NCHAINS; i++) {
|
|
struct blkid_chain *chn;
|
|
|
|
chn = pr->cur_chain = &pr->chains[i];
|
|
chn->binary = FALSE; /* for sure... */
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("chain safeprobe %s %s\n",
|
|
chn->driver->name,
|
|
chn->enabled? "ENABLED" : "DISABLED"));
|
|
|
|
if (!chn->enabled)
|
|
continue;
|
|
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
rc = chn->driver->safeprobe(pr, chn);
|
|
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
/* rc: -2 ambivalent, -1 = error, 0 = success, 1 = no result */
|
|
if (rc < 0)
|
|
goto done; /* error */
|
|
if (rc == 0)
|
|
count++; /* success */
|
|
}
|
|
|
|
done:
|
|
blkid_probe_end(pr);
|
|
if (rc < 0)
|
|
return rc;
|
|
return count ? 0 : 1;
|
|
}
|
|
|
|
/**
|
|
* blkid_do_fullprobe:
|
|
* @pr: prober
|
|
*
|
|
* This function gathers probing results from all enabled chains. Same as
|
|
* blkid_do_safeprobe() but does not check for collision between probing
|
|
* result.
|
|
*
|
|
* This is string-based NAME=value interface only.
|
|
*
|
|
* Returns: 0 on success, 1 if nothing is detected or -1 on case of error.
|
|
*/
|
|
int blkid_do_fullprobe(blkid_probe pr)
|
|
{
|
|
int i, count = 0, rc = 0;
|
|
|
|
if (!pr)
|
|
return -1;
|
|
|
|
blkid_probe_start(pr);
|
|
|
|
for (i = 0; i < BLKID_NCHAINS; i++) {
|
|
struct blkid_chain *chn;
|
|
|
|
chn = pr->cur_chain = &pr->chains[i];
|
|
chn->binary = FALSE; /* for sure... */
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("chain fullprobe %s: %s\n",
|
|
chn->driver->name,
|
|
chn->enabled? "ENABLED" : "DISABLED"));
|
|
|
|
if (!chn->enabled)
|
|
continue;
|
|
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
rc = chn->driver->probe(pr, chn);
|
|
|
|
blkid_probe_chain_reset_position(chn);
|
|
|
|
/* rc: -1 = error, 0 = success, 1 = no result */
|
|
if (rc < 0)
|
|
goto done; /* error */
|
|
if (rc == 0)
|
|
count++; /* success */
|
|
}
|
|
|
|
done:
|
|
blkid_probe_end(pr);
|
|
if (rc < 0)
|
|
return rc;
|
|
return count ? 0 : 1;
|
|
}
|
|
|
|
/* same sa blkid_probe_get_buffer() but works with 512-sectors */
|
|
unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
|
|
{
|
|
return pr ? blkid_probe_get_buffer(pr,
|
|
((blkid_loff_t) sector) << 9, 0x200) : NULL;
|
|
}
|
|
|
|
struct blkid_prval *blkid_probe_assign_value(
|
|
blkid_probe pr, const char *name)
|
|
{
|
|
struct blkid_prval *v;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
if (pr->nvals >= BLKID_NVALS)
|
|
return NULL;
|
|
|
|
v = &pr->vals[pr->nvals];
|
|
v->name = name;
|
|
v->chain = pr->cur_chain;
|
|
pr->nvals++;
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("assigning %s [%s]\n", name, v->chain->driver->name));
|
|
return v;
|
|
}
|
|
|
|
int blkid_probe_reset_last_value(blkid_probe pr)
|
|
{
|
|
struct blkid_prval *v;
|
|
|
|
if (pr == NULL || pr->nvals == 0)
|
|
return -1;
|
|
|
|
v = &pr->vals[pr->nvals - 1];
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("un-assigning %s [%s]\n", v->name, v->chain->driver->name));
|
|
|
|
memset(v, 0, sizeof(struct blkid_prval));
|
|
pr->nvals--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int blkid_probe_set_value(blkid_probe pr, const char *name,
|
|
unsigned char *data, size_t len)
|
|
{
|
|
struct blkid_prval *v;
|
|
|
|
if (len > BLKID_PROBVAL_BUFSIZ)
|
|
len = BLKID_PROBVAL_BUFSIZ;
|
|
|
|
v = blkid_probe_assign_value(pr, name);
|
|
if (!v)
|
|
return -1;
|
|
|
|
memcpy(v->data, data, len);
|
|
v->len = len;
|
|
return 0;
|
|
}
|
|
|
|
int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
|
|
const char *fmt, va_list ap)
|
|
{
|
|
struct blkid_prval *v;
|
|
ssize_t len;
|
|
|
|
v = blkid_probe_assign_value(pr, name);
|
|
if (!v)
|
|
return -1;
|
|
|
|
len = vsnprintf((char *) v->data, sizeof(v->data), fmt, ap);
|
|
|
|
if (len <= 0 || (size_t) len >= sizeof(v->data)) {
|
|
blkid_probe_reset_last_value(pr);
|
|
return -1;
|
|
}
|
|
v->len = len + 1;
|
|
return 0;
|
|
}
|
|
|
|
int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
|
|
const char *fmt, ...)
|
|
{
|
|
int rc;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
rc = blkid_probe_vsprintf_value(pr, name, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset,
|
|
size_t len, unsigned char *magic)
|
|
{
|
|
int rc = 0;
|
|
struct blkid_chain *chn = blkid_probe_get_chain(pr);
|
|
|
|
if (!chn || !magic || !len || chn->binary)
|
|
return 0;
|
|
|
|
switch (chn->driver->id) {
|
|
case BLKID_CHAIN_SUBLKS:
|
|
if (!(chn->flags & BLKID_SUBLKS_MAGIC))
|
|
return 0;
|
|
rc = blkid_probe_set_value(pr, "SBMAGIC", magic, len);
|
|
if (!rc)
|
|
rc = blkid_probe_sprintf_value(pr,
|
|
"SBMAGIC_OFFSET", "%llu", (unsigned long long)offset);
|
|
break;
|
|
case BLKID_CHAIN_PARTS:
|
|
if (!(chn->flags & BLKID_PARTS_MAGIC))
|
|
return 0;
|
|
rc = blkid_probe_set_value(pr, "PTMAGIC", magic, len);
|
|
if (!rc)
|
|
rc = blkid_probe_sprintf_value(pr,
|
|
"PTMAGIC_OFFSET", "%llu", (unsigned long long)offset);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_devno:
|
|
* @pr: probe
|
|
*
|
|
* Returns: block device number, or 0 for regular files.
|
|
*/
|
|
dev_t blkid_probe_get_devno(blkid_probe pr)
|
|
{
|
|
return pr->devno;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_wholedisk_devno:
|
|
* @pr: probe
|
|
*
|
|
* Returns: device number of the wholedisk, or 0 for regular files.
|
|
*/
|
|
dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
|
|
{
|
|
if (!pr->disk_devno) {
|
|
dev_t devno, disk_devno = 0;
|
|
|
|
devno = blkid_probe_get_devno(pr);
|
|
if (!devno)
|
|
return 0;
|
|
|
|
if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0)
|
|
pr->disk_devno = disk_devno;
|
|
}
|
|
return pr->disk_devno;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_is_wholedisk:
|
|
* @pr: probe
|
|
*
|
|
* Returns: 1 if the device is whole-disk or 0.
|
|
*/
|
|
int blkid_probe_is_wholedisk(blkid_probe pr)
|
|
{
|
|
dev_t devno, disk_devno;
|
|
|
|
devno = blkid_probe_get_devno(pr);
|
|
if (!devno)
|
|
return 0;
|
|
|
|
disk_devno = blkid_probe_get_wholedisk_devno(pr);
|
|
if (!disk_devno)
|
|
return 0;
|
|
|
|
return devno == disk_devno;
|
|
}
|
|
|
|
blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr)
|
|
{
|
|
dev_t disk;
|
|
|
|
if (blkid_probe_is_wholedisk(pr))
|
|
return NULL; /* this is not partition */
|
|
|
|
if (pr->parent)
|
|
/* this is cloned blkid_probe, use parent's stuff */
|
|
return blkid_probe_get_wholedisk_probe(pr->parent);
|
|
|
|
disk = blkid_probe_get_wholedisk_devno(pr);
|
|
|
|
if (pr->disk_probe && pr->disk_probe->devno != disk) {
|
|
/* we have disk prober, but for another disk... close it */
|
|
blkid_free_probe(pr->disk_probe);
|
|
pr->disk_probe = NULL;
|
|
}
|
|
|
|
if (!pr->disk_probe) {
|
|
/* Open a new disk prober */
|
|
char *disk_path = blkid_devno_to_devname(disk);
|
|
|
|
if (!disk_path)
|
|
return NULL;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("allocate a wholedisk probe\n"));
|
|
|
|
pr->disk_probe = blkid_new_probe_from_filename(disk_path);
|
|
|
|
free(disk_path);
|
|
|
|
if (!pr->disk_probe)
|
|
return NULL; /* ENOMEM? */
|
|
}
|
|
|
|
return pr->disk_probe;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_size:
|
|
* @pr: probe
|
|
*
|
|
* This function returns size of probing area as defined by blkid_probe_set_device().
|
|
* If the size of the probing area is unrestricted then this function returns
|
|
* the real size of device. See also blkid_get_dev_size().
|
|
*
|
|
* Returns: size in bytes or -1 in case of error.
|
|
*/
|
|
blkid_loff_t blkid_probe_get_size(blkid_probe pr)
|
|
{
|
|
return pr ? pr->size : -1;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_offset:
|
|
* @pr: probe
|
|
*
|
|
* This function returns offset of probing area as defined by blkid_probe_set_device().
|
|
*
|
|
* Returns: offset in bytes or -1 in case of error.
|
|
*/
|
|
blkid_loff_t blkid_probe_get_offset(blkid_probe pr)
|
|
{
|
|
return pr ? pr->off : -1;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_fd:
|
|
* @pr: probe
|
|
*
|
|
* Returns: file descriptor for assigned device/file or -1 in case of error.
|
|
*/
|
|
int blkid_probe_get_fd(blkid_probe pr)
|
|
{
|
|
return pr ? pr->fd : -1;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_sectorsize:
|
|
* @pr: probe or NULL (for NULL returns 512)
|
|
*
|
|
* Returns: block device logical sector size (BLKSSZGET ioctl, default 512).
|
|
*/
|
|
unsigned int blkid_probe_get_sectorsize(blkid_probe pr)
|
|
{
|
|
if (!pr)
|
|
return DEFAULT_SECTOR_SIZE; /*... and good luck! */
|
|
|
|
if (pr->blkssz)
|
|
return pr->blkssz;
|
|
|
|
if (S_ISBLK(pr->mode) &&
|
|
blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0)
|
|
return pr->blkssz;
|
|
|
|
pr->blkssz = DEFAULT_SECTOR_SIZE;
|
|
return pr->blkssz;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_sectors:
|
|
* @pr: probe
|
|
*
|
|
* Returns: 512-byte sector count or -1 in case of error.
|
|
*/
|
|
blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
|
|
{
|
|
return pr ? pr->size >> 9 : -1;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_numof_values:
|
|
* @pr: probe
|
|
*
|
|
* Returns: number of values in probing result or -1 in case of error.
|
|
*/
|
|
int blkid_probe_numof_values(blkid_probe pr)
|
|
{
|
|
if (!pr)
|
|
return -1;
|
|
return pr->nvals;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_get_value:
|
|
* @pr: probe
|
|
* @num: wanted value in range 0..N, where N is blkid_probe_numof_values() - 1
|
|
* @name: pointer to return value name or NULL
|
|
* @data: pointer to return value data or NULL
|
|
* @len: pointer to return value length or NULL
|
|
*
|
|
* Note, the @len returns length of the @data, including the terminating
|
|
* '\0' character.
|
|
*
|
|
* Returns: 0 on success, or -1 in case of error.
|
|
*/
|
|
int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
|
|
const char **data, size_t *len)
|
|
{
|
|
struct blkid_prval *v = __blkid_probe_get_value(pr, num);
|
|
|
|
if (!v)
|
|
return -1;
|
|
if (name)
|
|
*name = v->name;
|
|
if (data)
|
|
*data = (char *) v->data;
|
|
if (len)
|
|
*len = v->len;
|
|
|
|
DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_lookup_value:
|
|
* @pr: probe
|
|
* @name: name of value
|
|
* @data: pointer to return value data or NULL
|
|
* @len: pointer to return value length or NULL
|
|
*
|
|
* Note, the @len returns length of the @data, including the terminating
|
|
* '\0' character.
|
|
*
|
|
* Returns: 0 on success, or -1 in case of error.
|
|
*/
|
|
int blkid_probe_lookup_value(blkid_probe pr, const char *name,
|
|
const char **data, size_t *len)
|
|
{
|
|
struct blkid_prval *v = __blkid_probe_lookup_value(pr, name);
|
|
|
|
if (!v)
|
|
return -1;
|
|
if (data)
|
|
*data = (char *) v->data;
|
|
if (len)
|
|
*len = v->len;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blkid_probe_has_value:
|
|
* @pr: probe
|
|
* @name: name of value
|
|
*
|
|
* Returns: 1 if value exist in probing result, otherwise 0.
|
|
*/
|
|
int blkid_probe_has_value(blkid_probe pr, const char *name)
|
|
{
|
|
if (blkid_probe_lookup_value(pr, name, NULL, NULL) == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num)
|
|
{
|
|
if (!pr || num < 0 || num >= pr->nvals)
|
|
return NULL;
|
|
|
|
return &pr->vals[num];
|
|
}
|
|
|
|
struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
|
|
{
|
|
int i;
|
|
|
|
if (!pr || !pr->nvals || !name)
|
|
return NULL;
|
|
|
|
for (i = 0; i < pr->nvals; i++) {
|
|
struct blkid_prval *v = &pr->vals[i];
|
|
|
|
if (v->name && strcmp(name, v->name) == 0) {
|
|
DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
|
|
return v;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* converts DCE UUID (uuid[16]) to human readable string
|
|
* - the @len should be always 37 */
|
|
#ifdef HAVE_LIBUUID
|
|
void blkid_unparse_uuid(const unsigned char *uuid, char *str,
|
|
size_t len __attribute__((__unused__)))
|
|
{
|
|
uuid_unparse(uuid, str);
|
|
}
|
|
#else
|
|
void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
|
|
{
|
|
snprintf(str, len,
|
|
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
uuid[0], uuid[1], uuid[2], uuid[3],
|
|
uuid[4], uuid[5],
|
|
uuid[6], uuid[7],
|
|
uuid[8], uuid[9],
|
|
uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],uuid[15]);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Removes whitespace from the right-hand side of a string (trailing
|
|
* whitespace).
|
|
*
|
|
* Returns size of the new string (without \0).
|
|
*/
|
|
size_t blkid_rtrim_whitespace(unsigned char *str)
|
|
{
|
|
size_t i = strlen((char *) str);
|
|
|
|
while (i--) {
|
|
if (!isspace(str[i]))
|
|
break;
|
|
}
|
|
str[++i] = '\0';
|
|
return i;
|
|
}
|
|
|
|
/* Removes whitespace from the left-hand side of a string.
|
|
*
|
|
* Returns size of the new string (without \0).
|
|
*/
|
|
size_t blkid_ltrim_whitespace(unsigned char *str)
|
|
{
|
|
size_t len;
|
|
unsigned char *p;
|
|
|
|
for (p = str; p && isspace(*p); p++);
|
|
|
|
len = strlen((char *) p);
|
|
|
|
if (len && p > str)
|
|
memmove(str, p, len + 1);
|
|
|
|
return len;
|
|
}
|
|
/*
|
|
* Some mkfs-like utils wipe some parts (usually begin) of the device.
|
|
* For example LVM (pvcreate) or mkswap(8). This information could be used
|
|
* for later resolution to conflicts between superblocks.
|
|
*
|
|
* For example we found valid LVM superblock, LVM wipes 8KiB at the begin of
|
|
* the device. If we found another signature (for example MBR) within the
|
|
* wiped area then the signature has been added later and LVM superblock
|
|
* should be ignore.
|
|
*
|
|
* Note that this heuristic is not 100% reliable, for example "pvcreate --zero
|
|
* n" allows to keep the begin of the device unmodified. It's probably better
|
|
* to use this heuristic for conflicts between superblocks and partition tables
|
|
* than for conflicts between filesystem superblocks -- existence of unwanted
|
|
* partition table is very unusual, because PT is pretty visible (parsed and
|
|
* interpreted by kernel).
|
|
*
|
|
* Note that we usually expect only one signature on the device, it means that
|
|
* we have to remember only one wiped area from previously successfully
|
|
* detected signature.
|
|
*
|
|
* blkid_probe_set_wiper() -- defines wiped area (e.g. LVM)
|
|
* blkid_probe_use_wiper() -- try to use area (e.g. MBR)
|
|
*
|
|
* Note that there is not relation between _wiper and blkid_to_wipe().
|
|
*
|
|
*/
|
|
void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
|
|
{
|
|
struct blkid_chain *chn;
|
|
|
|
if (!pr)
|
|
return;
|
|
|
|
if (!size) {
|
|
DBG(DEBUG_LOWPROBE, printf("zeroize wiper\n"));
|
|
pr->wipe_size = pr->wipe_off = 0;
|
|
pr->wipe_chain = NULL;
|
|
return;
|
|
}
|
|
|
|
chn = pr->cur_chain;
|
|
|
|
if (!chn || !chn->driver ||
|
|
chn->idx < 0 || (size_t) chn->idx >= chn->driver->nidinfos)
|
|
return;
|
|
|
|
pr->wipe_size = size;
|
|
pr->wipe_off = off;
|
|
pr->wipe_chain = chn;
|
|
|
|
DBG(DEBUG_LOWPROBE,
|
|
printf("wiper set to %s::%s off=%jd size=%jd\n",
|
|
chn->driver->name,
|
|
chn->driver->idinfos[chn->idx]->name,
|
|
pr->wipe_off, pr->wipe_size));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Returns 1 if the <@off,@size> area was wiped
|
|
*/
|
|
int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
|
|
blkid_loff_t off, blkid_loff_t size)
|
|
{
|
|
if (!pr || !size)
|
|
return 0;
|
|
|
|
if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) {
|
|
if (chn)
|
|
*chn = pr->wipe_chain;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Try to use any area -- if the area has been previously wiped then the
|
|
* previous probing result should be ignored (reseted).
|
|
*/
|
|
void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
|
|
{
|
|
struct blkid_chain *chn = NULL;
|
|
|
|
if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) {
|
|
DBG(DEBUG_LOWPROBE, printf("previously wiped area modified "
|
|
" -- ignore previous results\n"));
|
|
blkid_probe_set_wiper(pr, 0, 0);
|
|
blkid_probe_chain_reset_vals(pr, chn);
|
|
}
|
|
}
|
|
|
|
int blkid_probe_ignore_backup(blkid_probe pr)
|
|
{
|
|
return pr && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_BACKUP);
|
|
}
|