]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - shlibs/blkid/src/probe.c
build-sys: include <uuid.h> rather than <uuid/uuid.h>
[thirdparty/util-linux.git] / shlibs / blkid / src / probe.c
index 6d61386c1e718d7f1d293324ea0eab1214603484..8b8982509553b55eae78c53a448f1f6af2e0348b 100644 (file)
@@ -16,7 +16,7 @@
  * 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 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).
  *   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.
  */
 
 /**
 #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_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdarg.h>
 
 #ifdef HAVE_LIBUUID
-# ifdef HAVE_UUID_UUID_H
-#  include <uuid/uuid.h>
-# else
-#  include <uuid.h>
-# endif
+# include <uuid.h>
 #endif
 
-#include "blkdev.h"
 #include "blkidP.h"
 
 /* chains */
@@ -287,21 +297,33 @@ struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
 
 void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
 {
-       int rc;
+       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;
-       pr->cur_chain = NULL;
        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;
 
@@ -399,16 +421,14 @@ 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];
 
+       if (!chn->driver->has_fltr || !chn->fltr)
+               return -1;
+
        for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++)
-               fltr[i] = ~fltr[i];
+               chn->fltr[i] = ~chn->fltr[i];
 
        DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
        /* blkid_probe_dump_filter(pr, chain); */
@@ -513,7 +533,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr,
 
 static void blkid_probe_reset_buffer(blkid_probe pr)
 {
-       ssize_t read_ct = 0, len_ct = 0;
+       uint64_t read_ct = 0, len_ct = 0;
 
        if (!pr || list_empty(&pr->buffers))
                return;
@@ -531,7 +551,8 @@ static void blkid_probe_reset_buffer(blkid_probe pr)
        }
 
        DBG(DEBUG_LOWPROBE,
-               printf("buffers summary: %jd bytes by %jd read() call(s)\n",
+               printf("buffers summary: %"PRIu64" bytes "
+                       "by %"PRIu64" read() call(s)\n",
                        len_ct, read_ct));
 
        INIT_LIST_HEAD(&pr->buffers);
@@ -545,6 +566,14 @@ int blkid_probe_is_tiny(blkid_probe pr)
        return pr && (pr->flags & BLKID_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_CDROM_DEV);
+}
+
 /**
  * blkid_probe_set_device:
  * @pr: probe
@@ -560,6 +589,8 @@ int blkid_probe_is_tiny(blkid_probe pr)
 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;
 
@@ -570,6 +601,7 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
 
        pr->flags &= ~BLKID_PRIVATE_FD;
        pr->flags &= ~BLKID_TINY_DEV;
+       pr->flags &= ~BLKID_CDROM_DEV;
        pr->fd = fd;
        pr->off = off;
        pr->size = 0;
@@ -581,16 +613,19 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
        /* 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;
 
-               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;
+       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(
@@ -602,9 +637,6 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
                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;
 
@@ -613,11 +645,19 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
                pr->size -= pr->off;
        }
 
+       if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+               pr->flags |= BLKID_TINY_DEV;
+
+#ifdef CDROM_GET_CAPABILITY
+       if (S_ISBLK(sb.st_mode) && ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
+               pr->flags |= BLKID_CDROM_DEV;
+#endif
+
        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;
+       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:
@@ -664,6 +704,63 @@ int blkid_probe_set_dimension(blkid_probe 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 ? &id->magics[0] : NULL;
+       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->magics && 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) {
+               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) {
+               pr->cur_chain = NULL;
+               pr->prob_flags = 0;
+               blkid_probe_set_wiper(pr, 0, 0);
+       }
+}
+
 /**
  * blkid_do_probe:
  * @pr: prober
@@ -715,23 +812,28 @@ int blkid_do_probe(blkid_probe pr)
        do {
                struct blkid_chain *chn = pr->cur_chain;
 
-               if (!chn)
+               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)
+                * 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 == chn->driver->nidinfos ||
+                                    chn->idx == -1)) {
 
                        int idx = chn->driver->id + 1;
 
                        if (idx < BLKID_NCHAINS)
                                chn = pr->cur_chain = &pr->chains[idx];
-                       else
+                       else {
+                               blkid_probe_end(pr);
                                return 1;       /* all chains already probed */
+                       }
                }
 
                chn->binary = FALSE;            /* for sure... */
@@ -763,7 +865,9 @@ int blkid_do_probe(blkid_probe pr)
  *
  * 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.
+ * 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.
@@ -775,6 +879,8 @@ int blkid_do_safeprobe(blkid_probe pr)
        if (!pr)
                return -1;
 
+       blkid_probe_start(pr);
+
        for (i = 0; i < BLKID_NCHAINS; i++) {
                struct blkid_chain *chn;
 
@@ -802,7 +908,7 @@ int blkid_do_safeprobe(blkid_probe pr)
        }
 
 done:
-       pr->cur_chain = NULL;
+       blkid_probe_end(pr);
        if (rc < 0)
                return rc;
        return count ? 0 : 1;
@@ -827,6 +933,8 @@ int blkid_do_fullprobe(blkid_probe pr)
        if (!pr)
                return -1;
 
+       blkid_probe_start(pr);
+
        for (i = 0; i < BLKID_NCHAINS; i++) {
                int rc;
                struct blkid_chain *chn;
@@ -855,7 +963,7 @@ int blkid_do_fullprobe(blkid_probe pr)
        }
 
 done:
-       pr->cur_chain = NULL;
+       blkid_probe_end(pr);
        if (rc < 0)
                return rc;
        return count ? 0 : 1;
@@ -965,14 +1073,49 @@ int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
  */
 dev_t blkid_probe_get_devno(blkid_probe pr)
 {
-       if (!pr->devno) {
-               struct stat sb;
+       return pr->devno;
+}
+
+/**
+ * blkid_probe_get_wholedisk_devno:
+ * @pr: probe
+ *
+ * Returns: device number of the wholedisk, or 0 for regilar 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 (fstat(pr->fd, &sb) == 0 &&
-                   (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)))
-                       pr->devno = sb.st_rdev;
+                if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0)
+                       pr->disk_devno = disk_devno;
        }
-       return pr->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;
 }
 
 /**
@@ -1016,7 +1159,7 @@ int blkid_probe_get_fd(blkid_probe pr)
 
 /**
  * blkid_probe_get_sectorsize:
- * @pr: probe
+ * @pr: probe or NULL (for NULL returns 512)
  *
  * Returns: block device logical sector size (BLKSSZGET ioctl, default 512).
  */
@@ -1024,27 +1167,29 @@ 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;
-       }
+       if (S_ISBLK(pr->mode) &&
+           blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0)
+               return pr->blkssz;
 
-fallback:
        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
@@ -1191,3 +1336,79 @@ size_t blkid_rtrim_whitespace(unsigned char *str)
        return i;
 }
 
+/*
+ * 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) this 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).
+ */
+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 || 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;
+}
+
+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("wiped area detected -- ignore previous results\n"));
+               blkid_probe_set_wiper(pr, 0, 0);
+               blkid_probe_chain_reset_vals(pr, chn);
+       }
+}
+