/* * 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) * * @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 filessystem [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, &drbd_idinfo, &drbdproxy_datalog_idinfo, &lvm2_idinfo, &lvm1_idinfo, &snapcow_idinfo, &verity_hash_idinfo, &luks_idinfo, &vmfs_volume_idinfo, /* Filesystems */ &vfat_idinfo, &swsuspend_idinfo, &swap_idinfo, &xfs_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, &cramfs_idinfo, &romfs_idinfo, &minix_idinfo, &gfs_idinfo, &gfs2_idinfo, &ocfs_idinfo, &ocfs2_idinfo, &oracleasm_idinfo, &vxfs_idinfo, &squashfs_idinfo, &netware_idinfo, &btrfs_idinfo, &ubifs_idinfo, &bfs_idinfo, &vmfs_fs_idinfo, &befs_idinfo, &nilfs2_idinfo, &exfat_idinfo, &f2fs_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) { if (!pr) return -1; 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) { if (!pr) return -1; 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(DEBUG_LOWPROBE, printf("a new probing usage-filter initialized\n")); return 0; } /** * blkid_known_fstype: * @fstype: filesystem name * * Returns: 1 for known filesytems, or 0 for unknown filesystem. */ int blkid_known_fstype(const char *fstype) { size_t i; if (!fstype) return 0; 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; if (!pr || chn->idx < -1) return -1; blkid_probe_chain_reset_vals(pr, chn); DBG(DEBUG_LOWPROBE, printf("--> starting probing loop [SUBLKS idx=%d]\n", chn->idx)); 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 */ goto nothing; 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; blkid_loff_t off = 0; int rc = 0; chn->idx = i; id = idinfos[i]; if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) { DBG(DEBUG_LOWPROBE, printf("filter out: %s\n", id->name)); continue; } if (id->minsz && id->minsz > pr->size) 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)) continue; /* don't probe for RAIDs on floppies */ if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) continue; DBG(DEBUG_LOWPROBE, printf("[%zd] %s:\n", i, id->name)); if (blkid_probe_get_idmag(pr, id, &off, &mag)) continue; /* final check by probing function */ if (id->probefunc) { DBG(DEBUG_LOWPROBE, printf("\tcall probefunc()\n")); if (id->probefunc(pr, mag) != 0) { blkid_probe_chain_reset_vals(pr, chn); continue; } } /* all cheks 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_vals(pr, chn); DBG(DEBUG_LOWPROBE, printf("failed to set result -- ingnore\n")); continue; } DBG(DEBUG_LOWPROBE, printf("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]\n", id->name, chn->idx)); return 0; } nothing: DBG(DEBUG_LOWPROBE, printf("<-- leaving probing loop (failed) [SUBLKS idx=%d]\n", chn->idx)); return 1; } /* * This is the same function as blkid_do_probe(), but returns only one result * (cannot be used in while()) and checks for ambivalen 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 blkid_prval vals[BLKID_NVALS_SUBLKS]; int nvals = BLKID_NVALS_SUBLKS; int idx = -1; int count = 0; int intol = 0; int rc; while ((rc = superblocks_probe(pr, chn)) == 0) { if (blkid_probe_is_tiny(pr) && !count) /* floppy or so -- returns the first result. */ return 0; 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 */ nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); idx = chn->idx; } } if (rc < 0) return rc; /* error */ if (count > 1 && intol) { DBG(DEBUG_LOWPROBE, printf("ERROR: superblocks chain: " "ambivalent result detected (%d filesystems)!\n", count)); return -2; /* error, ambivalent result (more FS) */ } if (!count) return 1; /* nothing detected */ if (idx != -1) { /* restore the first result */ blkid_probe_chain_reset_vals(pr, chn); blkid_probe_append_vals(pr, vals, nvals); chn->idx = idx; } /* * The RAID device could be partitioned. The problem are RAID1 devices * where the partition table is visible from underlaying 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; return 0; } 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; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, name); if (!v) return -1; if (len >= BLKID_PROBVAL_BUFSIZ) len = BLKID_PROBVAL_BUFSIZ - 1; /* make a space for \0 */ memcpy(v->data, data, len); v->data[len] = '\0'; /* 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) blkid_probe_reset_last_value(pr); /* ignore empty */ return 0; } 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; if (len > BLKID_PROBVAL_BUFSIZ) len = BLKID_PROBVAL_BUFSIZ; if ((chn->flags & BLKID_SUBLKS_LABELRAW) && blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0) return -1; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, "LABEL"); if (!v) return -1; if (len == BLKID_PROBVAL_BUFSIZ) len--; /* make a space for \0 */ memcpy(v->data, label, len); v->data[len] = '\0'; v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len == 1) blkid_probe_reset_last_value(pr); return 0; } 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; if ((chn->flags & BLKID_SUBLKS_LABELRAW) && blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0) return -1; if (!(chn->flags & BLKID_SUBLKS_LABEL)) return 0; v = blkid_probe_assign_value(pr, "LABEL"); if (!v) return -1; blkid_encode_to_utf8(enc, v->data, sizeof(v->data), label, len); v->len = blkid_rtrim_whitespace(v->data) + 1; if (v->len == 1) blkid_probe_reset_last_value(pr); return 0; } /* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */ static int 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; } 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); int rc = -1; va_list ap; if (len > BLKID_PROBVAL_BUFSIZ) len = BLKID_PROBVAL_BUFSIZ; if (uuid_is_empty(uuid, len)) return 0; if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && blkid_probe_set_value(pr, "UUID_RAW", uuid, len) < 0) return -1; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; va_start(ap, fmt); rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap); va_end(ap); /* convert to lower case (..be paranoid) */ if (!rc) { size_t i; struct blkid_prval *v = __blkid_probe_get_value(pr, blkid_probe_numof_values(pr)); if (v) { for (i = 0; i < v->len; i++) if (v->data[i] >= 'A' && v->data[i] <= 'F') v->data[i] = (v->data[i] - 'A') + 'a'; } } return rc; } /* function to set UUIDs that are in suberblocks 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; if (str == NULL || *str == '\0') return -1; if (!len) len = strlen((char *) str); if (len > BLKID_PROBVAL_BUFSIZ) len = BLKID_PROBVAL_BUFSIZ; if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && blkid_probe_set_value(pr, "UUID_RAW", str, len) < 0) return -1; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; v = blkid_probe_assign_value(pr, "UUID"); if (v) { if (len == BLKID_PROBVAL_BUFSIZ) len--; /* make a space for \0 */ memcpy((char *) v->data, str, len); v->data[len] = '\0'; v->len = len + 1; return 0; } return -1; } /* 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; if (uuid_is_empty(uuid, 16)) return 0; if (!name) { if ((chn->flags & BLKID_SUBLKS_UUIDRAW) && blkid_probe_set_value(pr, "UUID_RAW", uuid, 16) < 0) return -1; if (!(chn->flags & BLKID_SUBLKS_UUID)) return 0; v = blkid_probe_assign_value(pr, "UUID"); } else v = blkid_probe_assign_value(pr, name); blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data)); v->len = 37; return 0; } 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); }