/* * superblocks.c - reads information from filesystem and raid superblocks * * Copyright (C) 2008-2009 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. */ #include #include #include #include #include #include #include #include #include #include #include #include "superblocks.h" /** * SECTION:superblocks * @title: Superblocks probing * @short_description: filesystems and raids superblocks probing. * * The library API has been originally designed for superblocks probing only. * This is reason why some *deprecated* superblock specific functions don't use * '_superblocks_' namespace in the function name. Please, don't use these * functions in new code. * * The 'superblocks' probers support NAME=value (tags) interface only. The * superblocks probing is enabled by default (and controlled by * blkid_probe_enable_superblocks()). * * Currently supported tags: * * @TYPE: filesystem type * * @SEC_TYPE: secondary filesystem type * * @LABEL: filesystem label * * @LABEL_RAW: raw label from FS superblock * * @UUID: filesystem UUID (lower case) * * @UUID_SUB: subvolume uuid (e.g. btrfs) * * @LOGUUID: external log UUID (e.g. xfs) * * @UUID_RAW: raw UUID from FS superblock * * @EXT_JOURNAL: external journal UUID * * @USAGE: usage string: "raid", "filesystem", ... * * @VERSION: filesystem version * * @MOUNT: cluster mount name (?) -- ocfs only * * @SBMAGIC: super block magic string * * @SBMAGIC_OFFSET: offset of SBMAGIC * * @FSSIZE: size of filesystem [not-implemented yet] * * @SYSTEM_ID: ISO9660 system identifier * * @PUBLISHER_ID: ISO9660 publisher identifier * * @APPLICATION_ID: ISO9660 application identifier * * @BOOT_SYSTEM_ID: ISO9660 boot system identifier */ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn); static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn); static int blkid_probe_set_usage(blkid_probe pr, int usage); /* * Superblocks chains probing functions */ static const struct blkid_idinfo *idinfos[] = { /* RAIDs */ &linuxraid_idinfo, &ddfraid_idinfo, &iswraid_idinfo, &lsiraid_idinfo, &viaraid_idinfo, &silraid_idinfo, &nvraid_idinfo, &pdcraid_idinfo, &highpoint45x_idinfo, &highpoint37x_idinfo, &adraid_idinfo, &jmraid_idinfo, &bcache_idinfo, &drbd_idinfo, &drbdmanage_idinfo, &drbdproxy_datalog_idinfo, &lvm2_idinfo, &lvm1_idinfo, &snapcow_idinfo, &verity_hash_idinfo, &integrity_idinfo, &luks_idinfo, &vmfs_volume_idinfo, &ubi_idinfo, &vdo_idinfo, &stratis_idinfo, /* Filesystems */ &vfat_idinfo, &swsuspend_idinfo, &swap_idinfo, &xfs_idinfo, &xfs_log_idinfo, &exfs_idinfo, &ext4dev_idinfo, &ext4_idinfo, &ext3_idinfo, &ext2_idinfo, &jbd_idinfo, &reiser_idinfo, &reiser4_idinfo, &jfs_idinfo, &udf_idinfo, &iso9660_idinfo, &zfs_idinfo, &hfsplus_idinfo, &hfs_idinfo, &ufs_idinfo, &hpfs_idinfo, &sysv_idinfo, &xenix_idinfo, &ntfs_idinfo, &refs_idinfo, &cramfs_idinfo, &romfs_idinfo, &minix_idinfo, &gfs_idinfo, &gfs2_idinfo, &ocfs_idinfo, &ocfs2_idinfo, &oracleasm_idinfo, &vxfs_idinfo, &squashfs_idinfo, &squashfs3_idinfo, &netware_idinfo, &btrfs_idinfo, &ubifs_idinfo, &bfs_idinfo, &vmfs_fs_idinfo, &befs_idinfo, &nilfs2_idinfo, &exfat_idinfo, &f2fs_idinfo, &mpool_idinfo }; /* * Driver definition */ const struct blkid_chaindrv superblocks_drv = { .id = BLKID_CHAIN_SUBLKS, .name = "superblocks", .dflt_enabled = TRUE, .dflt_flags = BLKID_SUBLKS_DEFAULT, .idinfos = idinfos, .nidinfos = ARRAY_SIZE(idinfos), .has_fltr = TRUE, .probe = superblocks_probe, .safeprobe = superblocks_safeprobe, }; /** * blkid_probe_enable_superblocks: * @pr: probe * @enable: TRUE/FALSE * * Enables/disables the superblocks probing for non-binary interface. * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_enable_superblocks(blkid_probe pr, int enable) { pr->chains[BLKID_CHAIN_SUBLKS].enabled = enable; return 0; } /** * blkid_probe_set_superblocks_flags: * @pr: prober * @flags: BLKID_SUBLKS_* flags * * Sets probing flags to the superblocks prober. This function is optional, the * default are BLKID_SUBLKS_DEFAULTS flags. * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags) { pr->chains[BLKID_CHAIN_SUBLKS].flags = flags; return 0; } /** * blkid_probe_reset_superblocks_filter: * @pr: prober * * Resets superblocks probing filter * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_reset_superblocks_filter(blkid_probe pr) { return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS); } /** * blkid_probe_invert_superblocks_filter: * @pr: prober * * Inverts superblocks probing filter * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_invert_superblocks_filter(blkid_probe pr) { return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS); } /** * blkid_probe_filter_superblocks_type: * @pr: prober * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag * @names: NULL terminated array of probing function names (e.g. "vfat"). * * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @names; * * %BLKID_FLTR_ONLYIN - probe for items which are IN @names * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[]) { return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names); } /** * blkid_probe_filter_superblocks_usage: * @pr: prober * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag * @usage: BLKID_USAGE_* flags * * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @usage; * * %BLKID_FLTR_ONLYIN - probe for items which are IN @usage * * Returns: 0 on success, or -1 in case of error. */ int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage) { unsigned long *fltr; struct blkid_chain *chn; size_t i; fltr = blkid_probe_get_filter(pr, BLKID_CHAIN_SUBLKS, TRUE); if (!fltr) return -1; chn = &pr->chains[BLKID_CHAIN_SUBLKS]; for (i = 0; i < chn->driver->nidinfos; i++) { const struct blkid_idinfo *id = chn->driver->idinfos[i]; if (id->usage & usage) { if (flag & BLKID_FLTR_NOTIN) blkid_bmp_set_item(chn->fltr, i); } else if (flag & BLKID_FLTR_ONLYIN) blkid_bmp_set_item(chn->fltr, i); } DBG(LOWPROBE, ul_debug("a new probing usage-filter initialized")); return 0; } /** * blkid_known_fstype: * @fstype: filesystem name * * Returns: 1 for known filesystems, or 0 for unknown filesystem. */ int blkid_known_fstype(const char *fstype) { size_t i; for (i = 0; i < ARRAY_SIZE(idinfos); i++) { const struct blkid_idinfo *id = idinfos[i]; if (strcmp(id->name, fstype) == 0) return 1; } return 0; } /** * blkid_superblocks_get_name: * @idx: number >= 0 * @name: returns name of supported filesystem/raid (optional) * @usage: returns BLKID_USAGE_* flags, (optional) * * Returns: -1 if @idx is out of range, or 0 on success. */ int blkid_superblocks_get_name(size_t idx, const char **name, int *usage) { if (idx < ARRAY_SIZE(idinfos)) { if (name) *name = idinfos[idx]->name; if (usage) *usage = idinfos[idx]->usage; return 0; } return -1; } /* * The blkid_do_probe() backend. */ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn) { size_t i; int rc = BLKID_PROBE_NONE; if (chn->idx < -1) return -EINVAL; blkid_probe_chain_reset_values(pr, chn); if (pr->flags & BLKID_FL_NOSCAN_DEV) { DBG(LOWPROBE, ul_debug("*** ignore (noscan flag)")); return BLKID_PROBE_NONE; } if (pr->size <= 0 || (pr->size <= 1024 && !S_ISCHR(pr->mode))) { /* Ignore very very small block devices or regular files (e.g. * extended partitions). Note that size of the UBI char devices * is 1 byte */ DBG(LOWPROBE, ul_debug("*** ignore (size <= 1024)")); return BLKID_PROBE_NONE; } DBG(LOWPROBE, ul_debug("--> starting probing loop [SUBLKS idx=%d]", chn->idx)); i = chn->idx < 0 ? 0 : chn->idx + 1U; for ( ; i < ARRAY_SIZE(idinfos); i++) { const struct blkid_idinfo *id; const struct blkid_idmag *mag = NULL; uint64_t off = 0; chn->idx = i; id = idinfos[i]; if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) { DBG(LOWPROBE, ul_debug("filter out: %s", id->name)); rc = BLKID_PROBE_NONE; continue; } if (id->minsz && (unsigned)id->minsz > pr->size) { rc = BLKID_PROBE_NONE; continue; /* the device is too small */ } /* don't probe for RAIDs, swap or journal on CD/DVDs */ if ((id->usage & (BLKID_USAGE_RAID | BLKID_USAGE_OTHER)) && blkid_probe_is_cdrom(pr)) { rc = BLKID_PROBE_NONE; continue; } /* don't probe for RAIDs on floppies */ if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) { rc = BLKID_PROBE_NONE; continue; } DBG(LOWPROBE, ul_debug("[%zd] %s:", i, id->name)); rc = blkid_probe_get_idmag(pr, id, &off, &mag); if (rc < 0) break; if (rc != BLKID_PROBE_OK) continue; /* final check by probing function */ if (id->probefunc) { DBG(LOWPROBE, ul_debug("\tcall probefunc()")); rc = id->probefunc(pr, mag); if (rc != BLKID_PROBE_OK) { blkid_probe_chain_reset_values(pr, chn); if (rc < 0) break; continue; } } /* all checks passed */ if (chn->flags & BLKID_SUBLKS_TYPE) rc = blkid_probe_set_value(pr, "TYPE", (unsigned char *) id->name, strlen(id->name) + 1); if (!rc) rc = blkid_probe_set_usage(pr, id->usage); if (!rc && mag) rc = blkid_probe_set_magic(pr, off, mag->len, (unsigned char *) mag->magic); if (rc) { blkid_probe_chain_reset_values(pr, chn); DBG(LOWPROBE, ul_debug("failed to set result -- ignore")); continue; } DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]", id->name, chn->idx)); return BLKID_PROBE_OK; } DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [SUBLKS idx=%d]", rc, chn->idx)); return rc; } /* * This is the same function as blkid_do_probe(), but returns only one result * (cannot be used in while()) and checks for ambivalent results (more * filesystems on the device) -- in such case returns -2. * * The function does not check for filesystems when a RAID or crypto signature * is detected. The function also does not check for collision between RAIDs * and crypto devices. The first detected RAID or crypto device is returned. * * The function does not probe for ambivalent results on very small devices * (e.g. floppies), on small devices the first detected filesystem is returned. */ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) { struct list_head vals; int idx = -1; int count = 0; int intol = 0; int rc; INIT_LIST_HEAD(&vals); if (pr->flags & BLKID_FL_NOSCAN_DEV) return BLKID_PROBE_NONE; while ((rc = superblocks_probe(pr, chn)) == 0) { if (blkid_probe_is_tiny(pr) && !count) return BLKID_PROBE_OK; /* floppy or so -- returns the first result. */ count++; if (chn->idx >= 0 && idinfos[chn->idx]->usage & (BLKID_USAGE_RAID | BLKID_USAGE_CRYPTO)) break; if (chn->idx >= 0 && !(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT)) intol++; if (count == 1) { /* save the first result */ blkid_probe_chain_save_values(pr, chn, &vals); idx = chn->idx; } } if (rc < 0) goto done; /* error */ if (count > 1 && intol) { DBG(LOWPROBE, ul_debug("ERROR: superblocks chain: " "ambivalent result detected (%d filesystems)!", count)); rc = -2; /* error, ambivalent result (more FS) */ goto done; } if (!count) { rc = BLKID_PROBE_NONE; goto done; } if (idx != -1) { /* restore the first result */ blkid_probe_chain_reset_values(pr, chn); blkid_probe_append_values_list(pr, &vals); chn->idx = idx; } /* * The RAID device could be partitioned. The problem are RAID1 devices * where the partition table is visible from underlying devices. We * have to ignore such partition tables. */ if (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID) pr->prob_flags |= BLKID_PROBE_FL_IGNORE_PT; rc = BLKID_PROBE_OK; done: blkid_probe_free_values_list(&vals); return rc; } int blkid_probe_set_version(blkid_probe pr, const char *version) { struct blkid_chain *chn = blkid_probe_get_chain(pr); if (chn->flags & BLKID_SUBLKS_VERSION) return blkid_probe_set_value(pr, "VERSION", (unsigned char *) version, strlen(version) + 1); return 0; } int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...) { struct blkid_chain *chn = blkid_probe_get_chain(pr); int rc = 0; if (chn->flags & BLKID_SUBLKS_VERSION) { va_list ap; va_start(ap, fmt); rc = blkid_probe_vsprintf_value(pr, "VERSION", fmt, ap); va_end(ap); } return rc; } static int blkid_probe_set_usage(blkid_probe pr, int usage) { struct blkid_chain *chn = blkid_probe_get_chain(pr); char *u = NULL; if (!(chn->flags & BLKID_SUBLKS_USAGE)) return 0; if (usage & BLKID_USAGE_FILESYSTEM) u = "filesystem"; else if (usage & BLKID_USAGE_RAID) u = "raid"; else if (usage & BLKID_USAGE_CRYPTO) u = "crypto"; else if (usage & BLKID_USAGE_OTHER) u = "other"; else u = "unknown"; return blkid_probe_set_value(pr, "USAGE", (unsigned char *) u, strlen(u) + 1); } int blkid_probe_set_id_label(blkid_probe pr, const char *name, unsigned char *data, size_t len) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, name); if (!v) return -ENOMEM; rc = blkid_probe_value_set_data(v, data, len); if (!rc) { /* remove white spaces */ v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len > 1) v->len = blkid_ltrim_whitespace(v->data) + 1; if (v->len > 1) return 0; } blkid_probe_free_value(v); return rc; } int blkid_probe_set_utf8_id_label(blkid_probe pr, const char *name, unsigned char *data, size_t len, int enc) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, name); if (!v) return -ENOMEM; v->data = blkid_encode_alloc(len, &v->len); if (!v->data) rc = -ENOMEM; if (!rc) { blkid_encode_to_utf8(enc, v->data, v->len, data, len); v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len > 1) v->len = blkid_ltrim_whitespace(v->data) + 1; if (v->len > 1) return 0; } blkid_probe_free_value(v); return rc; } int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if ((chn->flags & BLKID_SUBLKS_LABELRAW) && (rc = blkid_probe_set_value(pr, "LABEL_RAW", label, len)) < 0) return rc; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, "LABEL"); if (!v) return -ENOMEM; rc = blkid_probe_value_set_data(v, label, len); if (!rc) { v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len > 1) return 0; } blkid_probe_free_value(v); return rc; } int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label, size_t len, int enc) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if ((chn->flags & BLKID_SUBLKS_LABELRAW) && (rc = blkid_probe_set_value(pr, "LABEL_RAW", label, len)) < 0) return rc; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, "LABEL"); if (!v) return -ENOMEM; v->data = blkid_encode_alloc(len, &v->len); if (!v->data) rc = -ENOMEM; if (!rc) { blkid_encode_to_utf8(enc, v->data, v->len, label, len); v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len > 1) return 0; } blkid_probe_free_value(v); return rc; } int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid, size_t len, const char *fmt, ...) { struct blkid_chain *chn = blkid_probe_get_chain(pr); va_list ap; int rc = 0; if (blkid_uuid_is_empty(uuid, len)) return 0; if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && (rc = blkid_probe_set_value(pr, "UUID_RAW", uuid, len)) < 0) return rc; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; va_start(ap, fmt); rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap); va_end(ap); return rc; } /* function to set UUIDs that are in superblocks stored as strings */ int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if (str == NULL || *str == '\0') return -EINVAL; if (!len) len = strlen((char *) str); if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && (rc = blkid_probe_set_value(pr, "UUID_RAW", str, len)) < 0) return rc; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; v = blkid_probe_assign_value(pr, "UUID"); if (!v) rc= -ENOMEM; if (!rc) rc = blkid_probe_value_set_data(v, str, len); if (!rc) { v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len > 1) return 0; } blkid_probe_free_value(v); return rc; } /* default _set_uuid function to set DCE UUIDs */ int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name) { struct blkid_chain *chn = blkid_probe_get_chain(pr); struct blkid_prval *v; int rc = 0; if (blkid_uuid_is_empty(uuid, 16)) return 0; if (!name) { if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && (rc = blkid_probe_set_value(pr, "UUID_RAW", uuid, 16)) < 0) return rc; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; v = blkid_probe_assign_value(pr, "UUID"); } else v = blkid_probe_assign_value(pr, name); if (!v) return -ENOMEM; v->len = UUID_STR_LEN; v->data = calloc(1, v->len); if (!v->data) rc = -ENOMEM; if (!rc) { blkid_unparse_uuid(uuid, (char *) v->data, v->len); return 0; } blkid_probe_free_value(v); return rc; } int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid) { return blkid_probe_set_uuid_as(pr, uuid, NULL); } /** * blkid_probe_set_request: * @pr: probe * @flags: BLKID_PROBREQ_* (deprecated) or BLKID_SUBLKS_* flags * * Returns: 0 on success, or -1 in case of error. * * Deprecated: Use blkid_probe_set_superblocks_flags(). */ int blkid_probe_set_request(blkid_probe pr, int flags) { return blkid_probe_set_superblocks_flags(pr, flags); } /** * blkid_probe_reset_filter: * @pr: prober * * Returns: 0 on success, or -1 in case of error. * * Deprecated: Use blkid_probe_reset_superblocks_filter(). */ int blkid_probe_reset_filter(blkid_probe pr) { return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS); } /** * blkid_probe_invert_filter: * @pr: prober * * Returns: 0 on success, or -1 in case of error. * * Deprecated: Use blkid_probe_invert_superblocks_filter(). */ int blkid_probe_invert_filter(blkid_probe pr) { return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS); } /** * blkid_probe_filter_types * @pr: prober * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag * @names: NULL terminated array of probing function names (e.g. "vfat"). * * Returns: 0 on success, or -1 in case of error. * * Deprecated: Use blkid_probe_filter_superblocks_types(). */ int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[]) { return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names); } /** * blkid_probe_filter_usage * @pr: prober * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag * @usage: BLKID_USAGE_* flags * * Returns: 0 on success, or -1 in case of error. * * Deprecated: Use blkid_probe_filter_superblocks_usage(). */ int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage) { return blkid_probe_filter_superblocks_usage(pr, flag, usage); }