| /* |
| * 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 librray 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 reseted 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. |
| * |
| */ |
| |
| /** |
| * 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_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| #ifdef HAVE_SYS_MKDEV_H |
| #include <sys/mkdev.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #include <stdint.h> |
| #include <stdarg.h> |
| |
| #ifdef HAVE_LIBUUID |
| # ifdef HAVE_UUID_UUID_H |
| # include <uuid/uuid.h> |
| # else |
| # include <uuid.h> |
| # endif |
| #endif |
| |
| #include "blkdev.h" |
| #include "blkidP.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. |
| */ |
| 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; |
| |
| /* 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; |
| } |
| |
| /** |
| * 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); |
| 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_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_PRIVATE_FD) && pr->fd >= 0) |
| close(pr->fd); |
| blkid_probe_reset_buffer(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; |
| |
| if (!pr || !chn) |
| return NULL; |
| |
| pr->cur_chain = chn; |
| chn->binary = TRUE; |
| blkid_probe_chain_reset_position(chn); |
| |
| rc = chn->driver->probe(pr, chn); |
| |
| chn->binary = FALSE; |
| pr->cur_chain = NULL; |
| blkid_probe_chain_reset_position(chn); |
| |
| 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_buffer(pr); |
| blkid_probe_reset_vals(pr); |
| |
| 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 reseted 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) |
| { |
| int i; |
| struct blkid_chain *chn; |
| unsigned long *fltr; |
| |
| fltr = blkid_probe_get_filter(pr, chain, FALSE); |
| if (!fltr) |
| return -1; |
| |
| chn = &pr->chains[chain]; |
| |
| for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++) |
| fltr[i] = ~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; |
| int 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; |
| |
| 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\n", |
| x->off, x->len)); |
| 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\n", off, len)); |
| |
| 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) |
| { |
| ssize_t read_ct = 0, len_ct = 0; |
| |
| if (!pr || list_empty(&pr->buffers)) |
| return; |
| |
| DBG(DEBUG_LOWPROBE, printf("reseting probing buffers\n")); |
| |
| 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: %jd bytes by %jd 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_TINY_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) |
| { |
| if (!pr) |
| return -1; |
| |
| blkid_reset_probe(pr); |
| |
| if ((pr->flags & BLKID_PRIVATE_FD) && pr->fd >= 0) |
| close(pr->fd); |
| |
| pr->flags &= ~BLKID_PRIVATE_FD; |
| pr->flags &= ~BLKID_TINY_DEV; |
| pr->fd = fd; |
| pr->off = off; |
| pr->size = 0; |
| pr->devno = 0; |
| pr->mode = 0; |
| pr->blkssz = 0; |
| |
| #if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE) |
| /* Disable read-ahead */ |
| posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); |
| #endif |
| if (size) |
| pr->size = size; |
| else { |
| struct stat sb; |
| |
| if (fstat(fd, &sb)) |
| goto err; |
| |
| pr->mode = sb.st_mode; |
| |
| if (S_ISBLK(sb.st_mode)) |
| blkdev_get_size(fd, (unsigned long long *) &pr->size); |
| 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 (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) |
| pr->devno = sb.st_rdev; |
| |
| 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; |
| } |
| |
| DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n", |
| pr->off, pr->size)); |
| |
| if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode)) |
| pr->flags |= BLKID_TINY_DEV; |
| |
| 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: size=%llu, off=%llu " |
| "-to-> size=%llu, off=%llu\n", |
| (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_TINY_DEV; |
| |
| if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode)) |
| pr->flags |= BLKID_TINY_DEV; |
| |
| blkid_probe_reset_buffer(pr); |
| |
| return 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 reseted |
| * 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) |
| 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 == chn->driver->nidinfos || |
| chn->idx == -1)) { |
| |
| int idx = chn->driver->id + 1; |
| |
| if (idx < BLKID_NCHAINS) |
| chn = pr->cur_chain = &pr->chains[idx]; |
| else |
| 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_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. |
| * |
| * 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; |
| |
| 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: |
| pr->cur_chain = NULL; |
| 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_so_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; |
| |
| for (i = 0; i < BLKID_NCHAINS; i++) { |
| int rc; |
| 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: |
| pr->cur_chain = NULL; |
| 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; |
| size_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) { |
| 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; |
| } |
| |
| /** |
| * blkid_probe_get_devno: |
| * @pr: probe |
| * |
| * Returns: block device number, or 0 for regilar files. |
| */ |
| dev_t blkid_probe_get_devno(blkid_probe pr) |
| { |
| if (!pr->devno) { |
| struct stat sb; |
| |
| if (fstat(pr->fd, &sb) == 0 && |
| (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))) |
| pr->devno = sb.st_rdev; |
| } |
| return pr->devno; |
| } |
| |
| /** |
| * blkid_probe_get_size: |
| * @pr: probe |
| * |
| * Returns: block device (or file) 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_sectorsize: |
| * @pr: probe |
| * |
| * 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 (!pr->mode) { |
| struct stat st; |
| |
| if (fstat(pr->fd, &st)) |
| goto fallback; |
| pr->mode = st.st_mode; |
| } |
| if (S_ISBLK(pr->mode)) { |
| if (blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz)) |
| goto fallback; |
| |
| return pr->blkssz; |
| } |
| |
| fallback: |
| pr->blkssz = DEFAULT_SECTOR_SIZE; |
| return pr->blkssz; |
| } |
| |
| /** |
| * 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 == NULL || 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 == NULL || pr->nvals == 0 || name == NULL) |
| 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 */ |
| void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len) |
| { |
| #ifdef HAVE_LIBUUID |
| uuid_unparse(uuid, str); |
| #else |
| 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; |
| } |
| |