| /* |
| * 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> |
| #include <limits.h> |
| |
| #include "blkidP.h" |
| #include "all-io.h" |
| #include "sysfs.h" |
| #include "strutils.h" |
| #include "list.h" |
| |
| /* |
| * 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_values(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(LOWPROBE, ul_debug("allocate a new probe")); |
| |
| /* 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); |
| INIT_LIST_HEAD(&pr->values); |
| return pr; |
| } |
| |
| /* |
| * Clone @parent, the new clone shares all, but except: |
| * |
| * - probing result |
| * - buffers 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(LOWPROBE, ul_debug("allocate a probe clone")); |
| |
| 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; |
| blkid_probe pr = 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_buffers(pr); |
| blkid_probe_reset_values(pr); |
| blkid_free_probe(pr->disk_probe); |
| |
| DBG(LOWPROBE, ul_debug("free probe")); |
| free(pr); |
| } |
| |
| void blkid_probe_free_value(struct blkid_prval *v) |
| { |
| if (!v) |
| return; |
| |
| list_del(&v->prvals); |
| free(v->data); |
| |
| DBG(LOWPROBE, ul_debug(" free value %s", v->name)); |
| free(v); |
| } |
| |
| /* |
| * Removes chain values from probing result. |
| */ |
| void blkid_probe_chain_reset_values(blkid_probe pr, struct blkid_chain *chn) |
| { |
| |
| struct list_head *p, *pnext; |
| |
| if (list_empty(&pr->values)) |
| return; |
| |
| DBG(LOWPROBE, ul_debug("Resetting %s values", chn->driver->name)); |
| |
| list_for_each_safe(p, pnext, &pr->values) { |
| struct blkid_prval *v = list_entry(p, |
| struct blkid_prval, prvals); |
| |
| if (v->chain == chn) |
| blkid_probe_free_value(v); |
| } |
| } |
| |
| static void blkid_probe_chain_reset_position(struct blkid_chain *chn) |
| { |
| chn->idx = -1; |
| } |
| |
| /* |
| * Move chain values from probing result to @vals |
| */ |
| int blkid_probe_chain_save_values(blkid_probe pr, struct blkid_chain *chn, |
| struct list_head *vals) |
| { |
| struct list_head *p, *pnext; |
| struct blkid_prval *v; |
| |
| DBG(LOWPROBE, ul_debug("saving %s values", chn->driver->name)); |
| |
| list_for_each_safe(p, pnext, &pr->values) { |
| |
| v = list_entry(p, struct blkid_prval, prvals); |
| if (v->chain != chn) |
| continue; |
| |
| list_del(&v->prvals); |
| INIT_LIST_HEAD(&v->prvals); |
| |
| list_add_tail(&v->prvals, vals); |
| } |
| return 0; |
| } |
| |
| /* |
| * Appends values from @vals to the probing result |
| */ |
| void blkid_probe_append_values_list(blkid_probe pr, struct list_head *vals) |
| { |
| DBG(LOWPROBE, ul_debug("appending values")); |
| |
| list_splice(vals, &pr->values); |
| INIT_LIST_HEAD(vals); |
| } |
| |
| |
| void blkid_probe_free_values_list(struct list_head *vals) |
| { |
| if (!vals) |
| return; |
| |
| DBG(LOWPROBE, ul_debug("freeing values list")); |
| |
| while (!list_empty(vals)) { |
| struct blkid_prval *v = list_entry(vals->next, struct blkid_prval, prvals); |
| blkid_probe_free_value(v); |
| } |
| } |
| |
| struct blkid_chain *blkid_probe_get_chain(blkid_probe pr) |
| { |
| return pr->cur_chain; |
| } |
| |
| static const char *blkid_probe_get_probername(blkid_probe pr) |
| { |
| struct blkid_chain *chn = blkid_probe_get_chain(pr); |
| |
| if (chn && chn->idx >= 0 && (unsigned)chn->idx < chn->driver->nidinfos) |
| return chn->driver->idinfos[chn->idx]->name; |
| |
| return NULL; |
| } |
| |
| void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn) |
| { |
| int rc, org_prob_flags; |
| struct blkid_chain *org_chn; |
| |
| /* 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(LOWPROBE, ul_debug("returning %s binary data", 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; |
| |
| blkid_probe_reset_values(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(LOWPROBE, ul_debug("%d: %s: %s", |
| 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 (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; |
| |
| 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(LOWPROBE, ul_debug("probing filter inverted")); |
| /* 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 (has) { |
| if (flag & BLKID_FLTR_NOTIN) |
| blkid_bmp_set_item(fltr, i); |
| } else if (flag & BLKID_FLTR_ONLYIN) |
| blkid_bmp_set_item(fltr, i); |
| } |
| |
| DBG(LOWPROBE, ul_debug("%s: a new probing type-filter initialized", |
| chn->driver->name)); |
| /* blkid_probe_dump_filter(pr, chain); */ |
| return 0; |
| } |
| |
| static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len) |
| { |
| ssize_t ret; |
| struct blkid_bufinfo *bf = NULL; |
| |
| if (blkid_llseek(pr->fd, real_off, SEEK_SET) < 0) { |
| errno = 0; |
| return NULL; |
| } |
| |
| /* someone trying to overflow some buffers? */ |
| if (len > ULONG_MAX - sizeof(struct blkid_bufinfo)) { |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| /* allocate info and space for data by one malloc call */ |
| bf = calloc(1, sizeof(struct blkid_bufinfo) + len); |
| if (!bf) { |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo); |
| bf->len = len; |
| bf->off = real_off; |
| INIT_LIST_HEAD(&bf->bufs); |
| |
| DBG(LOWPROBE, ul_debug("\tread: off=%"PRIu64" len=%"PRIu64"", |
| real_off, len)); |
| |
| ret = read(pr->fd, bf->data, len); |
| if (ret != (ssize_t) len) { |
| DBG(LOWPROBE, ul_debug("\tread failed: %m")); |
| free(bf); |
| |
| /* I/O errors on CDROMs are non-fatal to work with hybrid |
| * audio+data disks */ |
| if (ret >= 0 || blkid_probe_is_cdrom(pr)) |
| errno = 0; |
| return NULL; |
| } |
| |
| return bf; |
| } |
| |
| /* |
| * Search in buffers we already in memory |
| */ |
| static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uint64_t len) |
| { |
| uint64_t real_off = pr->off + off; |
| struct list_head *p; |
| |
| list_for_each(p, &pr->buffers) { |
| struct blkid_bufinfo *x = |
| list_entry(p, struct blkid_bufinfo, bufs); |
| |
| if (real_off >= x->off && real_off + len <= x->off + x->len) { |
| DBG(BUFFER, ul_debug("\treuse: off=%"PRIu64" len=%"PRIu64" (for off=%"PRIu64" len=%"PRIu64")", |
| x->off, x->len, real_off, len)); |
| return x; |
| } |
| } |
| return NULL; |
| } |
| |
| /* |
| * Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer() |
| * will return modified buffer. This is usable when you want to call the same probing |
| * function more than once and hide previously detected magic strings. |
| * |
| * See blkid_probe_hide_range(). |
| */ |
| static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len) |
| { |
| uint64_t real_off = pr->off + off; |
| struct list_head *p; |
| int ct = 0; |
| |
| list_for_each(p, &pr->buffers) { |
| struct blkid_bufinfo *x = |
| list_entry(p, struct blkid_bufinfo, bufs); |
| unsigned char *data; |
| |
| if (real_off >= x->off && real_off + len <= x->off + x->len) { |
| |
| assert(x->off <= real_off); |
| assert(x->off + x->len >= real_off + len); |
| |
| data = real_off ? x->data + (real_off - x->off) : x->data; |
| |
| DBG(BUFFER, ul_debug("\thidding: off=%"PRIu64" len=%"PRIu64, |
| off, len)); |
| memset(data, 0, len); |
| ct++; |
| } |
| } |
| return ct == 0 ? -EINVAL : 0; |
| } |
| |
| |
| /* |
| * Note that @off is offset within probing area, the probing area is defined by |
| * pr->off and pr->size. |
| */ |
| unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len) |
| { |
| struct blkid_bufinfo *bf = NULL; |
| uint64_t real_off = pr->off + off; |
| |
| /* |
| DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju", |
| off, real_off, pr->off, pr->off + pr->size, len)); |
| */ |
| |
| if (pr->size == 0) { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (len == 0 || (!S_ISCHR(pr->mode) && pr->off + pr->size < real_off + len)) { |
| DBG(BUFFER, ul_debug("\t ignore: request out of probing area")); |
| errno = 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 |
| * begin of the device. |
| */ |
| return blkid_probe_get_buffer(pr->parent, |
| pr->off + off - pr->parent->off, len); |
| } |
| |
| /* try buffers we already have in memory or read from device */ |
| bf = get_cached_buffer(pr, off, len); |
| if (!bf) { |
| bf = read_buffer(pr, real_off, len); |
| if (!bf) |
| return NULL; |
| |
| list_add_tail(&bf->bufs, &pr->buffers); |
| } |
| |
| assert(bf->off <= real_off); |
| assert(bf->off + bf->len >= real_off + len); |
| |
| errno = 0; |
| return real_off ? bf->data + (real_off - bf->off) : bf->data; |
| } |
| |
| /** |
| * blkid_probe_reset_buffers: |
| * @pr: prober |
| * |
| * libblkid reuse all already read buffers from the device. The bufferes may be |
| * modified by blkid_probe_hide_range(). This function reset and free all |
| * cached bufferes. The next blkid_do_probe() will read all data from the |
| * device. |
| * |
| * Returns: <0 in case of failure, or 0 on success. |
| */ |
| int blkid_probe_reset_buffers(blkid_probe pr) |
| { |
| uint64_t ct = 0, len = 0; |
| |
| pr->flags &= ~BLKID_FL_MODIF_BUFF; |
| |
| if (list_empty(&pr->buffers)) |
| return 0; |
| |
| DBG(BUFFER, ul_debug("Resetting probing buffers")); |
| |
| while (!list_empty(&pr->buffers)) { |
| struct blkid_bufinfo *bf = list_entry(pr->buffers.next, |
| struct blkid_bufinfo, bufs); |
| ct++; |
| len += bf->len; |
| list_del(&bf->bufs); |
| |
| DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]", |
| bf->off, bf->len)); |
| free(bf); |
| } |
| |
| DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls", |
| len, ct)); |
| |
| INIT_LIST_HEAD(&pr->buffers); |
| |
| return 0; |
| } |
| |
| /** |
| * blkid_probe_hide_range: |
| * @pr: prober |
| * @off: start of the range |
| * @len: size of the range |
| * |
| * This function modifies in-memory cached data from the device. The specified |
| * range is zeroized. This is usable together with blkid_probe_step_back(). |
| * The next blkid_do_probe() will not see specified area. |
| * |
| * Note that this is usable for already (by library) read data, and this |
| * function is not a way how to hide any large areas on your device. |
| * |
| * The function blkid_probe_reset_buffers() reverts all. |
| * |
| * Returns: <0 in case of failure, or 0 on success. |
| */ |
| int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len) |
| { |
| int rc = hide_buffer(pr, off, len); |
| |
| if (rc == 0) |
| pr->flags |= BLKID_FL_MODIF_BUFF; |
| return rc; |
| } |
| |
| static void blkid_probe_reset_values(blkid_probe pr) |
| { |
| if (list_empty(&pr->values)) |
| return; |
| |
| DBG(LOWPROBE, ul_debug("resetting results")); |
| |
| while (!list_empty(&pr->values)) { |
| struct blkid_prval *v = list_entry(pr->values.next, |
| struct blkid_prval, prvals); |
| blkid_probe_free_value(v); |
| } |
| |
| INIT_LIST_HEAD(&pr->values); |
| } |
| |
| /* |
| * Small devices need a special care. |
| */ |
| int blkid_probe_is_tiny(blkid_probe pr) |
| { |
| return (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->flags & BLKID_FL_CDROM_DEV); |
| } |
| |
| #ifdef CDROM_GET_CAPABILITY |
| |
| static int is_sector_readable(int fd, uint64_t sector) |
| { |
| char buf[512]; |
| ssize_t sz; |
| |
| if (blkid_llseek(fd, sector * 512, SEEK_SET) < 0) |
| goto failed; |
| |
| sz = read(fd, buf, sizeof(buf)); |
| if (sz != (ssize_t) sizeof(buf)) |
| goto failed; |
| |
| return 1; |
| failed: |
| DBG(LOWPROBE, ul_debug("CDROM: read sector %"PRIu64" failed %m", sector)); |
| errno = 0; |
| return 0; |
| } |
| |
| /* |
| * Linux kernel reports (BLKGETSIZE) cdrom device size greater than area |
| * readable by read(2). We have to reduce the probing area to avoid unwanted |
| * I/O errors in probing functions. It seems that unreadable are always last 2 |
| * or 3 CD blocks (CD block size is 2048 bytes, it means 12 in 512-byte |
| * sectors). |
| */ |
| static void cdrom_size_correction(blkid_probe pr) |
| { |
| uint64_t n, nsectors = pr->size >> 9; |
| |
| for (n = nsectors - 12; n < nsectors; n++) { |
| if (!is_sector_readable(pr->fd, n)) |
| goto failed; |
| } |
| |
| DBG(LOWPROBE, ul_debug("CDROM: full size available")); |
| return; |
| failed: |
| /* 'n' is the failed sector, reduce device size to n-1; */ |
| DBG(LOWPROBE, ul_debug("CDROM: reduce size from %ju to %ju.", |
| (uintmax_t) pr->size, |
| (uintmax_t) n << 9)); |
| pr->size = n << 9; |
| } |
| |
| #endif |
| |
| /** |
| * 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; |
| uint64_t devsiz = 0; |
| char *dm_uuid = NULL; |
| |
| blkid_reset_probe(pr); |
| blkid_probe_reset_buffers(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 = (uint64_t) 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)) { |
| errno = EINVAL; |
| goto err; |
| } |
| |
| pr->mode = sb.st_mode; |
| if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) |
| pr->devno = sb.st_rdev; |
| |
| if (S_ISBLK(sb.st_mode)) { |
| if (blkdev_get_size(fd, (unsigned long long *) &devsiz)) { |
| DBG(LOWPROBE, ul_debug("failed to get device size")); |
| goto err; |
| } |
| } else if (S_ISCHR(sb.st_mode)) |
| devsiz = 1; /* UBI devices are char... */ |
| else if (S_ISREG(sb.st_mode)) |
| devsiz = sb.st_size; /* regular file */ |
| |
| pr->size = size ? (uint64_t)size : devsiz; |
| |
| if (off && size == 0) |
| /* only offset without size specified */ |
| pr->size -= (uint64_t) off; |
| |
| if (pr->off + pr->size > devsiz) { |
| DBG(LOWPROBE, ul_debug("area specified by offset and size is bigger than device")); |
| errno = EINVAL; |
| goto err; |
| } |
| |
| if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode)) |
| pr->flags |= BLKID_FL_TINY_DEV; |
| |
| if (S_ISBLK(sb.st_mode) && |
| sysfs_devno_is_dm_private(sb.st_rdev, &dm_uuid)) { |
| DBG(LOWPROBE, ul_debug("ignore private device mapper device")); |
| pr->flags |= BLKID_FL_NOSCAN_DEV; |
| } |
| |
| #ifdef CDROM_GET_CAPABILITY |
| else if (S_ISBLK(sb.st_mode) && |
| !blkid_probe_is_tiny(pr) && |
| !dm_uuid && |
| blkid_probe_is_wholedisk(pr) && |
| ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) { |
| |
| pr->flags |= BLKID_FL_CDROM_DEV; |
| cdrom_size_correction(pr); |
| } |
| #endif |
| free(dm_uuid); |
| |
| DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64"", |
| pr->off, pr->size)); |
| DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s", |
| blkid_probe_is_wholedisk(pr) ?"YES" : "NO", |
| S_ISREG(pr->mode) ? "YES" : "NO")); |
| |
| return 0; |
| err: |
| DBG(LOWPROBE, ul_debug("failed to prepare a device for low-probing")); |
| return -1; |
| |
| } |
| |
| int blkid_probe_get_dimension(blkid_probe pr, uint64_t *off, uint64_t *size) |
| { |
| *off = pr->off; |
| *size = pr->size; |
| return 0; |
| } |
| |
| int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size) |
| { |
| DBG(LOWPROBE, ul_debug( |
| "changing probing area: size=%"PRIu64", off=%"PRIu64" " |
| "-to-> size=%"PRIu64", off=%"PRIu64"", |
| pr->size, pr->off, size, off)); |
| |
| pr->off = off; |
| pr->size = size; |
| pr->flags &= ~BLKID_FL_TINY_DEV; |
| |
| if (pr->size <= 1440ULL * 1024ULL && !S_ISCHR(pr->mode)) |
| pr->flags |= BLKID_FL_TINY_DEV; |
| |
| blkid_probe_reset_buffers(pr); |
| |
| return 0; |
| } |
| |
| /* |
| * Check for matching magic value. |
| * Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found |
| * or no magic present, or negative value on error. |
| */ |
| int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, |
| uint64_t *offset, const struct blkid_idmag **res) |
| { |
| const struct blkid_idmag *mag = NULL; |
| uint64_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 && errno) |
| return -errno; |
| |
| if (buf && !memcmp(mag->magic, |
| buf + (mag->sboff & 0x3ff), mag->len)) { |
| |
| DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld", |
| mag->sboff, mag->kboff)); |
| if (offset) |
| *offset = off + (mag->sboff & 0x3ff); |
| if (res) |
| *res = mag; |
| return BLKID_PROBE_OK; |
| } |
| mag++; |
| } |
| |
| if (id && id->magics[0].magic) |
| /* magic string(s) defined, but not found */ |
| return BLKID_PROBE_NONE; |
| |
| return BLKID_PROBE_OK; |
| } |
| |
| static inline void blkid_probe_start(blkid_probe pr) |
| { |
| DBG(LOWPROBE, ul_debug("start probe")); |
| pr->cur_chain = NULL; |
| pr->prob_flags = 0; |
| blkid_probe_set_wiper(pr, 0, 0); |
| } |
| |
| static inline void blkid_probe_end(blkid_probe pr) |
| { |
| DBG(LOWPROBE, ul_debug("end probe")); |
| 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->flags & BLKID_FL_NOSCAN_DEV) |
| 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(LOWPROBE, ul_debug("chain probe %s %s (idx=%d)", |
| 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 (if you want to erase also superblock with broken check |
| * sums then use BLKID_SUBLKS_BADCSUM too). |
| * |
| * 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. All in-memory cached data from the device are always |
| * reset. |
| * |
| * <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; |
| uint64_t offset, magoff, l; |
| char buf[BUFSIZ]; |
| int fd, rc = 0; |
| struct blkid_chain *chn; |
| |
| 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; |
| |
| magoff = strtoumax(off, NULL, 10); |
| offset = magoff + pr->off; |
| fd = blkid_probe_get_fd(pr); |
| if (fd < 0) |
| return -1; |
| |
| if (len > sizeof(buf)) |
| len = sizeof(buf); |
| |
| DBG(LOWPROBE, ul_debug( |
| "do_wipe [offset=0x%"PRIx64" (%"PRIu64"), len=%zu, chain=%s, idx=%d, dryrun=%s]\n", |
| offset, offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not")); |
| |
| l = blkid_llseek(fd, offset, SEEK_SET); |
| if ((blkid_loff_t)l == (off_t) -1) |
| return -1; |
| |
| memset(buf, 0, len); |
| |
| if (!dryrun && len) { |
| /* wipen on device */ |
| if (write_all(fd, buf, len)) |
| return -1; |
| fsync(fd); |
| pr->flags &= ~BLKID_FL_MODIF_BUFF; /* be paranoid */ |
| |
| return blkid_probe_step_back(pr); |
| |
| } else if (dryrun) { |
| /* wipe in memory only */ |
| blkid_probe_hide_range(pr, magoff, len); |
| 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. |
| * |
| * Note that blkid_probe_hide_range() changes semantic of this function and |
| * cached bufferes are not reset, but library uses in-memory modified |
| * buffers to call the next probing function. |
| * |
| * <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); |
| * |
| * blkid_probe_enable_partitions(pr, 1); |
| * blkid_probe_set_partitions_flags(pr, BLKID_PARTS_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 erase @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; |
| |
| chn = pr->cur_chain; |
| if (!chn) |
| return -1; |
| |
| if (!(pr->flags & BLKID_FL_MODIF_BUFF)) |
| blkid_probe_reset_buffers(pr); |
| |
| if (chn->idx >= 0) { |
| chn->idx--; |
| DBG(LOWPROBE, ul_debug("step back: moving %s chain index to %d", |
| 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(LOWPROBE, ul_debug("step back: moving to previous chain")); |
| |
| 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 superblocks 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 ambivalent result is |
| * detected and -1 on case of error. |
| */ |
| int blkid_do_safeprobe(blkid_probe pr) |
| { |
| int i, count = 0, rc = 0; |
| |
| if (pr->flags & BLKID_FL_NOSCAN_DEV) |
| 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(LOWPROBE, ul_debug("chain safeprobe %s %s", |
| 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->flags & BLKID_FL_NOSCAN_DEV) |
| 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(LOWPROBE, ul_debug("chain fullprobe %s: %s", |
| 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 blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200); |
| } |
| |
| struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, const char *name) |
| { |
| struct blkid_prval *v; |
| |
| v = calloc(1, sizeof(struct blkid_prval)); |
| if (!v) |
| return NULL; |
| |
| INIT_LIST_HEAD(&v->prvals); |
| v->name = name; |
| v->chain = pr->cur_chain; |
| list_add_tail(&v->prvals, &pr->values); |
| |
| DBG(LOWPROBE, ul_debug("assigning %s [%s]", name, v->chain->driver->name)); |
| return v; |
| } |
| |
| /* Note that value data is always terminated by zero to keep things robust, |
| * this extra zero is not count to the value length. It's caller responsibility |
| * to set proper value length (for strings we count terminator to the length, |
| * for binary data it's without terminator). |
| */ |
| int blkid_probe_value_set_data(struct blkid_prval *v, |
| const unsigned char *data, size_t len) |
| { |
| v->data = calloc(1, len + 1); /* always terminate by \0 */ |
| |
| if (!v->data) |
| return -ENOMEM; |
| memcpy(v->data, data, len); |
| v->len = len; |
| return 0; |
| } |
| |
| int blkid_probe_set_value(blkid_probe pr, const char *name, |
| const unsigned char *data, size_t len) |
| { |
| struct blkid_prval *v; |
| |
| v = blkid_probe_assign_value(pr, name); |
| if (!v) |
| return -1; |
| |
| return blkid_probe_value_set_data(v, data, len); |
| } |
| |
| 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 -ENOMEM; |
| |
| len = vasprintf((char **) &v->data, fmt, ap); |
| |
| if (len <= 0) { |
| blkid_probe_free_value(v); |
| return len == 0 ? -EINVAL : -ENOMEM; |
| } |
| 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, uint64_t offset, |
| size_t len, const unsigned char *magic) |
| { |
| int rc = 0; |
| struct blkid_chain *chn = blkid_probe_get_chain(pr); |
| |
| if (!chn || !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; |
| } |
| |
| int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected) |
| { |
| if (csum != expected) { |
| struct blkid_chain *chn = blkid_probe_get_chain(pr); |
| |
| DBG(LOWPROBE, ul_debug( |
| "incorrect checksum for type %s," |
| " got %"PRIX64", expected %"PRIX64"", |
| blkid_probe_get_probername(pr), |
| csum, expected)); |
| /* |
| * Accept bad checksum if BLKID_SUBLKS_BADCSUM flags is set |
| */ |
| if (chn->driver->id == BLKID_CHAIN_SUBLKS |
| && (chn->flags & BLKID_SUBLKS_BADCSUM)) { |
| blkid_probe_set_value(pr, "SBBADCSUM", (unsigned char *) "1", 2); |
| goto accept; |
| } |
| return 0; /* bad checksum */ |
| } |
| |
| accept: |
| return 1; |
| } |
| |
| /** |
| * 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(LOWPROBE, ul_debug("allocate a wholedisk probe")); |
| |
| 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 (blkid_loff_t) pr->size; |
| } |
| |
| /** |
| * 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 (blkid_loff_t) pr->off; |
| } |
| |
| /** |
| * 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->fd; |
| } |
| |
| /** |
| * 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->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_set_sectorsize: |
| * @pr: probe |
| * @sz: new size (to overwrite system default) |
| * |
| * Note that blkid_probe_set_device() resets this setting. Use it after |
| * blkid_probe_set_device() and before any probing call. |
| * |
| * Since: 2.30 |
| * |
| * Returns: 0 or <0 in case of error |
| */ |
| int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz) |
| { |
| pr->blkssz = sz; |
| return 0; |
| } |
| |
| /** |
| * 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 (blkid_loff_t) (pr->size >> 9); |
| } |
| |
| /** |
| * 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) |
| { |
| int i = 0; |
| struct list_head *p; |
| |
| list_for_each(p, &pr->values) |
| ++i; |
| return i; |
| } |
| |
| /** |
| * 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(LOWPROBE, ul_debug("returning %s value", 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) |
| { |
| int i = 0; |
| struct list_head *p; |
| |
| if (num < 0) |
| return NULL; |
| |
| list_for_each(p, &pr->values) { |
| if (i++ != num) |
| continue; |
| return list_entry(p, struct blkid_prval, prvals); |
| } |
| return NULL; |
| } |
| |
| struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name) |
| { |
| struct list_head *p; |
| |
| if (list_empty(&pr->values)) |
| return NULL; |
| |
| list_for_each(p, &pr->values) { |
| struct blkid_prval *v = list_entry(p, struct blkid_prval, |
| prvals); |
| |
| if (v->name && strcmp(name, v->name) == 0) { |
| DBG(LOWPROBE, ul_debug("returning %s value", 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 |
| |
| /* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */ |
| int blkid_uuid_is_empty(const unsigned char *buf, size_t len) |
| { |
| size_t i; |
| |
| for (i = 0; i < len; i++) |
| if (buf[i]) |
| return 0; |
| return 1; |
| } |
| |
| /* 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) |
| { |
| return rtrim_whitespace(str); |
| } |
| |
| /* 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) |
| { |
| return ltrim_whitespace(str); |
| } |
| |
| /* |
| * 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, uint64_t off, uint64_t size) |
| { |
| struct blkid_chain *chn; |
| |
| if (!size) { |
| DBG(LOWPROBE, ul_debug("zeroize wiper")); |
| 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(LOWPROBE, |
| ul_debug("wiper set to %s::%s off=%"PRIu64" size=%"PRIu64"", |
| 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, uint64_t off, uint64_t size) |
| { |
| if (!size) |
| return 0; |
| |
| if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) { |
| *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 (reset). |
| */ |
| void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size) |
| { |
| struct blkid_chain *chn = NULL; |
| |
| if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) { |
| DBG(LOWPROBE, ul_debug("previously wiped area modified " |
| " -- ignore previous results")); |
| blkid_probe_set_wiper(pr, 0, 0); |
| blkid_probe_chain_reset_values(pr, chn); |
| } |
| } |