/* * 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 "blkdev.h" #include "blkidP.h" #include "superblocks.h" /** * SECTION:superblocks * @title: Superblocks probing * @short_description: filesystems and raids superblocks probing. * * The library API has been originaly 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 controled 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] */ 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, &lvm2_idinfo, &lvm1_idinfo, &snapcow_idinfo, &luks_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 }; /* * 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; int i; if (!pr) return -1; 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) { int 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; } /* * The blkid_do_probe() backend. */ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn) { int i = 0; 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)); i = chn->idx + 1; for ( ; i < ARRAY_SIZE(idinfos); i++) { const struct blkid_idinfo *id; const struct blkid_idmag *mag; blkid_loff_t off = 0; int hasmag = 0; chn->idx = i; if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) continue; id = idinfos[i]; mag = id->magics ? &id->magics[0] : 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( "%s: magic sboff=%u, kboff=%ld\n", id->name, mag->sboff, mag->kboff)); hasmag = 1; off += mag->sboff & 0x3ff; break; } mag++; } if (hasmag == 0 && id->magics && id->magics[0].magic) /* magic string(s) defined, but not found */ continue; /* final check by probing function */ if (id->probefunc) { DBG(DEBUG_LOWPROBE, printf( "%s: call probefunc()\n", id->name)); if (id->probefunc(pr, mag) != 0) continue; } /* all cheks passed */ if (chn->flags & BLKID_SUBLKS_TYPE) blkid_probe_set_value(pr, "TYPE", (unsigned char *) id->name, strlen(id->name) + 1); if (chn->flags & BLKID_SUBLKS_USAGE) blkid_probe_set_usage(pr, id->usage); if (hasmag && (chn->flags & BLKID_SUBLKS_MAGIC)) { blkid_probe_set_value(pr, "SBMAGIC", (unsigned char *) mag->magic, mag->len); blkid_probe_sprintf_value(pr, "SBMAGIC_OFFSET", "%llu", off); } DBG(DEBUG_LOWPROBE, printf("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]\n", id->name, chn->idx)); return 0; } 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 signature is * detected. The function also does not check for collision between RAIDs. The * first detected RAID 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 (!count) { /* save the first result */ nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); idx = chn->idx; } count++; if (idinfos[chn->idx]->usage & BLKID_USAGE_RAID) break; if (!(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT)) intol++; } 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 */ /* restore the first result */ blkid_probe_chain_reset_vals(pr, chn); blkid_probe_append_vals(pr, vals, nvals); chn->idx = idx; 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) { char *u = NULL; 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_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; 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; 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) { int 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) { int 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); } /* * DEPRECATED FUNCTIONS */ /** * 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); }