diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
commit | cfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch) | |
tree | d0baf160cbee3195249d095f85e52d20c21acf02 /libblkid/src | |
parent | Initial commit. (diff) | |
download | util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip |
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libblkid/src')
105 files changed, 24643 insertions, 0 deletions
diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am new file mode 100644 index 0000000..bc90eb8 --- /dev/null +++ b/libblkid/src/Makemodule.am @@ -0,0 +1,253 @@ + +# blkid.h is generated, so it's store in builddir! +blkidincdir = $(includedir)/blkid +nodist_blkidinc_HEADERS = libblkid/src/blkid.h + +usrlib_exec_LTLIBRARIES += libblkid.la +libblkid_la_SOURCES = \ + include/list.h \ + \ + libblkid/src/blkidP.h \ + libblkid/src/init.c \ + libblkid/src/cache.c \ + libblkid/src/config.c \ + libblkid/src/dev.c \ + libblkid/src/devname.c \ + libblkid/src/devno.c \ + libblkid/src/encode.c \ + libblkid/src/evaluate.c \ + libblkid/src/getsize.c \ + libblkid/src/probe.c \ + libblkid/src/read.c \ + libblkid/src/resolve.c \ + libblkid/src/save.c \ + libblkid/src/superblocks/superblocks.h \ + libblkid/src/tag.c \ + libblkid/src/verify.c \ + libblkid/src/version.c \ + \ + libblkid/src/partitions/aix.c \ + libblkid/src/partitions/aix.h \ + libblkid/src/partitions/atari.c \ + libblkid/src/partitions/bsd.c \ + libblkid/src/partitions/dos.c \ + libblkid/src/partitions/gpt.c \ + libblkid/src/partitions/mac.c \ + libblkid/src/partitions/minix.c \ + libblkid/src/partitions/partitions.c \ + libblkid/src/partitions/partitions.h \ + libblkid/src/partitions/sgi.c \ + libblkid/src/partitions/solaris_x86.c \ + libblkid/src/partitions/sun.c \ + libblkid/src/partitions/ultrix.c \ + libblkid/src/partitions/unixware.c \ + \ + libblkid/src/superblocks/adaptec_raid.c \ + libblkid/src/superblocks/apfs.c \ + libblkid/src/superblocks/bcache.c \ + libblkid/src/superblocks/befs.c \ + libblkid/src/superblocks/bfs.c \ + libblkid/src/superblocks/bitlocker.c \ + libblkid/src/superblocks/bluestore.c \ + libblkid/src/superblocks/btrfs.c \ + libblkid/src/superblocks/cs_fvault2.c \ + libblkid/src/superblocks/cramfs.c \ + libblkid/src/superblocks/ddf_raid.c \ + libblkid/src/superblocks/drbd.c \ + libblkid/src/superblocks/drbdproxy_datalog.c \ + libblkid/src/superblocks/drbdmanage.c \ + libblkid/src/superblocks/exfat.c \ + libblkid/src/superblocks/exfs.c \ + libblkid/src/superblocks/ext.c \ + libblkid/src/superblocks/f2fs.c \ + libblkid/src/superblocks/gfs.c \ + libblkid/src/superblocks/hfs.c \ + libblkid/src/superblocks/highpoint_raid.c \ + libblkid/src/superblocks/hpfs.c \ + libblkid/src/superblocks/iso9660.c \ + libblkid/src/superblocks/isw_raid.c \ + libblkid/src/superblocks/jfs.c \ + libblkid/src/superblocks/jmicron_raid.c \ + libblkid/src/superblocks/linux_raid.c \ + libblkid/src/superblocks/lsi_raid.c \ + libblkid/src/superblocks/luks.c \ + libblkid/src/superblocks/lvm.c \ + libblkid/src/superblocks/minix.c \ + libblkid/src/superblocks/mpool.c \ + libblkid/src/superblocks/netware.c \ + libblkid/src/superblocks/nilfs.c \ + libblkid/src/superblocks/ntfs.c \ + libblkid/src/superblocks/refs.c \ + libblkid/src/superblocks/nvidia_raid.c \ + libblkid/src/superblocks/ocfs.c \ + libblkid/src/superblocks/promise_raid.c \ + libblkid/src/superblocks/reiserfs.c \ + libblkid/src/superblocks/romfs.c \ + libblkid/src/superblocks/silicon_raid.c \ + libblkid/src/superblocks/squashfs.c \ + libblkid/src/superblocks/stratis.c \ + libblkid/src/superblocks/superblocks.c \ + libblkid/src/superblocks/superblocks.h \ + libblkid/src/superblocks/swap.c \ + libblkid/src/superblocks/sysv.c \ + libblkid/src/superblocks/ubi.c \ + libblkid/src/superblocks/ubifs.c \ + libblkid/src/superblocks/udf.c \ + libblkid/src/superblocks/ufs.c \ + libblkid/src/superblocks/vdo.c \ + libblkid/src/superblocks/vfat.c \ + libblkid/src/superblocks/via_raid.c \ + libblkid/src/superblocks/vmfs.c \ + libblkid/src/superblocks/vxfs.c \ + libblkid/src/superblocks/xfs.c \ + libblkid/src/superblocks/zfs.c \ + libblkid/src/superblocks/zonefs.c \ + libblkid/src/superblocks/erofs.c \ + \ + libblkid/src/topology/topology.c \ + libblkid/src/topology/topology.h + +if LINUX +libblkid_la_SOURCES += \ + libblkid/src/topology/dm.c \ + libblkid/src/topology/evms.c \ + libblkid/src/topology/ioctl.c \ + libblkid/src/topology/lvm.c \ + libblkid/src/topology/md.c \ + libblkid/src/topology/sysfs.c +endif + +libblkid_la_LIBADD = libcommon.la + +EXTRA_libblkid_la_DEPENDENCIES = \ + libblkid/src/libblkid.sym + +libblkid_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(SOLIB_CFLAGS) \ + -I$(ul_libblkid_incdir) \ + -I$(top_srcdir)/libblkid/src + +libblkid_la_LDFLAGS = $(SOLIB_LDFLAGS) +if HAVE_VSCRIPT +libblkid_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/libblkid/src/libblkid.sym +endif +libblkid_la_LDFLAGS += -version-info $(LIBBLKID_VERSION_INFO) + +EXTRA_DIST += \ + libblkid/src/libblkid.sym + +if BUILD_LIBBLKID_TESTS +check_PROGRAMS += \ + test_blkid_cache \ + test_blkid_config \ + test_blkid_dev \ + test_blkid_devname \ + test_blkid_devno \ + test_blkid_evaluate \ + test_blkid_read \ + test_blkid_resolve \ + test_blkid_save \ + test_blkid_tag \ + test_blkid_verify + +blkid_tests_cflags = -DTEST_PROGRAM $(libblkid_la_CFLAGS) +blkid_tests_ldflags = +blkid_tests_ldadd = $(LDADD) libblkid.la +blkid_tests_ldflags += -static + +test_blkid_cache_SOURCES = libblkid/src/cache.c +test_blkid_cache_CFLAGS = $(blkid_tests_cflags) +test_blkid_cache_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_cache_LDADD = $(blkid_tests_ldadd) + +test_blkid_config_SOURCES = libblkid/src/config.c +test_blkid_config_CFLAGS = $(blkid_tests_cflags) +test_blkid_config_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_config_LDADD = $(blkid_tests_ldadd) + +test_blkid_dev_SOURCES = libblkid/src/dev.c +test_blkid_dev_CFLAGS = $(blkid_tests_cflags) +test_blkid_dev_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_dev_LDADD = $(blkid_tests_ldadd) + +test_blkid_devname_SOURCES = libblkid/src/devname.c +test_blkid_devname_CFLAGS = $(blkid_tests_cflags) +test_blkid_devname_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_devname_LDADD = $(blkid_tests_ldadd) + +test_blkid_devno_SOURCES = libblkid/src/devno.c +test_blkid_devno_CFLAGS = $(blkid_tests_cflags) +test_blkid_devno_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_devno_LDADD = $(blkid_tests_ldadd) + +test_blkid_evaluate_SOURCES = libblkid/src/evaluate.c +test_blkid_evaluate_CFLAGS = $(blkid_tests_cflags) +test_blkid_evaluate_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_evaluate_LDADD = $(blkid_tests_ldadd) + +test_blkid_read_SOURCES = libblkid/src/read.c +test_blkid_read_CFLAGS = $(blkid_tests_cflags) +test_blkid_read_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_read_LDADD = $(blkid_tests_ldadd) + +test_blkid_resolve_SOURCES = libblkid/src/resolve.c +test_blkid_resolve_CFLAGS = $(blkid_tests_cflags) +test_blkid_resolve_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_resolve_LDADD = $(blkid_tests_ldadd) + +test_blkid_save_SOURCES = libblkid/src/save.c +test_blkid_save_CFLAGS = $(blkid_tests_cflags) +test_blkid_save_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_save_LDADD = $(blkid_tests_ldadd) + +test_blkid_tag_SOURCES = libblkid/src/tag.c +test_blkid_tag_CFLAGS = $(blkid_tests_cflags) +test_blkid_tag_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_tag_LDADD = $(blkid_tests_ldadd) + +test_blkid_verify_SOURCES = libblkid/src/verify.c +test_blkid_verify_CFLAGS = $(blkid_tests_cflags) +test_blkid_verify_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_verify_LDADD = $(blkid_tests_ldadd) + +if FUZZING_ENGINE +check_PROGRAMS += test_blkid_fuzz + +test_blkid_fuzz_SOURCES = libblkid/src/fuzz.c + +# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements +nodist_EXTRA_test_blkid_fuzz_SOURCES = dummy.cxx + +test_blkid_fuzz_CFLAGS = $(blkid_tests_cflags) -DFUZZ_TARGET +test_blkid_fuzz_LDFLAGS = $(blkid_tests_ldflags) -lpthread +test_blkid_fuzz_LDADD = $(blkid_tests_ldadd) $(LIB_FUZZING_ENGINE) +endif + +check_PROGRAMS += test_blkid_fuzz_sample + +test_blkid_fuzz_sample_SOURCES = libblkid/src/fuzz.c + +test_blkid_fuzz_sample_CFLAGS = $(blkid_tests_cflags) +test_blkid_fuzz_sample_LDFLAGS = $(blkid_tests_ldflags) +test_blkid_fuzz_sample_LDADD = $(blkid_tests_ldadd) + +endif # BUILD_LIBBLKID_TESTS + +# move lib from $(usrlib_execdir) to $(libdir) if needed +install-exec-hook-libblkid: + if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libblkid.so"; then \ + $(MKDIR_P) $(DESTDIR)$(libdir); \ + mv $(DESTDIR)$(usrlib_execdir)/libblkid.so.* $(DESTDIR)$(libdir); \ + so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libblkid.so); \ + so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ + (cd $(DESTDIR)$(usrlib_execdir) && \ + rm -f libblkid.so && \ + $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libblkid.so); \ + fi + +uninstall-hook-libblkid: + rm -f $(DESTDIR)$(libdir)/libblkid.so* + +INSTALL_EXEC_HOOKS += install-exec-hook-libblkid +UNINSTALL_HOOKS += uninstall-hook-libblkid diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in new file mode 100644 index 0000000..c232d72 --- /dev/null +++ b/libblkid/src/blkid.h.in @@ -0,0 +1,499 @@ +/* + * blkid.h - Interface for libblkid, a library to identify block devices + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _BLKID_BLKID_H +#define _BLKID_BLKID_H + +#include <stdint.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLKID_VERSION "@LIBBLKID_VERSION@" +#define BLKID_DATE "@LIBBLKID_DATE@" + +/** + * blkid_dev: + * + * The device object keeps information about one device + */ +typedef struct blkid_struct_dev *blkid_dev; + +/** + * blkid_cache: + * + * information about all system devices + */ +typedef struct blkid_struct_cache *blkid_cache; + +/** + * blkid_probe: + * + * low-level probing setting + */ +typedef struct blkid_struct_probe *blkid_probe; + +/** + * blkid_topology: + * + * device topology information + */ +typedef struct blkid_struct_topology *blkid_topology; + +/** + * blkid_partlist + * + * list of all detected partitions and partitions tables + */ +typedef struct blkid_struct_partlist *blkid_partlist; + +/** + * blkid_partition: + * + * information about a partition + */ +typedef struct blkid_struct_partition *blkid_partition; + +/** + * blkid_parttable: + * + * information about a partition table + */ +typedef struct blkid_struct_parttable *blkid_parttable; + +/** + * blkid_loff_t: + * + * 64-bit signed number for offsets and sizes + */ +typedef int64_t blkid_loff_t; + +/** + * blkid_tag_iterate: + * + * tags iterator for high-level (blkid_cache) API + */ +typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; + +/** + * blkid_dev_iterate: + * + * devices iterator for high-level (blkid_cache) API + */ +typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; + +/* + * Flags for blkid_get_dev + * + * BLKID_DEV_CREATE Create an empty device structure if not found + * in the cache. + * BLKID_DEV_VERIFY Make sure the device structure corresponds + * with reality. + * BLKID_DEV_FIND Just look up a device entry, and return NULL + * if it is not found. + * BLKID_DEV_NORMAL Get a valid device structure, either from the + * cache or by probing the device. + */ +#define BLKID_DEV_FIND 0x0000 +#define BLKID_DEV_CREATE 0x0001 +#define BLKID_DEV_VERIFY 0x0002 +#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) + + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +#ifndef __ul_attribute__ +# if __GNUC_PREREQ (3, 4) +# define __ul_attribute__(_a_) __attribute__(_a_) +# else +# define __ul_attribute__(_a_) +# endif +#endif + +/* init.c */ +extern void blkid_init_debug(int mask); + +/* cache.c */ +extern void blkid_put_cache(blkid_cache cache); +extern int blkid_get_cache(blkid_cache *cache, const char *filename); +extern void blkid_gc_cache(blkid_cache cache); + +/* dev.c */ +extern const char *blkid_dev_devname(blkid_dev dev) + __ul_attribute__((warn_unused_result)); + +extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); +extern int blkid_dev_set_search(blkid_dev_iterate iter, + const char *search_type, const char *search_value); +extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); +extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); + +/* devno.c */ +extern char *blkid_devno_to_devname(dev_t devno) + __ul_attribute__((warn_unused_result)); +extern int blkid_devno_to_wholedisk(dev_t dev, char *diskname, + size_t len, dev_t *diskdevno) + __ul_attribute__((warn_unused_result)); + +/* devname.c */ +extern int blkid_probe_all(blkid_cache cache); +extern int blkid_probe_all_new(blkid_cache cache); +extern int blkid_probe_all_removable(blkid_cache cache); + +extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags); + +/* getsize.c */ +extern blkid_loff_t blkid_get_dev_size(int fd); + +/* verify.c */ +extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); + +/* read.c */ + +/* resolve.c */ +extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, + const char *devname) + __ul_attribute__((warn_unused_result)); +extern char *blkid_get_devname(blkid_cache cache, const char *token, + const char *value) + __ul_attribute__((warn_unused_result)); + +/* tag.c */ +extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); +extern int blkid_tag_next(blkid_tag_iterate iterate, + const char **type, const char **value); +extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); +extern int blkid_dev_has_tag(blkid_dev dev, const char *type, const char *value); + +extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value); + +extern int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val); + +/* version.c */ +extern int blkid_parse_version_string(const char *ver_string) + __ul_attribute__((nonnull)); +extern int blkid_get_library_version(const char **ver_string, + const char **date_string); + +/* encode.c */ +extern int blkid_encode_string(const char *str, char *str_enc, size_t len); +extern int blkid_safe_string(const char *str, char *str_safe, size_t len); + +/* evaluate.c */ +extern int blkid_send_uevent(const char *devname, const char *action); +extern char *blkid_evaluate_tag(const char *token, const char *value, + blkid_cache *cache) + __ul_attribute__((warn_unused_result)); +extern char *blkid_evaluate_spec(const char *spec, blkid_cache *cache) + __ul_attribute__((warn_unused_result)); + +/* probe.c */ +extern blkid_probe blkid_new_probe(void) + __ul_attribute__((warn_unused_result)); +extern blkid_probe blkid_new_probe_from_filename(const char *filename) + __ul_attribute__((warn_unused_result)) + __ul_attribute__((nonnull)); +extern void blkid_free_probe(blkid_probe pr); + +extern void blkid_reset_probe(blkid_probe pr); +extern int blkid_probe_reset_buffers(blkid_probe pr); +extern int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len); + +extern int blkid_probe_set_device(blkid_probe pr, int fd, + blkid_loff_t off, blkid_loff_t size) + __ul_attribute__((nonnull)); + +extern dev_t blkid_probe_get_devno(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern int blkid_probe_is_wholedisk(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern blkid_loff_t blkid_probe_get_size(blkid_probe pr) + __ul_attribute__((nonnull)); +extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr) + __ul_attribute__((nonnull)); +extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz) + __ul_attribute__((nonnull)); +extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern int blkid_probe_get_fd(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value) + __ul_attribute__((nonnull)); +extern void blkid_probe_reset_hints(blkid_probe pr) + __ul_attribute__((nonnull)); + +/* + * superblocks probing + */ +extern int blkid_known_fstype(const char *fstype) + __ul_attribute__((nonnull)); + +extern int blkid_superblocks_get_name(size_t idx, const char **name, int *usage); + +extern int blkid_probe_enable_superblocks(blkid_probe pr, int enable) + __ul_attribute__((nonnull)); + +#define BLKID_SUBLKS_LABEL (1 << 1) /* read LABEL from superblock */ +#define BLKID_SUBLKS_LABELRAW (1 << 2) /* read and define LABEL_RAW result value*/ +#define BLKID_SUBLKS_UUID (1 << 3) /* read UUID from superblock */ +#define BLKID_SUBLKS_UUIDRAW (1 << 4) /* read and define UUID_RAW result value */ +#define BLKID_SUBLKS_TYPE (1 << 5) /* define TYPE result value */ +#define BLKID_SUBLKS_SECTYPE (1 << 6) /* define compatible fs type (second type) */ +#define BLKID_SUBLKS_USAGE (1 << 7) /* define USAGE result value */ +#define BLKID_SUBLKS_VERSION (1 << 8) /* read FS type from superblock */ +#define BLKID_SUBLKS_MAGIC (1 << 9) /* define SBMAGIC and SBMAGIC_OFFSET */ +#define BLKID_SUBLKS_BADCSUM (1 << 10) /* allow a bad checksum */ +#define BLKID_SUBLKS_FSINFO (1 << 11) /* read and define fs properties from superblock */ + +#define BLKID_SUBLKS_DEFAULT (BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | \ + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE) + +extern int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags) + __ul_attribute__((nonnull)); +extern int blkid_probe_reset_superblocks_filter(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_probe_invert_superblocks_filter(blkid_probe pr) + __ul_attribute__((nonnull)); + +/** + * BLKID_FLTR_NOTIN + */ +#define BLKID_FLTR_NOTIN 1 +/** + * BLKID_FLTR_ONLYIN + */ +#define BLKID_FLTR_ONLYIN 2 +extern int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[]) + __ul_attribute__((nonnull)); + +#define BLKID_USAGE_FILESYSTEM (1 << 1) +#define BLKID_USAGE_RAID (1 << 2) +#define BLKID_USAGE_CRYPTO (1 << 3) +#define BLKID_USAGE_OTHER (1 << 4) +extern int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage) + __ul_attribute__((nonnull)); + +/* + * topology probing + */ +extern int blkid_probe_enable_topology(blkid_probe pr, int enable) + __ul_attribute__((nonnull)); + +/* binary interface */ +extern blkid_topology blkid_probe_get_topology(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp) + __ul_attribute__((nonnull)); +extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp) + __ul_attribute__((nonnull)); +extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp) + __ul_attribute__((nonnull)); +extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp) + __ul_attribute__((nonnull)); +extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp) + __ul_attribute__((nonnull)); +extern unsigned long blkid_topology_get_dax(blkid_topology tp) + __ul_attribute__((nonnull)); +extern uint64_t blkid_topology_get_diskseq(blkid_topology tp) + __ul_attribute__((nonnull)); + +/* + * partitions probing + */ +extern int blkid_known_pttype(const char *pttype); +extern int blkid_partitions_get_name(const size_t idx, const char **name); + +extern int blkid_probe_enable_partitions(blkid_probe pr, int enable) + __ul_attribute__((nonnull)); + +extern int blkid_probe_reset_partitions_filter(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_probe_invert_partitions_filter(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]) + __ul_attribute__((nonnull)); + +/* partitions probing flags */ +#define BLKID_PARTS_FORCE_GPT (1 << 1) +#define BLKID_PARTS_ENTRY_DETAILS (1 << 2) +#define BLKID_PARTS_MAGIC (1 << 3) +extern int blkid_probe_set_partitions_flags(blkid_probe pr, int flags) + __ul_attribute__((nonnull)); + +/* binary interface */ +extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr) + __ul_attribute__((nonnull)); + +extern int blkid_partlist_numof_partitions(blkid_partlist ls) + __ul_attribute__((nonnull)); +extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls) + __ul_attribute__((nonnull)); +extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n) + __ul_attribute__((nonnull)); +extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n) + __ul_attribute__((nonnull)); +extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno) + __ul_attribute__((nonnull)); +extern blkid_parttable blkid_partition_get_table(blkid_partition par) + __ul_attribute__((nonnull)); + +extern const char *blkid_partition_get_name(blkid_partition par) + __ul_attribute__((nonnull)); +extern const char *blkid_partition_get_uuid(blkid_partition par) + __ul_attribute__((nonnull)); +extern int blkid_partition_get_partno(blkid_partition par) + __ul_attribute__((nonnull)); +extern blkid_loff_t blkid_partition_get_start(blkid_partition par) + __ul_attribute__((nonnull)); +extern blkid_loff_t blkid_partition_get_size(blkid_partition par) + __ul_attribute__((nonnull)); + +extern int blkid_partition_get_type(blkid_partition par) + __ul_attribute__((nonnull)); +extern const char *blkid_partition_get_type_string(blkid_partition par) + __ul_attribute__((nonnull)); +extern unsigned long long blkid_partition_get_flags(blkid_partition par) + __ul_attribute__((nonnull)); + +extern int blkid_partition_is_logical(blkid_partition par) + __ul_attribute__((nonnull)); +extern int blkid_partition_is_extended(blkid_partition par) + __ul_attribute__((nonnull)); +extern int blkid_partition_is_primary(blkid_partition par) + __ul_attribute__((nonnull)); + +extern const char *blkid_parttable_get_type(blkid_parttable tab) + __ul_attribute__((nonnull)); +extern const char *blkid_parttable_get_id(blkid_parttable tab) + __ul_attribute__((nonnull)); +extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab) + __ul_attribute__((nonnull)); +extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab) + __ul_attribute__((nonnull)); + +/* + * NAME=value low-level interface + */ +extern int blkid_do_probe(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_do_safeprobe(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_do_fullprobe(blkid_probe pr) + __ul_attribute__((nonnull)); + +/** + * BLKID_PROBE_OK: + * + * probing return value; superblock (RAID, partiton table, ...) succesfully detected + */ +#define BLKID_PROBE_OK 0 +/** + * BLKID_PROBE_NONE: + * + * probing return value; found nothing + */ +#define BLKID_PROBE_NONE 1 +/** + * BLKID_PROBE_ERROR: + * + * probing return value; probing ends with en error (see errno for more details) + */ +#define BLKID_PROBE_ERROR -1 +/** + * BLKID_PROBE_AMBIGUOUS: + * + * probing return value; more than one probing result, in this case, it's not + * safe to use the device automaticaly and user intervention is recommended + */ +#define BLKID_PROBE_AMBIGUOUS -2 + +extern int blkid_probe_numof_values(blkid_probe pr) + __ul_attribute__((nonnull)); +extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name, + const char **data, size_t *len) + __ul_attribute__((nonnull(1))); +extern int blkid_probe_lookup_value(blkid_probe pr, const char *name, + const char **data, size_t *len) + __ul_attribute__((nonnull(1, 2))); +extern int blkid_probe_has_value(blkid_probe pr, const char *name) + __ul_attribute__((nonnull)); +extern int blkid_do_wipe(blkid_probe pr, int dryrun) + __ul_attribute__((nonnull)); +extern int blkid_probe_step_back(blkid_probe pr) + __ul_attribute__((nonnull)); + +/* + * Deprecated functions/macros + */ +#ifndef BLKID_DISABLE_DEPRECATED + +#define BLKID_PROBREQ_LABEL BLKID_SUBLKS_LABEL +#define BLKID_PROBREQ_LABELRAW BLKID_SUBLKS_LABELRAW +#define BLKID_PROBREQ_UUID BLKID_SUBLKS_UUID +#define BLKID_PROBREQ_UUIDRAW BLKID_SUBLKS_UUIDRAW +#define BLKID_PROBREQ_TYPE BLKID_SUBLKS_TYPE +#define BLKID_PROBREQ_SECTYPE BLKID_SUBLKS_SECTYPE +#define BLKID_PROBREQ_USAGE BLKID_SUBLKS_USAGE +#define BLKID_PROBREQ_VERSION BLKID_SUBLKS_VERSION + +extern int blkid_probe_set_request(blkid_probe pr, int flags) + __ul_attribute__((deprecated)); + +extern int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage) + __ul_attribute__((deprecated)); + +extern int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[]) + __ul_attribute__((deprecated)); + +extern int blkid_probe_invert_filter(blkid_probe pr) + __ul_attribute__((deprecated)); + +extern int blkid_probe_reset_filter(blkid_probe pr) + __ul_attribute__((deprecated)); + +#endif /* BLKID_DISABLE_DEPRECATED */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BLKID_BLKID_H */ diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h new file mode 100644 index 0000000..007cc35 --- /dev/null +++ b/libblkid/src/blkidP.h @@ -0,0 +1,570 @@ +/* + * blkidP.h - Internal interfaces for libblkid + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#ifndef _BLKID_BLKIDP_H +#define _BLKID_BLKIDP_H + +/* Always confirm that /dev/disk-by symlinks match with LABEL/UUID on device */ +/* #define CONFIG_BLKID_VERIFY_UDEV 1 */ + +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> + +#ifndef UUID_STR_LEN +# define UUID_STR_LEN 37 +#endif + +#include "c.h" +#include "bitops.h" /* $(top_srcdir)/include/ */ +#include "blkdev.h" + +#include "debug.h" +#include "blkid.h" +#include "list.h" +#include "encode.h" + +/* + * This describes the attributes of a specific device. + * We can traverse all of the tags by bid_tags (linking to the tag bit_names). + * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag + * values, if they exist. + */ +struct blkid_struct_dev +{ + struct list_head bid_devs; /* All devices in the cache */ + struct list_head bid_tags; /* All tags for this device */ + blkid_cache bid_cache; /* Dev belongs to this cache */ + char *bid_name; /* Device real path (as used in cache) */ + char *bid_xname; /* Device path as used by application (maybe symlink..) */ + char *bid_type; /* Preferred device TYPE */ + int bid_pri; /* Device priority */ + dev_t bid_devno; /* Device major/minor number */ + time_t bid_time; /* Last update time of device */ + suseconds_t bid_utime; /* Last update time (microseconds) */ + unsigned int bid_flags; /* Device status bitflags */ + char *bid_label; /* Shortcut to device LABEL */ + char *bid_uuid; /* Shortcut to binary UUID */ +}; + +#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ +#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ +#define BLKID_BID_FL_REMOVABLE 0x0008 /* Device added by blkid_probe_all_removable() */ + +/* + * Each tag defines a NAME=value pair for a particular device. The tags + * are linked via bit_names for a single device, so that traversing the + * names list will get you a list of all tags associated with a device. + * They are also linked via bit_values for all devices, so one can easily + * search all tags with a given NAME for a specific value. + */ +struct blkid_struct_tag +{ + struct list_head bit_tags; /* All tags for this device */ + struct list_head bit_names; /* All tags with given NAME */ + char *bit_name; /* NAME of tag (shared) */ + char *bit_val; /* value of tag */ + blkid_dev bit_dev; /* pointer to device */ +}; +typedef struct blkid_struct_tag *blkid_tag; + +/* + * Chain IDs + */ +enum { + BLKID_CHAIN_SUBLKS, /* FS/RAID superblocks (enabled by default) */ + BLKID_CHAIN_TOPLGY, /* Block device topology */ + BLKID_CHAIN_PARTS, /* Partition tables */ + + BLKID_NCHAINS /* number of chains */ +}; + +struct blkid_chain { + const struct blkid_chaindrv *driver; /* chain driver */ + + int enabled; /* boolean */ + int flags; /* BLKID_<chain>_* */ + int binary; /* boolean */ + int idx; /* index of the current prober (or -1) */ + unsigned long *fltr; /* filter or NULL */ + void *data; /* private chain data or NULL */ +}; + +/* + * Chain driver + */ +struct blkid_chaindrv { + const size_t id; /* BLKID_CHAIN_* */ + const char *name; /* name of chain (for debug purpose) */ + const int dflt_flags; /* default chain flags */ + const int dflt_enabled; /* default enabled boolean */ + int has_fltr; /* boolean */ + + const struct blkid_idinfo **idinfos; /* description of probing functions */ + const size_t nidinfos; /* number of idinfos */ + + /* driver operations */ + int (*probe)(blkid_probe, struct blkid_chain *); + int (*safeprobe)(blkid_probe, struct blkid_chain *); + void (*free_data)(blkid_probe, void *); +}; + +/* chains */ +extern const struct blkid_chaindrv superblocks_drv; +extern const struct blkid_chaindrv topology_drv; +extern const struct blkid_chaindrv partitions_drv; + +/* + * Low-level probe result + */ +struct blkid_prval +{ + const char *name; /* value name */ + unsigned char *data; /* value data */ + size_t len; /* length of value data */ + + struct blkid_chain *chain; /* owner */ + struct list_head prvals; /* list of results */ +}; + +/* + * Filesystem / Raid magic strings + */ +struct blkid_idmag +{ + const char *magic; /* magic string */ + unsigned int len; /* length of magic */ + + const char *hoff; /* hint which contains byte offset to kboff */ + long kboff; /* kilobyte offset of superblock */ + unsigned int sboff; /* byte offset within superblock */ + + int is_zoned; /* indicate magic location is calculated based on zone position */ + long zonenum; /* zone number which has superblock */ + long kboff_inzone; /* kilobyte offset of superblock in a zone */ +}; + +/* + * Filesystem / Raid description + */ +struct blkid_idinfo +{ + const char *name; /* fs, raid or partition table name */ + int usage; /* BLKID_USAGE_* flag */ + int flags; /* BLKID_IDINFO_* flags */ + int minsz; /* minimal device size */ + + /* probe function */ + int (*probefunc)(blkid_probe pr, const struct blkid_idmag *mag); + + struct blkid_idmag magics[]; /* NULL or array with magic strings */ +}; + +#define BLKID_NONE_MAGIC {{ NULL }} + +/* + * tolerant FS - can share the same device with more filesystems (e.g. typical + * on CD-ROMs). We need this flag to detect ambivalent results (e.g. valid fat + * and valid linux swap on the same device). + */ +#define BLKID_IDINFO_TOLERANT (1 << 1) + +struct blkid_bufinfo { + unsigned char *data; + uint64_t off; + uint64_t len; + struct list_head bufs; /* list of buffers */ +}; + +/* + * Probing hint + */ +struct blkid_hint { + char *name; + uint64_t value; + struct list_head hints; +}; + +/* + * Low-level probing control struct + */ +struct blkid_struct_probe +{ + int fd; /* device file descriptor */ + uint64_t off; /* begin of data on the device */ + uint64_t size; /* end of data on the device */ + + dev_t devno; /* device number (st.st_rdev) */ + dev_t disk_devno; /* devno of the whole-disk or 0 */ + unsigned int blkssz; /* sector size (BLKSSZGET ioctl) */ + mode_t mode; /* struct stat.sb_mode */ + uint64_t zone_size; /* zone size (BLKGETZONESZ ioctl) */ + + int flags; /* private library flags */ + int prob_flags; /* always zeroized by blkid_do_*() */ + + uint64_t wipe_off; /* begin of the wiped area */ + uint64_t wipe_size; /* size of the wiped area */ + struct blkid_chain *wipe_chain; /* superblock, partition, ... */ + + struct list_head buffers; /* list of buffers */ + struct list_head hints; + + struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */ + struct blkid_chain *cur_chain; /* current chain */ + + struct list_head values; /* results */ + + struct blkid_struct_probe *parent; /* for clones */ + struct blkid_struct_probe *disk_probe; /* whole-disk probing */ +}; + +/* private flags library flags */ +#define BLKID_FL_PRIVATE_FD (1 << 1) /* see blkid_new_probe_from_filename() */ +#define BLKID_FL_TINY_DEV (1 << 2) /* <= 1.47MiB (floppy or so) */ +#define BLKID_FL_CDROM_DEV (1 << 3) /* is a CD/DVD drive */ +#define BLKID_FL_NOSCAN_DEV (1 << 4) /* do not scan this device */ +#define BLKID_FL_MODIF_BUFF (1 << 5) /* cached buffers has been modified */ +#define BLKID_FL_OPAL_LOCKED (1 << 6) /* OPAL device is locked (I/O errors) */ + +/* private per-probing flags */ +#define BLKID_PROBE_FL_IGNORE_PT (1 << 1) /* ignore partition table */ + +extern blkid_probe blkid_clone_probe(blkid_probe parent); +extern blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr); + +/* + * Evaluation methods (for blkid_eval_* API) + */ +enum { + BLKID_EVAL_UDEV = 0, + BLKID_EVAL_SCAN, + + __BLKID_EVAL_LAST +}; + +/* + * Library config options + */ +struct blkid_config { + int eval[__BLKID_EVAL_LAST]; /* array with EVALUATION=<udev,cache> options */ + int nevals; /* number of elems in eval array */ + int uevent; /* SEND_UEVENT=<yes|not> option */ + char *cachefile; /* CACHE_FILE=<path> option */ +}; + +extern struct blkid_config *blkid_read_config(const char *filename) + __ul_attribute__((warn_unused_result)); +extern void blkid_free_config(struct blkid_config *conf); + +/* + * Minimum number of seconds between device probes, even when reading + * from the cache. This is to avoid re-probing all devices which were + * just probed by another program that does not share the cache. + */ +#define BLKID_PROBE_MIN 2 + +/* + * Time in seconds an entry remains verified in the in-memory cache + * before being reverified (in case of long-running processes that + * keep a cache in memory and continue to use it for a long time). + */ +#define BLKID_PROBE_INTERVAL 200 + +/* This describes an entire blkid cache file and probed devices. + * We can traverse all of the found devices via bic_list. + * We can traverse all of the tag types by bic_tags, which hold empty tags + * for each tag type. Those tags can be used as list_heads for iterating + * through all devices with a specific tag type (e.g. LABEL). + */ +struct blkid_struct_cache +{ + struct list_head bic_devs; /* List head of all devices */ + struct list_head bic_tags; /* List head of all tag types */ + time_t bic_time; /* Last probe time */ + time_t bic_ftime; /* Mod time of the cachefile */ + unsigned int bic_flags; /* Status flags of the cache */ + char *bic_filename; /* filename of cache */ + blkid_probe probe; /* low-level probing stuff */ +}; + +#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ +#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ + +/* config file */ +#define BLKID_CONFIG_FILE "/etc/blkid.conf" + +/* cache file on systemds with /run */ +#define BLKID_RUNTIME_TOPDIR "/run" +#define BLKID_RUNTIME_DIR BLKID_RUNTIME_TOPDIR "/blkid" +#define BLKID_CACHE_FILE BLKID_RUNTIME_DIR "/blkid.tab" + +/* old systems */ +#define BLKID_CACHE_FILE_OLD "/etc/blkid.tab" + +#define BLKID_ERR_IO 5 +#define BLKID_ERR_SYSFS 9 +#define BLKID_ERR_MEM 12 +#define BLKID_ERR_CACHE 14 +#define BLKID_ERR_DEV 19 +#define BLKID_ERR_PARAM 22 +#define BLKID_ERR_BIG 27 + +/* + * Priority settings for different types of devices + */ +#define BLKID_PRI_UBI 50 +#define BLKID_PRI_DM 40 +#define BLKID_PRI_EVMS 30 +#define BLKID_PRI_LVM 20 +#define BLKID_PRI_MD 10 + +#define BLKID_DEBUG_HELP (1 << 0) +#define BLKID_DEBUG_INIT (1 << 1) +#define BLKID_DEBUG_CACHE (1 << 2) +#define BLKID_DEBUG_CONFIG (1 << 3) +#define BLKID_DEBUG_DEV (1 << 4) +#define BLKID_DEBUG_DEVNAME (1 << 5) +#define BLKID_DEBUG_DEVNO (1 << 6) +#define BLKID_DEBUG_EVALUATE (1 << 7) +#define BLKID_DEBUG_LOWPROBE (1 << 8) +#define BLKID_DEBUG_PROBE (1 << 9) +#define BLKID_DEBUG_READ (1 << 10) +#define BLKID_DEBUG_SAVE (1 << 11) +#define BLKID_DEBUG_TAG (1 << 12) +#define BLKID_DEBUG_BUFFER (1 << 13) +#define BLKID_DEBUG_ALL 0xFFFF /* (1 << 16) aka FFFF is expected by API */ + +UL_DEBUG_DECLARE_MASK(libblkid); +#define DBG(m, x) __UL_DBG(libblkid, BLKID_DEBUG_, m, x) +#define ON_DBG(m, x) __UL_DBG_CALL(libblkid, BLKID_DEBUG_, m, x) + +#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libblkid) +#include "debugobj.h" + +extern void blkid_debug_dump_dev(blkid_dev dev); + + +/* devno.c */ +struct dir_list { + char *name; + struct dir_list *next; +}; +extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **) + __attribute__((nonnull(1,4))); +extern int blkid_driver_has_major(const char *drvname, int drvmaj) + __attribute__((warn_unused_result)); + +/* read.c */ +extern void blkid_read_cache(blkid_cache cache) + __attribute__((nonnull)); + +/* save.c */ +extern int blkid_flush_cache(blkid_cache cache) + __attribute__((nonnull)); + +/* cache */ +extern char *blkid_safe_getenv(const char *arg) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern char *blkid_get_cache_filename(struct blkid_config *conf) + __attribute__((warn_unused_result)); +/* + * Functions to create and find a specific tag type: tag.c + */ +extern void blkid_free_tag(blkid_tag tag); +extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength) + __attribute__((nonnull(1,2))); + +/* + * Functions to create and find a specific tag type: dev.c + */ +extern blkid_dev blkid_new_dev(void) + __attribute__((warn_unused_result)); +extern void blkid_free_dev(blkid_dev dev); + +/* probe.c */ +extern int blkid_probe_is_tiny(blkid_probe pr) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); +extern int blkid_probe_is_cdrom(blkid_probe pr) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); +extern int blkdid_probe_is_opal_locked(blkid_probe pr) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern unsigned char *blkid_probe_get_buffer(blkid_probe pr, + uint64_t off, uint64_t len) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern int blkid_probe_get_dimension(blkid_probe pr, + uint64_t *off, uint64_t *size) + __attribute__((nonnull)); + +extern int blkid_probe_set_dimension(blkid_probe pr, + uint64_t off, uint64_t size) + __attribute__((nonnull)); + +extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, + uint64_t *offset, const struct blkid_idmag **res) + __attribute__((nonnull(1))); + +/* returns superblock according to 'struct blkid_idmag' */ +extern unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size); +#define blkid_probe_get_sb(_pr, _mag, type) \ + ((type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type))) + +extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern int blkid_probe_is_covered_by_pt(blkid_probe pr, + uint64_t offset, uint64_t size) + __attribute__((warn_unused_result)); + +extern void blkid_probe_chain_reset_values(blkid_probe pr, struct blkid_chain *chn) + __attribute__((nonnull)); +extern int blkid_probe_chain_save_values(blkid_probe pr, + struct blkid_chain *chn, + struct list_head *vals) + __attribute__((nonnull)); + +extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, + const char *name) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern void blkid_probe_free_value(struct blkid_prval *v); + + +extern void blkid_probe_append_values_list(blkid_probe pr, + struct list_head *vals) + __attribute__((nonnull)); + +extern void blkid_probe_free_values_list(struct list_head *vals); + +extern struct blkid_chain *blkid_probe_get_chain(blkid_probe pr) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern int __blkid_probe_invert_filter(blkid_probe pr, int chain) + __attribute__((nonnull)); +extern int __blkid_probe_reset_filter(blkid_probe pr, int chain) + __attribute__((nonnull)); +extern int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[]) + __attribute__((nonnull)); + +extern void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); + +extern struct blkid_prval *blkid_probe_new_val(void) + __attribute__((warn_unused_result)); +extern int blkid_probe_set_value(blkid_probe pr, const char *name, + const unsigned char *data, size_t len) + __attribute__((nonnull)); +extern int blkid_probe_value_set_data(struct blkid_prval *v, + const unsigned char *data, size_t len) + __attribute__((nonnull)); + +extern int blkid_probe_vsprintf_value(blkid_probe pr, const char *name, + const char *fmt, va_list ap) + __attribute__((nonnull)); + +extern int blkid_probe_sprintf_value(blkid_probe pr, const char *name, + const char *fmt, ...) + __attribute__((nonnull)) + __attribute__ ((__format__ (__printf__, 3, 4))); + +extern int blkid_probe_set_magic(blkid_probe pr, uint64_t offset, + size_t len, const unsigned char *magic) + __attribute__((nonnull)); + +extern int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected) + __attribute__((nonnull)); +extern int blkid_probe_verify_csum_buf(blkid_probe pr, size_t n, const void *csum, + const void *expected) __attribute__((nonnull)); + +extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len) + __attribute__((nonnull)); +extern int blkid_uuid_is_empty(const unsigned char *buf, size_t len); + +extern size_t blkid_rtrim_whitespace(unsigned char *str) + __attribute__((nonnull)); +extern size_t blkid_ltrim_whitespace(unsigned char *str) + __attribute__((nonnull)); + +extern void blkid_probe_set_wiper(blkid_probe pr, uint64_t off, + uint64_t size) + __attribute__((nonnull)); +extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn, + uint64_t off, uint64_t size) + __attribute__((nonnull)) + __attribute__((warn_unused_result)); +extern void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size) + __attribute__((nonnull)); + +extern int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value) + __attribute__((nonnull(1,2))) + __attribute__((warn_unused_result)); + +extern int blkid_probe_get_partitions_flags(blkid_probe pr) + __attribute__((nonnull)); + +/* filter bitmap macros */ +#define blkid_bmp_wordsize (8 * sizeof(unsigned long)) +#define blkid_bmp_idx_bit(item) (1UL << ((item) % blkid_bmp_wordsize)) +#define blkid_bmp_idx_byte(item) ((item) / blkid_bmp_wordsize) + +#define blkid_bmp_set_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] |= blkid_bmp_idx_bit(item)) + +#define blkid_bmp_unset_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] &= ~blkid_bmp_idx_bit(item)) + +#define blkid_bmp_get_item(bmp, item) \ + ((bmp)[ blkid_bmp_idx_byte(item) ] & blkid_bmp_idx_bit(item)) + +#define blkid_bmp_nwords(max_items) \ + (((max_items) + blkid_bmp_wordsize) / blkid_bmp_wordsize) + +#define blkid_bmp_nbytes(max_items) \ + (blkid_bmp_nwords(max_items) * sizeof(unsigned long)) + +#endif /* _BLKID_BLKIDP_H */ diff --git a/libblkid/src/cache.c b/libblkid/src/cache.c new file mode 100644 index 0000000..5f59873 --- /dev/null +++ b/libblkid/src/cache.c @@ -0,0 +1,223 @@ +/* + * cache.c - allocation/initialization/free routines for cache + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include "blkidP.h" +#include "env.h" + +/** + * SECTION:cache + * @title: Cache + * @short_description: basic routines to work with libblkid cache + * + * Block device information is normally kept in a cache file blkid.tab and is + * verified to still be valid before being returned to the user (if the user has + * read permission on the raw block device, otherwise not). The cache file also + * allows unprivileged users (normally anyone other than root, or those not in the + * "disk" group) to locate devices by label/id. The standard location of the + * cache file can be overridden by the environment variable BLKID_FILE. + * + * In situations where one is getting information about a single known device, it + * does not impact performance whether the cache is used or not (unless you are + * not able to read the block device directly). If you are dealing with multiple + * devices, use of the cache is highly recommended (even if empty) as devices will + * be scanned at most one time and the on-disk cache will be updated if possible. + * There is rarely a reason not to use the cache. + * + * In some cases (modular kernels), block devices are not even visible until after + * they are accessed the first time, so it is critical that there is some way to + * locate these devices without enumerating only visible devices, so the use of + * the cache file is required in this situation. + */ +static const char *get_default_cache_filename(void) +{ + struct stat st; + + if (stat(BLKID_RUNTIME_TOPDIR, &st) == 0 && S_ISDIR(st.st_mode)) + return BLKID_CACHE_FILE; /* cache in /run */ + + return BLKID_CACHE_FILE_OLD; /* cache in /etc */ +} + +/* returns allocated path to cache */ +char *blkid_get_cache_filename(struct blkid_config *conf) +{ + char *filename; + + filename = safe_getenv("BLKID_FILE"); + if (filename) + filename = strdup(filename); + else if (conf) + filename = conf->cachefile ? strdup(conf->cachefile) : NULL; + else { + struct blkid_config *c = blkid_read_config(NULL); + if (!c) + filename = strdup(get_default_cache_filename()); + else { + filename = c->cachefile; /* already allocated */ + c->cachefile = NULL; + blkid_free_config(c); + } + } + return filename; +} + +/** + * blkid_get_cache: + * @cache: pointer to return cache handler + * @filename: path to the cache file or NULL for the default path + * + * Allocates and initializes library cache handler. + * + * Returns: 0 on success or number less than zero in case of error. + */ +int blkid_get_cache(blkid_cache *ret_cache, const char *filename) +{ + blkid_cache cache; + + if (!ret_cache) + return -BLKID_ERR_PARAM; + + if (!(cache = calloc(1, sizeof(struct blkid_struct_cache)))) + return -BLKID_ERR_MEM; + + DBG(CACHE, ul_debugobj(cache, "alloc (from %s)", filename ? filename : "default cache")); + INIT_LIST_HEAD(&cache->bic_devs); + INIT_LIST_HEAD(&cache->bic_tags); + + if (filename && !*filename) + filename = NULL; + if (filename) + cache->bic_filename = strdup(filename); + else + cache->bic_filename = blkid_get_cache_filename(NULL); + + blkid_read_cache(cache); + *ret_cache = cache; + return 0; +} + +/** + * blkid_put_cache: + * @cache: cache handler + * + * Saves changes to cache file. + */ +void blkid_put_cache(blkid_cache cache) +{ + if (!cache) + return; + + (void) blkid_flush_cache(cache); + + DBG(CACHE, ul_debugobj(cache, "freeing cache struct")); + + /* DBG(CACHE, ul_debug_dump_cache(cache)); */ + + while (!list_empty(&cache->bic_devs)) { + blkid_dev dev = list_entry(cache->bic_devs.next, + struct blkid_struct_dev, + bid_devs); + blkid_free_dev(dev); + } + + DBG(CACHE, ul_debugobj(cache, "freeing cache tag heads")); + while (!list_empty(&cache->bic_tags)) { + blkid_tag tag = list_entry(cache->bic_tags.next, + struct blkid_struct_tag, + bit_tags); + + while (!list_empty(&tag->bit_names)) { + blkid_tag bad = list_entry(tag->bit_names.next, + struct blkid_struct_tag, + bit_names); + + DBG(CACHE, ul_debugobj(cache, "warning: unfreed tag %s=%s", + bad->bit_name, bad->bit_val)); + blkid_free_tag(bad); + } + blkid_free_tag(tag); + } + + blkid_free_probe(cache->probe); + + free(cache->bic_filename); + free(cache); +} + +/** + * blkid_gc_cache: + * @cache: cache handler + * + * Removes garbage (non-existing devices) from the cache. + */ +void blkid_gc_cache(blkid_cache cache) +{ + struct list_head *p, *pnext; + struct stat st; + + if (!cache) + return; + + list_for_each_safe(p, pnext, &cache->bic_devs) { + blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); + if (stat(dev->bid_name, &st) < 0) { + DBG(CACHE, ul_debugobj(cache, "freeing non-existing %s", dev->bid_name)); + blkid_free_dev(dev); + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + } else { + DBG(CACHE, ul_debug("Device %s exists", dev->bid_name)); + } + } +} + +#ifdef TEST_PROGRAM +int main(int argc, char** argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_init_debug(BLKID_DEBUG_ALL); + + if ((argc > 2)) { + fprintf(stderr, "Usage: %s [filename] \n", argv[0]); + exit(1); + } + + if ((ret = blkid_get_cache(&cache, argv[1])) < 0) { + fprintf(stderr, "error %d parsing cache file %s\n", ret, + argv[1] ? argv[1] : blkid_get_cache_filename(NULL)); + exit(1); + } + if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if ((ret = blkid_probe_all(cache)) < 0) + fprintf(stderr, "error probing devices\n"); + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/libblkid/src/config.c b/libblkid/src/config.c new file mode 100644 index 0000000..d3f5eea --- /dev/null +++ b/libblkid/src/config.c @@ -0,0 +1,202 @@ +/* + * config.c - blkid.conf routines + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#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_ERRNO_H +#include <errno.h> +#endif +#include <stdint.h> +#include <stdarg.h> + +#include "blkidP.h" +#include "env.h" + +static int parse_evaluate(struct blkid_config *conf, char *s) +{ + while(s && *s) { + char *sep; + + if (conf->nevals >= __BLKID_EVAL_LAST) + goto err; + sep = strchr(s, ','); + if (sep) + *sep = '\0'; + if (strcmp(s, "udev") == 0) + conf->eval[conf->nevals] = BLKID_EVAL_UDEV; + else if (strcmp(s, "scan") == 0) + conf->eval[conf->nevals] = BLKID_EVAL_SCAN; + else + goto err; + conf->nevals++; + if (sep) + s = sep + 1; + else + break; + } + return 0; +err: + DBG(CONFIG, ul_debug( + "config file: unknown evaluation method '%s'.", s)); + return -1; +} + +static int parse_next(FILE *fd, struct blkid_config *conf) +{ + char buf[BUFSIZ]; + char *s; + + /* read the next non-blank non-comment line */ + do { + if (fgets (buf, sizeof(buf), fd) == NULL) + return feof(fd) ? 0 : -1; + s = strchr (buf, '\n'); + if (!s) { + /* Missing final newline? Otherwise extremely */ + /* long line - assume file was corrupted */ + if (feof(fd)) + s = strchr (buf, '\0'); + else { + DBG(CONFIG, ul_debug( + "config file: missing newline at line '%s'.", + buf)); + return -1; + } + } + *s = '\0'; + if (--s >= buf && *s == '\r') + *s = '\0'; + + s = buf; + while (*s == ' ' || *s == '\t') /* skip space */ + s++; + + } while (*s == '\0' || *s == '#'); + + if (!strncmp(s, "SEND_UEVENT=", 12)) { + s += 12; + if (*s && !strcasecmp(s, "yes")) + conf->uevent = TRUE; + else if (*s) + conf->uevent = FALSE; + } else if (!strncmp(s, "CACHE_FILE=", 11)) { + s += 11; + free(conf->cachefile); + if (*s) + conf->cachefile = strdup(s); + else + conf->cachefile = NULL; + } else if (!strncmp(s, "EVALUATE=", 9)) { + s += 9; + if (*s && parse_evaluate(conf, s) == -1) + return -1; + } else { + DBG(CONFIG, ul_debug( + "config file: unknown option '%s'.", s)); + return -1; + } + return 0; +} + +/* return real config data or built-in default */ +struct blkid_config *blkid_read_config(const char *filename) +{ + struct blkid_config *conf; + FILE *f; + + if (!filename) + filename = safe_getenv("BLKID_CONF"); + if (!filename) + filename = BLKID_CONFIG_FILE; + + conf = calloc(1, sizeof(*conf)); + if (!conf) + return NULL; + conf->uevent = -1; + + DBG(CONFIG, ul_debug("reading config file: %s.", filename)); + + f = fopen(filename, "r" UL_CLOEXECSTR); + if (!f) { + DBG(CONFIG, ul_debug("%s: does not exist, using built-in default", filename)); + goto dflt; + } + while (!feof(f)) { + if (parse_next(f, conf)) { + DBG(CONFIG, ul_debug("%s: parse error", filename)); + goto err; + } + } +dflt: + if (!conf->nevals) { + conf->eval[0] = BLKID_EVAL_UDEV; + conf->eval[1] = BLKID_EVAL_SCAN; + conf->nevals = 2; + } + if (!conf->cachefile) + conf->cachefile = strdup(BLKID_CACHE_FILE); + if (conf->uevent == -1) + conf->uevent = TRUE; + if (f) + fclose(f); + return conf; +err: + free(conf->cachefile); + free(conf); + fclose(f); + return NULL; +} + +void blkid_free_config(struct blkid_config *conf) +{ + if (!conf) + return; + free(conf->cachefile); + free(conf); +} + +#ifdef TEST_PROGRAM +/* + * usage: tst_config [<filename>] + */ +int main(int argc, char *argv[]) +{ + int i; + struct blkid_config *conf; + char *filename = NULL; + + blkid_init_debug(BLKID_DEBUG_ALL); + + if (argc == 2) + filename = argv[1]; + + conf = blkid_read_config(filename); + if (!conf) + return EXIT_FAILURE; + + printf("EVALUATE: "); + for (i = 0; i < conf->nevals; i++) + printf("%s ", conf->eval[i] == BLKID_EVAL_UDEV ? "udev" : "scan"); + printf("\n"); + + printf("SEND UEVENT: %s\n", conf->uevent ? "TRUE" : "FALSE"); + printf("CACHE_FILE: %s\n", conf->cachefile); + + blkid_free_config(conf); + return EXIT_SUCCESS; +} +#endif diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c new file mode 100644 index 0000000..c38ec3d --- /dev/null +++ b/libblkid/src/dev.c @@ -0,0 +1,279 @@ +/* + * dev.c - allocation/initialization/free routines for dev + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <stdlib.h> +#include <string.h> + +#include "blkidP.h" + +/* + * NOTE: reference manual is not structured as code. The following section is a generic + * section for all high-level cache search+iterate routines. + */ + +/** + * SECTION:search + * @title: Search and iterate + * @short_description: search devices and iterate over devices in the cache. + * + * Note that high-level probing API provides information about superblocks + * (filesystems/raids) only. For partitions and topology is necessary to use + * the low-level API. + */ + +blkid_dev blkid_new_dev(void) +{ + blkid_dev dev; + + if (!(dev = calloc(1, sizeof(struct blkid_struct_dev)))) + return NULL; + + DBG(DEV, ul_debugobj(dev, "alloc")); + INIT_LIST_HEAD(&dev->bid_devs); + INIT_LIST_HEAD(&dev->bid_tags); + + return dev; +} + +void blkid_free_dev(blkid_dev dev) +{ + if (!dev) + return; + + DBG(DEV, ul_debugobj(dev, "freeing (%s)", dev->bid_name)); + + list_del(&dev->bid_devs); + while (!list_empty(&dev->bid_tags)) { + blkid_tag tag = list_entry(dev->bid_tags.next, + struct blkid_struct_tag, + bit_tags); + blkid_free_tag(tag); + } + free(dev->bid_xname); + free(dev->bid_name); + free(dev); +} + +/* + * Given a blkid device, return its name. The function returns the name + * previously used for blkid_get_dev(). This name does not have to be canonical + * (real path) name, but for example symlink. + */ +const char *blkid_dev_devname(blkid_dev dev) +{ + if (!dev) + return NULL; + if (dev->bid_xname) + return dev->bid_xname; + return dev->bid_name; +} + +void blkid_debug_dump_dev(blkid_dev dev) +{ + struct list_head *p; + + if (!dev) { + printf(" dev: NULL\n"); + return; + } + + fprintf(stderr, " dev: name = %s\n", dev->bid_name); + fprintf(stderr, " dev: DEVNO=\"0x%0lx\"\n", (unsigned long)dev->bid_devno); + fprintf(stderr, " dev: TIME=\"%lld.%lld\"\n", (long long)dev->bid_time, (long long)dev->bid_utime); + fprintf(stderr, " dev: PRI=\"%d\"\n", dev->bid_pri); + fprintf(stderr, " dev: flags = 0x%08X\n", dev->bid_flags); + + list_for_each(p, &dev->bid_tags) { + blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); + if (tag) + fprintf(stderr, " tag: %s=\"%s\"\n", tag->bit_name, + tag->bit_val); + else + fprintf(stderr, " tag: NULL\n"); + } +} + +/* + * dev iteration routines for the public libblkid interface. + * + * These routines do not expose the list.h implementation, which are a + * contamination of the namespace, and which force us to reveal far, far + * too much of our internal implementation. I'm not convinced I want + * to keep list.h in the long term, anyway. It's fine for kernel + * programming, but performance is not the #1 priority for this + * library, and I really don't like the trade-off of type-safety for + * performance for this application. [tytso:20030125.2007EST] + */ + +/* + * This series of functions iterate over all devices in a blkid cache + */ +#define DEV_ITERATE_MAGIC 0x01a5284c + +struct blkid_struct_dev_iterate { + int magic; + blkid_cache cache; + char *search_type; + char *search_value; + struct list_head *p; +}; + +blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache) +{ + blkid_dev_iterate iter; + + if (!cache) { + errno = EINVAL; + return NULL; + } + + iter = malloc(sizeof(struct blkid_struct_dev_iterate)); + if (iter) { + iter->magic = DEV_ITERATE_MAGIC; + iter->cache = cache; + iter->p = cache->bic_devs.next; + iter->search_type = NULL; + iter->search_value = NULL; + } + return iter; +} + +int blkid_dev_set_search(blkid_dev_iterate iter, + const char *search_type, const char *search_value) +{ + char *new_type, *new_value; + + if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type || + !search_value) + return -1; + new_type = malloc(strlen(search_type)+1); + new_value = malloc(strlen(search_value)+1); + if (!new_type || !new_value) { + free(new_type); + free(new_value); + return -1; + } + strcpy(new_type, search_type); + strcpy(new_value, search_value); + free(iter->search_type); + free(iter->search_value); + iter->search_type = new_type; + iter->search_value = new_value; + return 0; +} + +/* + * Return 0 on success, -1 on error + */ +int blkid_dev_next(blkid_dev_iterate iter, + blkid_dev *ret_dev) +{ + blkid_dev dev; + + if (!ret_dev || !iter || iter->magic != DEV_ITERATE_MAGIC) + return -1; + *ret_dev = NULL; + while (iter->p != &iter->cache->bic_devs) { + dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs); + iter->p = iter->p->next; + if (iter->search_type && + !blkid_dev_has_tag(dev, iter->search_type, + iter->search_value)) + continue; + *ret_dev = dev; + return 0; + } + return -1; +} + +void blkid_dev_iterate_end(blkid_dev_iterate iter) +{ + if (!iter || iter->magic != DEV_ITERATE_MAGIC) + return; + iter->magic = 0; + free(iter->search_type); + free(iter->search_value); + free(iter); +} + +#ifdef TEST_PROGRAM +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif + +static void __attribute__((__noreturn__)) usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog); + fprintf(stderr, "\tList all devices and exit\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + blkid_dev_iterate iter; + blkid_cache cache = NULL; + blkid_dev dev; + int c, ret; + char *tmp; + char *file = NULL; + char *search_type = NULL; + char *search_value = NULL; + + while ((c = getopt (argc, argv, "m:f:")) != EOF) + switch (c) { + case 'f': + file = optarg; + break; + case 'm': + { + int mask = strtoul (optarg, &tmp, 0); + if (*tmp) { + fprintf(stderr, "Invalid debug mask: %s\n", + optarg); + exit(1); + } + blkid_init_debug(mask); + break; + } + case '?': + usage(argv[0]); + } + if (argc >= optind+2) { + search_type = argv[optind]; + search_value = argv[optind+1]; + optind += 2; + } + if (argc != optind) + usage(argv[0]); + + if ((ret = blkid_get_cache(&cache, file)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + + iter = blkid_dev_iterate_begin(cache); + if (search_type) + blkid_dev_set_search(iter, search_type, search_value); + while (blkid_dev_next(iter, &dev) == 0) { + printf("Device: %s\n", blkid_dev_devname(dev)); + } + blkid_dev_iterate_end(iter); + + + blkid_put_cache(cache); + return (0); +} +#endif diff --git a/libblkid/src/devname.c b/libblkid/src/devname.c new file mode 100644 index 0000000..3f48032 --- /dev/null +++ b/libblkid/src/devname.c @@ -0,0 +1,647 @@ +/* + * devname.c - get a dev by its device inode name + * + * Copyright (C) Andries Brouwer + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#define _GNU_SOURCE 1 + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <dirent.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <time.h> + +#include "blkidP.h" + +#include "canonicalize.h" /* $(top_srcdir)/include */ +#include "pathnames.h" +#include "sysfs.h" +#include "fileutils.h" + +/* + * Find a dev struct in the cache by device name, if available. + * + * If there is no entry with the specified device name, and the create + * flag is set, then create an empty device entry. + */ +blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) +{ + blkid_dev dev = NULL, tmp; + struct list_head *p, *pnext; + char *cn = NULL; + + if (!cache || !devname) + return NULL; + + /* search by name */ + list_for_each(p, &cache->bic_devs) { + tmp = list_entry(p, struct blkid_struct_dev, bid_devs); + if (strcmp(tmp->bid_name, devname) != 0) + continue; + dev = tmp; + break; + } + + /* try canonicalize the name */ + if (!dev && (cn = canonicalize_path(devname))) { + if (strcmp(cn, devname) != 0) { + DBG(DEVNAME, ul_debug("search canonical %s", cn)); + list_for_each(p, &cache->bic_devs) { + tmp = list_entry(p, struct blkid_struct_dev, bid_devs); + if (strcmp(tmp->bid_name, cn) != 0) + continue; + dev = tmp; + + /* update name returned by blkid_dev_devname() */ + free(dev->bid_xname); + dev->bid_xname = strdup(devname); + break; + } + } else { + free(cn); + cn = NULL; + } + } + + if (!dev && (flags & BLKID_DEV_CREATE)) { + if (access(devname, F_OK) < 0) + goto done; + dev = blkid_new_dev(); + if (!dev) + goto done; + dev->bid_time = (uintmax_t)1 << (sizeof(time_t) * 8 - 1); + if (cn) { + dev->bid_name = cn; + dev->bid_xname = strdup(devname); + cn = NULL; /* see free() below */ + } else + dev->bid_name = strdup(devname); + + dev->bid_cache = cache; + list_add_tail(&dev->bid_devs, &cache->bic_devs); + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + } + + if (flags & BLKID_DEV_VERIFY) { + dev = blkid_verify(cache, dev); + if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) + goto done; + /* + * If the device is verified, then search the blkid + * cache for any entries that match on the type, uuid, + * and label, and verify them; if a cache entry can + * not be verified, then it's stale and so we remove + * it. + */ + list_for_each_safe(p, pnext, &cache->bic_devs) { + blkid_dev dev2 = list_entry(p, struct blkid_struct_dev, bid_devs); + if (dev2->bid_flags & BLKID_BID_FL_VERIFIED) + continue; + if (!dev->bid_type || !dev2->bid_type || + strcmp(dev->bid_type, dev2->bid_type) != 0) + continue; + if (dev->bid_label && dev2->bid_label && + strcmp(dev->bid_label, dev2->bid_label) != 0) + continue; + if (dev->bid_uuid && dev2->bid_uuid && + strcmp(dev->bid_uuid, dev2->bid_uuid) != 0) + continue; + if ((dev->bid_label && !dev2->bid_label) || + (!dev->bid_label && dev2->bid_label) || + (dev->bid_uuid && !dev2->bid_uuid) || + (!dev->bid_uuid && dev2->bid_uuid)) + continue; + dev2 = blkid_verify(cache, dev2); + if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED)) + blkid_free_dev(dev2); + } + } +done: + if (dev) + DBG(DEVNAME, ul_debug("%s requested, found %s in cache", devname, dev->bid_name)); + free(cn); + return dev; +} + +/* Directories where we will try to search for device names */ +static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL }; + +/* + * Return 1 if the device is a device-mapper 'leaf' node + * not holding any other devices in its hierarchy. + */ +static int is_dm_leaf(const char *devname) +{ + DIR *dir; + char path[NAME_MAX + 18 + 1]; + int ret; + + snprintf(path, sizeof(path), "/sys/block/%s/holders", devname); + if ((dir = opendir(path)) == NULL) + return 0; + + ret = xreaddir(dir) == NULL ? 1 : 0; /* 'leaf' has no entries */ + + closedir(dir); + return ret; +} + +/* + * Probe a single block device to add to the device cache. + */ +static void probe_one(blkid_cache cache, const char *ptname, + dev_t devno, int pri, int only_if_new, int removable) +{ + blkid_dev dev = NULL; + struct list_head *p, *pnext; + const char **dir; + char *devname = NULL; + + /* See if we already have this device number in the cache. */ + list_for_each_safe(p, pnext, &cache->bic_devs) { + blkid_dev tmp = list_entry(p, struct blkid_struct_dev, + bid_devs); + if (tmp->bid_devno == devno) { + if (only_if_new && !access(tmp->bid_name, F_OK)) + return; + dev = blkid_verify(cache, tmp); + if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) + break; + dev = NULL; + } + } + if (dev && dev->bid_devno == devno) + goto set_pri; + + /* Try to translate private device-mapper dm-<N> names + * to standard /dev/mapper/<name>. + */ + if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) { + devname = canonicalize_dm_name(ptname); + if (!devname) + blkid__scan_dir("/dev/mapper", devno, NULL, &devname); + if (devname) + goto get_dev; + } + + /* + * Take a quick look at /dev/ptname for the device number. We check + * all of the likely device directories. If we don't find it, or if + * the stat information doesn't check out, use blkid_devno_to_devname() + * to find it via an exhaustive search for the device major/minor. + */ + for (dir = dirlist; *dir; dir++) { + struct stat st; + char device[256]; + + snprintf(device, sizeof(device), "%s/%s", *dir, ptname); + if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && + dev->bid_devno == devno) + goto set_pri; + + if (stat(device, &st) == 0 && + (S_ISBLK(st.st_mode) || + (S_ISCHR(st.st_mode) && !strncmp(ptname, "ubi", 3))) && + st.st_rdev == devno) { + devname = strdup(device); + goto get_dev; + } + } + /* Do a short-cut scan of /dev/mapper first */ + if (!devname) + blkid__scan_dir("/dev/mapper", devno, NULL, &devname); + if (!devname) { + devname = blkid_devno_to_devname(devno); + if (!devname) + return; + } + +get_dev: + dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); + free(devname); + +set_pri: + if (dev) { + if (pri) + dev->bid_pri = pri; + else if (!strncmp(dev->bid_name, "/dev/mapper/", 12)) { + dev->bid_pri = BLKID_PRI_DM; + if (is_dm_leaf(ptname)) + dev->bid_pri += 5; + } else if (!strncmp(ptname, "md", 2)) + dev->bid_pri = BLKID_PRI_MD; + if (removable) + dev->bid_flags |= BLKID_BID_FL_REMOVABLE; + } +} + +#define PROC_PARTITIONS "/proc/partitions" +#define VG_DIR "/proc/lvm/VGs" + +/* + * This function initializes the UUID cache with devices from the LVM + * proc hierarchy. We currently depend on the names of the LVM + * hierarchy giving us the device structure in /dev. (XXX is this a + * safe thing to do?) + */ +#ifdef VG_DIR +static dev_t lvm_get_devno(const char *lvm_device) +{ + FILE *lvf; + char buf[1024]; + int ma, mi; + dev_t ret = 0; + + DBG(DEVNAME, ul_debug("opening %s", lvm_device)); + if ((lvf = fopen(lvm_device, "r" UL_CLOEXECSTR)) == NULL) { + DBG(DEVNAME, ul_debug("%s: (%d) %m", lvm_device, errno)); + return 0; + } + + while (fgets(buf, sizeof(buf), lvf)) { + if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { + ret = makedev(ma, mi); + break; + } + } + fclose(lvf); + + return ret; +} + +static void lvm_probe_all(blkid_cache cache, int only_if_new) +{ + DIR *vg_list; + struct dirent *vg_iter; + int vg_len = strlen(VG_DIR); + dev_t dev; + + if ((vg_list = opendir(VG_DIR)) == NULL) + return; + + DBG(DEVNAME, ul_debug("probing LVM devices under %s", VG_DIR)); + + while ((vg_iter = readdir(vg_list)) != NULL) { + DIR *lv_list; + char *vdirname; + char *vg_name; + struct dirent *lv_iter; + size_t len; + + vg_name = vg_iter->d_name; + if (!strcmp(vg_name, ".") || !strcmp(vg_name, "..")) + continue; + len = vg_len + strlen(vg_name) + 8; + vdirname = malloc(len); + if (!vdirname) + goto exit; + snprintf(vdirname, len, "%s/%s/LVs", VG_DIR, vg_name); + + lv_list = opendir(vdirname); + free(vdirname); + if (lv_list == NULL) + continue; + + while ((lv_iter = readdir(lv_list)) != NULL) { + char *lv_name, *lvm_device; + + lv_name = lv_iter->d_name; + if (!strcmp(lv_name, ".") || !strcmp(lv_name, "..")) + continue; + + len = vg_len + strlen(vg_name) + strlen(lv_name) + 8; + lvm_device = malloc(len); + if (!lvm_device) { + closedir(lv_list); + goto exit; + } + snprintf(lvm_device, len, "%s/%s/LVs/%s", VG_DIR, vg_name, + lv_name); + dev = lvm_get_devno(lvm_device); + snprintf(lvm_device, len, "%s/%s", vg_name, lv_name); + DBG(DEVNAME, ul_debug("Probe LVM dev %s: devno 0x%04X", + lvm_device, + (unsigned int) dev)); + probe_one(cache, lvm_device, dev, BLKID_PRI_LVM, + only_if_new, 0); + free(lvm_device); + } + closedir(lv_list); + } +exit: + closedir(vg_list); +} +#endif + +static void +ubi_probe_all(blkid_cache cache, int only_if_new) +{ + const char **dirname; + + for (dirname = dirlist; *dirname; dirname++) { + DIR *dir; + struct dirent *iter; + + DBG(DEVNAME, ul_debug("probing UBI volumes under %s", + *dirname)); + + dir = opendir(*dirname); + if (dir == NULL) + continue ; + + while ((iter = readdir(dir)) != NULL) { + char *name; + struct stat st; + dev_t dev; + + name = iter->d_name; +#ifdef _DIRENT_HAVE_D_TYPE + if (iter->d_type != DT_UNKNOWN && + iter->d_type != DT_CHR && iter->d_type != DT_LNK) + continue; +#endif + if (!strcmp(name, ".") || !strcmp(name, "..") || + !strstr(name, "ubi")) + continue; + if (!strcmp(name, "ubi_ctrl")) + continue; + if (fstatat(dirfd(dir), name, &st, 0)) + continue; + + dev = st.st_rdev; + + if (!S_ISCHR(st.st_mode) || !minor(dev)) + continue; + DBG(DEVNAME, ul_debug("Probe UBI vol %s/%s: devno 0x%04X", + *dirname, name, (int) dev)); + probe_one(cache, name, dev, BLKID_PRI_UBI, only_if_new, 0); + } + closedir(dir); + } +} + +/* + * This function uses /sys to read all block devices in way compatible with + * /proc/partitions (like the original libblkid implementation) + */ +static int +sysfs_probe_all(blkid_cache cache, int only_if_new, int only_removable) +{ + DIR *sysfs; + struct dirent *dev; + + sysfs = opendir(_PATH_SYS_BLOCK); + if (!sysfs) + return -BLKID_ERR_SYSFS; + + DBG(DEVNAME, ul_debug(" probe /sys/block")); + + /* scan /sys/block */ + while ((dev = xreaddir(sysfs))) { + DIR *dir = NULL; + dev_t devno; + size_t nparts = 0; + unsigned int maxparts = 0, removable = 0; + struct dirent *part; + struct path_cxt *pc = NULL; + uint64_t size = 0; + + DBG(DEVNAME, ul_debug("checking %s", dev->d_name)); + + devno = sysfs_devname_to_devno(dev->d_name); + if (!devno) + goto next; + pc = ul_new_sysfs_path(devno, NULL, NULL); + if (!pc) + goto next; + + if (ul_path_read_u64(pc, &size, "size") != 0) + size = 0; + if (ul_path_read_u32(pc, &removable, "removable") != 0) + removable = 0; + + /* ignore empty devices */ + if (!size) + goto next; + + /* accept removable if only removable requested */ + if (only_removable) { + if (!removable) + goto next; + + /* emulate /proc/partitions + * -- ignore empty devices and non-partitionable removable devices */ + } else { + if (ul_path_read_u32(pc, &maxparts, "ext_range") != 0) + maxparts = 0; + if (!maxparts && removable) + goto next; + } + + DBG(DEVNAME, ul_debug("read device name %s", dev->d_name)); + + dir = ul_path_opendir(pc, NULL); + if (!dir) + goto next; + + /* read /sys/block/<name>/ do get partitions */ + while ((part = xreaddir(dir))) { + dev_t partno; + + if (!sysfs_blkdev_is_partition_dirent(dir, part, dev->d_name)) + continue; + + /* ignore extended partitions + * -- recount size to blocks like /proc/partitions */ + if (ul_path_readf_u64(pc, &size, "%s/size", part->d_name) == 0 + && (size >> 1) == 1) + continue; + partno = __sysfs_devname_to_devno(NULL, part->d_name, dev->d_name); + if (!partno) + continue; + + DBG(DEVNAME, ul_debug(" Probe partition dev %s, devno 0x%04X", + part->d_name, (unsigned int) partno)); + nparts++; + probe_one(cache, part->d_name, partno, 0, only_if_new, 0); + } + + if (!nparts) { + /* add non-partitioned whole disk to cache */ + DBG(DEVNAME, ul_debug(" Probe whole dev %s, devno 0x%04X", + dev->d_name, (unsigned int) devno)); + probe_one(cache, dev->d_name, devno, 0, only_if_new, 0); + } else { + /* remove partitioned whole-disk from cache */ + struct list_head *p, *pnext; + + list_for_each_safe(p, pnext, &cache->bic_devs) { + blkid_dev tmp = list_entry(p, struct blkid_struct_dev, + bid_devs); + if (tmp->bid_devno == devno) { + DBG(DEVNAME, ul_debug(" freeing %s", tmp->bid_name)); + blkid_free_dev(tmp); + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + break; + } + } + } + next: + if (dir) + closedir(dir); + if (pc) + ul_unref_path(pc); + } + + closedir(sysfs); + return 0; +} + +/* + * Read the device data for all available block devices in the system. + */ +static int probe_all(blkid_cache cache, int only_if_new, int update_interval) +{ + int rc; + + if (!cache) + return -BLKID_ERR_PARAM; + + if (cache->bic_flags & BLKID_BIC_FL_PROBED && + time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL) { + DBG(PROBE, ul_debug("don't re-probe [delay < %d]", BLKID_PROBE_INTERVAL)); + return 0; + } + + blkid_read_cache(cache); +#ifdef VG_DIR + lvm_probe_all(cache, only_if_new); +#endif + ubi_probe_all(cache, only_if_new); + + rc = sysfs_probe_all(cache, only_if_new, 0); + + /* Don't mark the change as "probed" if /sys not avalable */ + if (update_interval && rc == 0) { + cache->bic_time = time(NULL); + cache->bic_flags |= BLKID_BIC_FL_PROBED; + } + + blkid_flush_cache(cache); + return 0; +} + +/** + * blkid_probe_all: + * @cache: cache handler + * + * Probes all block devices. + * + * Returns: 0 on success, or number less than zero in case of error. + */ +int blkid_probe_all(blkid_cache cache) +{ + int ret; + + DBG(PROBE, ul_debug("Begin blkid_probe_all()")); + ret = probe_all(cache, 0, 1); + DBG(PROBE, ul_debug("End blkid_probe_all() [rc=%d]", ret)); + return ret; +} + +/** + * blkid_probe_all_new: + * @cache: cache handler + * + * Probes all new block devices. + * + * Returns: 0 on success, or number less than zero in case of error. + */ +int blkid_probe_all_new(blkid_cache cache) +{ + int ret; + + DBG(PROBE, ul_debug("Begin blkid_probe_all_new()")); + ret = probe_all(cache, 1, 0); + DBG(PROBE, ul_debug("End blkid_probe_all_new() [rc=%d]", ret)); + return ret; +} + +/** + * blkid_probe_all_removable: + * @cache: cache handler + * + * The libblkid probing is based on devices from /proc/partitions by default. + * This file usually does not contain removable devices (e.g. CDROMs) and this kind + * of devices are invisible for libblkid. + * + * This function adds removable block devices to @cache (probing is based on + * information from the /sys directory). Don't forget that removable devices + * (floppies, CDROMs, ...) could be pretty slow. It's very bad idea to call + * this function by default. + * + * Note that devices which were detected by this function won't be written to + * blkid.tab cache file. + * + * Returns: 0 on success, or number less than zero in case of error. + */ +int blkid_probe_all_removable(blkid_cache cache) +{ + int ret; + + DBG(PROBE, ul_debug("Begin blkid_probe_all_removable()")); + ret = sysfs_probe_all(cache, 0, 1); + DBG(PROBE, ul_debug("End blkid_probe_all_removable() [rc=%d]", ret)); + return ret; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_init_debug(BLKID_DEBUG_ALL); + if (argc != 1) { + fprintf(stderr, "Usage: %s\n" + "Probe all devices and exit\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if (blkid_probe_all(cache) < 0) + printf("%s: error probing devices\n", argv[0]); + + if (blkid_probe_all_removable(cache) < 0) + printf("%s: error probing removable devices\n", argv[0]); + + blkid_put_cache(cache); + return (0); +} +#endif diff --git a/libblkid/src/devno.c b/libblkid/src/devno.c new file mode 100644 index 0000000..a1ab543 --- /dev/null +++ b/libblkid/src/devno.c @@ -0,0 +1,368 @@ +/* + * devno.c - find a particular device by its device number (major/minor) + * + * Copyright (C) 2000, 2001, 2003 Theodore Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <dirent.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_MKDEV_H +#include <sys/mkdev.h> +#endif +#include <fcntl.h> +#include <inttypes.h> + +#include "blkidP.h" +#include "pathnames.h" +#include "sysfs.h" +#include "strutils.h" + +static char *blkid_strconcat(const char *a, const char *b, const char *c) +{ + char *res, *p; + size_t len, al, bl, cl; + + al = a ? strlen(a) : 0; + bl = b ? strlen(b) : 0; + cl = c ? strlen(c) : 0; + + len = al + bl + cl; + if (!len) + return NULL; + p = res = malloc(len + 1); + if (!res) + return NULL; + if (al) + p = mempcpy(p, a, al); + if (bl) + p = mempcpy(p, b, bl); + if (cl) + p = mempcpy(p, c, cl); + *p = '\0'; + return res; +} + +/* + * This function adds an entry to the directory list + */ +static void add_to_dirlist(const char *dir, const char *subdir, + struct dir_list **list) +{ + struct dir_list *dp; + + dp = malloc(sizeof(struct dir_list)); + if (!dp) + return; + dp->name = subdir ? blkid_strconcat(dir, "/", subdir) : + dir ? strdup(dir) : NULL; + + if (!dp->name) { + free(dp); + return; + } + dp->next = *list; + *list = dp; +} + +/* + * This function frees a directory list + */ +static void free_dirlist(struct dir_list **list) +{ + struct dir_list *dp, *next; + + for (dp = *list; dp; dp = next) { + next = dp->next; + free(dp->name); + free(dp); + } + *list = NULL; +} + +void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list, + char **devname) +{ + DIR *dir; + struct dirent *dp; + struct stat st; + + if ((dir = opendir(dirname)) == NULL) + return; + + while ((dp = readdir(dir)) != NULL) { +#ifdef _DIRENT_HAVE_D_TYPE + if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK && + dp->d_type != DT_LNK && dp->d_type != DT_DIR) + continue; +#endif + if (dp->d_name[0] == '.' && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) + continue; + + if (fstatat(dirfd(dir), dp->d_name, &st, 0)) + continue; + + if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { + *devname = blkid_strconcat(dirname, "/", dp->d_name); + DBG(DEVNO, ul_debug("found 0x%llx at %s", (long long)devno, + *devname)); + break; + } + + if (!list || !S_ISDIR(st.st_mode)) + continue; + + /* add subdirectory (but not symlink) to the list */ +#ifdef _DIRENT_HAVE_D_TYPE + if (dp->d_type == DT_LNK) + continue; + if (dp->d_type == DT_UNKNOWN) +#endif + { + if (fstatat(dirfd(dir), dp->d_name, &st, AT_SYMLINK_NOFOLLOW) || + !S_ISDIR(st.st_mode)) + continue; /* symlink or fstatat() failed */ + } + + if (*dp->d_name == '.' || ( +#ifdef _DIRENT_HAVE_D_TYPE + dp->d_type == DT_DIR && +#endif + strcmp(dp->d_name, "shm") == 0)) + /* ignore /dev/.{udev,mount,mdadm} and /dev/shm */ + continue; + + add_to_dirlist(dirname, dp->d_name, list); + } + closedir(dir); +} + +/* Directories where we will try to search for device numbers */ +static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL }; + +/** + * SECTION: misc + * @title: Miscellaneous utils + * @short_description: mix of various utils for low-level and high-level API + */ + + + +static char *scandev_devno_to_devpath(dev_t devno) +{ + struct dir_list *list = NULL, *new_list = NULL; + char *devname = NULL; + const char **dir; + + /* + * Add the starting directories to search in reverse order of + * importance, since we are using a stack... + */ + for (dir = devdirs; *dir; dir++) + add_to_dirlist(*dir, NULL, &list); + + while (list) { + struct dir_list *current = list; + + list = list->next; + DBG(DEVNO, ul_debug("directory %s", current->name)); + blkid__scan_dir(current->name, devno, &new_list, &devname); + free(current->name); + free(current); + if (devname) + break; + /* + * If we're done checking at this level, descend to + * the next level of subdirectories. (breadth-first) + */ + if (list == NULL) { + list = new_list; + new_list = NULL; + } + } + free_dirlist(&list); + free_dirlist(&new_list); + + return devname; +} + +/** + * blkid_devno_to_devname: + * @devno: device number + * + * This function finds the pathname to a block device with a given + * device number. + * + * Returns: a pointer to allocated memory to the pathname on success, + * and NULL on failure. + */ +char *blkid_devno_to_devname(dev_t devno) +{ + char *path; + char buf[PATH_MAX]; + + path = sysfs_devno_to_devpath(devno, buf, sizeof(buf)); + if (path) + path = strdup(path); + if (!path) + path = scandev_devno_to_devpath(devno); + + if (!path) { + DBG(DEVNO, ul_debug("blkid: couldn't find devno 0x%04lx", + (unsigned long) devno)); + } else { + DBG(DEVNO, ul_debug("found devno 0x%04llx as %s", (long long)devno, path)); + } + + return path; +} + + +/** + * blkid_devno_to_wholedisk: + * @dev: device number + * @diskname: buffer to return diskname (or NULL) + * @len: diskname buffer size (or 0) + * @diskdevno: pointer to returns devno of entire disk (or NULL) + * + * This function uses sysfs to convert the @devno device number to the *name* + * of the whole disk. The function DOES NOT return full device name. The @dev + * argument could be partition or whole disk -- both is converted. + * + * For example: sda1, 0x0801 --> sda, 0x0800 + * + * For conversion to the full disk *path* use blkid_devno_to_devname(), for + * example: + * + * <informalexample> + * <programlisting> + * + * dev_t dev = 0x0801, disk; // sda1 = 8:1 + * char *diskpath, diskname[32]; + * + * blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk); + * diskpath = blkid_devno_to_devname(disk); + * + * // print "0x0801: sda, /dev/sda, 8:0 + * printf("0x%x: %s, %s, %d:%d\n", + * dev, diskname, diskpath, major(disk), minor(disk)); + * + * free(diskpath); + * + * </programlisting> + * </informalexample> + * + * Returns: 0 on success or -1 in case of error. + */ +int blkid_devno_to_wholedisk(dev_t dev, char *diskname, + size_t len, dev_t *diskdevno) +{ + return sysfs_devno_to_wholedisk( dev, diskname, len, diskdevno); +} + +/* + * Returns 1 if the @major number is associated with @drvname. + */ +int blkid_driver_has_major(const char *drvname, int drvmaj) +{ + FILE *f; + char buf[128]; + int match = 0; + + f = fopen(_PATH_PROC_DEVICES, "r" UL_CLOEXECSTR); + if (!f) + return 0; + + while (fgets(buf, sizeof(buf), f)) { /* skip to block dev section */ + if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0) + break; + } + + while (fgets(buf, sizeof(buf), f)) { + int maj; + char name[64 + 1]; + + if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2) + continue; + + if (maj == drvmaj && strcmp(name, drvname) == 0) { + match = 1; + break; + } + } + + fclose(f); + + DBG(DEVNO, ul_debug("major %d %s associated with '%s' driver", + drvmaj, match ? "is" : "is NOT", drvname)); + return match; +} + +#ifdef TEST_PROGRAM +int main(int argc, char** argv) +{ + char *devname, *tmp; + char diskname[PATH_MAX]; + int devmaj, devmin; + dev_t devno, disk_devno; + const char *errmsg = "Couldn't parse %s: %s\n"; + + blkid_init_debug(BLKID_DEBUG_ALL); + if ((argc != 2) && (argc != 3)) { + fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n" + "Resolve a device number to a device name\n", + argv[0], argv[0]); + exit(1); + } + if (argc == 2) { + devno = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "device number", argv[1]); + exit(1); + } + } else { + devmaj = strtoul(argv[1], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "major number", argv[1]); + exit(1); + } + devmin = strtoul(argv[2], &tmp, 0); + if (*tmp) { + fprintf(stderr, errmsg, "minor number", argv[2]); + exit(1); + } + devno = makedev(devmaj, devmin); + } + printf("Looking for device 0x%04llx\n", (long long)devno); + devname = blkid_devno_to_devname(devno); + free(devname); + + printf("Looking for whole-device for 0x%04llx\n", (long long)devno); + if (blkid_devno_to_wholedisk(devno, diskname, + sizeof(diskname), &disk_devno) == 0) + printf("found devno 0x%04llx as /dev/%s\n", (long long) disk_devno, diskname); + + return 0; +} +#endif diff --git a/libblkid/src/encode.c b/libblkid/src/encode.c new file mode 100644 index 0000000..8213873 --- /dev/null +++ b/libblkid/src/encode.c @@ -0,0 +1,263 @@ + +/* + * encode.c - string conversion routines (mostly for compatibility with + * udev/volume_id) + * + * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +#include "blkidP.h" +#include "strutils.h" + +/** + * SECTION: encode + * @title: Encoding utils + * @short_description: encode strings to safe udev-compatible formats + * + */ + +/* count of characters used to encode one unicode char */ +static int utf8_encoded_expected_len(const char *str) +{ + unsigned char c = (unsigned char)str[0]; + + if (c < 0x80) + return 1; + if ((c & 0xe0) == 0xc0) + return 2; + if ((c & 0xf0) == 0xe0) + return 3; + if ((c & 0xf8) == 0xf0) + return 4; + if ((c & 0xfc) == 0xf8) + return 5; + if ((c & 0xfe) == 0xfc) + return 6; + return 0; +} + +/* decode one unicode char */ +static int utf8_encoded_to_unichar(const char *str) +{ + int unichar; + int len; + int i; + + len = utf8_encoded_expected_len(str); + switch (len) { + case 1: + return (int)str[0]; + case 2: + unichar = str[0] & 0x1f; + break; + case 3: + unichar = (int)str[0] & 0x0f; + break; + case 4: + unichar = (int)str[0] & 0x07; + break; + case 5: + unichar = (int)str[0] & 0x03; + break; + case 6: + unichar = (int)str[0] & 0x01; + break; + default: + return -1; + } + + for (i = 1; i < len; i++) { + if (((int)str[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= (int)str[i] & 0x3f; + } + + return unichar; +} + +/* expected size used to encode one unicode char */ +static int utf8_unichar_to_encoded_len(int unichar) +{ + if (unichar < 0x80) + return 1; + if (unichar < 0x800) + return 2; + if (unichar < 0x10000) + return 3; + if (unichar < 0x200000) + return 4; + if (unichar < 0x4000000) + return 5; + return 6; +} + +/* check if unicode char has a valid numeric range */ +static int utf8_unichar_valid_range(int unichar) +{ + if (unichar > 0x10ffff) + return 0; + if ((unichar & 0xfffff800) == 0xd800) + return 0; + if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) + return 0; + if ((unichar & 0xffff) == 0xffff) + return 0; + return 1; +} + +/* validate one encoded unicode char and return its length */ +static int utf8_encoded_valid_unichar(const char *str) +{ + int len; + int unichar; + int i; + + len = utf8_encoded_expected_len(str); + if (len == 0) + return -1; + + /* ascii is valid */ + if (len == 1) + return 1; + + /* check if expected encoded chars are available */ + for (i = 0; i < len; i++) + if ((str[i] & 0x80) != 0x80) + return -1; + + unichar = utf8_encoded_to_unichar(str); + + /* check if encoded length matches encoded value */ + if (utf8_unichar_to_encoded_len(unichar) != len) + return -1; + + /* check if value has valid range */ + if (!utf8_unichar_valid_range(unichar)) + return -1; + + return len; +} + +static int is_whitelisted(char c, const char *white) +{ + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + strchr("#+-.:=@_", c) != NULL || + (white != NULL && strchr(white, c) != NULL)) + return 1; + return 0; +} + +/** + * blkid_encode_string: + * @str: input string to be encoded + * @str_enc: output string to store the encoded input string + * @len: maximum size of the output string, which may be + * four times as long as the input string + * + * Encode all potentially unsafe characters of a string to the + * corresponding hex value prefixed by '\x'. + * + * Returns: 0 if the entire string was copied, non-zero otherwise. + **/ +int blkid_encode_string(const char *str, char *str_enc, size_t len) +{ + size_t i, j; + + if (!str || !str_enc || !len) + return -1; + + for (i = 0, j = 0; str[i] != '\0'; i++) { + int seqlen; + + seqlen = utf8_encoded_valid_unichar(&str[i]); + if (seqlen > 1) { + if (len-j < (size_t)seqlen) + goto err; + memcpy(&str_enc[j], &str[i], seqlen); + j += seqlen; + i += (seqlen-1); + } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) { + if (len-j < 4) + goto err; + sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); + j += 4; + } else { + if (len-j < 1) + goto err; + str_enc[j] = str[i]; + j++; + } + if (j+3 >= len) + goto err; + } + if (len-j < 1) + goto err; + str_enc[j] = '\0'; + return 0; +err: + return -1; +} + +/** + * blkid_safe_string: + * @str: input string + * @str_safe: output string + * @len: size of output string + * + * Processing whitespace characters. Allows valid ascii,valid utf8. + * Replace everything else with'_' + * + * Returns: 0 on success or -1 in case of error. + */ +int blkid_safe_string(const char *str, char *str_safe, size_t len) +{ + size_t i = 0; + + if (!str || !str_safe || !len) + return -1; + + __normalize_whitespace( + (const unsigned char *) str, strnlen(str, len), + (unsigned char *) str_safe, len); + + while (i < len && str_safe[i] != '\0') { + int seqsz; + + /* accept ASCII from ' ' to '~' */ + if (str_safe[i] > 0x20 && str_safe[i] <= 0x7E) + i++; + + /* accept hex encoding */ + else if (str_safe[i] == '\\' && str_safe[i+1] == 'x') + i += 2; + + /* replace whitespace */ + else if (isspace(str_safe[i])) + str_safe[i++] = '_'; + + /* accept valid utf8 */ + else if ((seqsz = utf8_encoded_valid_unichar(&str_safe[i])) >= 1) + i += seqsz; + + /* everything else is replaced with '_' */ + else + str_safe[i++] = '_'; + } + + str_safe[len - 1] = '\0'; + return 0; +} diff --git a/libblkid/src/evaluate.c b/libblkid/src/evaluate.c new file mode 100644 index 0000000..cd2f3a3 --- /dev/null +++ b/libblkid/src/evaluate.c @@ -0,0 +1,325 @@ +/* + * evaluate.c - very high-level API to evaluate LABELs or UUIDs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#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_ERRNO_H +#include <errno.h> +#endif +#include <stdint.h> +#include <stdarg.h> + +#include "pathnames.h" +#include "canonicalize.h" +#include "closestream.h" + +#include "blkidP.h" + +/** + * SECTION:evaluate + * @title: Tags and Spec evaluation + * @short_description: top-level API for LABEL and UUID evaluation. + * + * This API provides very simple and portable way how evaluate LABEL and UUID + * tags. The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and + * 2.6 systems and on systems with or without udev. Currently, the libblkid + * library supports "udev" and "scan" methods. The "udev" method uses udev + * /dev/disk/by-* symlinks and the "scan" method scans all block devices from + * the /proc/partitions file. The evaluation could be controlled by the + * /etc/blkid.conf config file. The default is to try "udev" and then "scan" + * method. + * + * The blkid_evaluate_tag() also automatically informs udevd when an obsolete + * /dev/disk/by-* symlink is detected. + * + * If you are not sure how translate LABEL or UUID to the device name use this + * API. + */ + +#ifdef CONFIG_BLKID_VERIFY_UDEV +/* returns zero when the device has NAME=value (LABEL/UUID) */ +static int verify_tag(const char *devname, const char *name, const char *value) +{ + blkid_probe pr; + int fd = -1, rc = -1; + size_t len; + const char *data; + int errsv = 0; + + if (strcmp(token, "ID") == 0) + return 0; /* non-content tag */ + + pr = blkid_new_probe(); + if (!pr) + return -1; + + blkid_probe_enable_superblocks(pr, TRUE); + blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID); + + blkid_probe_enable_partitions(pr, TRUE); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + + fd = open(devname, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + errsv = errno; + goto done; + } + if (blkid_probe_set_device(pr, fd, 0, 0)) + goto done; + rc = blkid_do_safeprobe(pr); + if (rc) + goto done; + rc = blkid_probe_lookup_value(pr, name, &data, &len); + if (!rc) + rc = memcmp(value, data, len); +done: + DBG(EVALUATE, ul_debug("%s: %s verification %s", + devname, name, rc == 0 ? "PASS" : "FAILED")); + if (fd >= 0) + close(fd); + blkid_free_probe(pr); + + /* for non-root users we use unverified udev links */ + return errsv == EACCES ? 0 : rc; +} +#endif /* CONFIG_BLKID_VERIFY_UDEV*/ + +/** + * blkid_send_uevent: + * @devname: absolute path to the device + * @action: event string + * + * Returns: -1 in case of failure, or 0 on success. + */ +int blkid_send_uevent(const char *devname, const char *action) +{ + char uevent[PATH_MAX]; + struct stat st; + FILE *f; + int rc = -1; + + DBG(EVALUATE, ul_debug("%s: uevent '%s' requested", devname, action)); + + if (!devname || !action) + return -1; + if (stat(devname, &st) || !S_ISBLK(st.st_mode)) + return -1; + + snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent", + major(st.st_rdev), minor(st.st_rdev)); + + f = fopen(uevent, "w" UL_CLOEXECSTR); + if (f) { + rc = 0; + if (fputs(action, f) >= 0) + rc = 0; + if (close_stream(f) != 0) + DBG(EVALUATE, ul_debug("write failed: %s", uevent)); + } + DBG(EVALUATE, ul_debug("%s: send uevent %s", + uevent, rc == 0 ? "SUCCESS" : "FAILED")); + return rc; +} + +static char *evaluate_by_udev(const char *token, const char *value, int uevent) +{ + char dev[PATH_MAX]; + char *path = NULL; + size_t len; + struct stat st; + + DBG(EVALUATE, ul_debug("evaluating by udev %s=%s", token, value)); + + if (!strcmp(token, "UUID")) + strcpy(dev, _PATH_DEV_BYUUID "/"); + else if (!strcmp(token, "LABEL")) + strcpy(dev, _PATH_DEV_BYLABEL "/"); + else if (!strcmp(token, "PARTLABEL")) + strcpy(dev, _PATH_DEV_BYPARTLABEL "/"); + else if (!strcmp(token, "PARTUUID")) + strcpy(dev, _PATH_DEV_BYPARTUUID "/"); + else if (!strcmp(token, "ID")) + strcpy(dev, _PATH_DEV_BYID "/"); + else { + DBG(EVALUATE, ul_debug("unsupported token %s", token)); + return NULL; /* unsupported tag */ + } + + len = strlen(dev); + if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0) + return NULL; + + DBG(EVALUATE, ul_debug("expected udev link: %s", dev)); + + if (stat(dev, &st)) + goto failed; /* link or device does not exist */ + + if (!S_ISBLK(st.st_mode)) + return NULL; + + path = canonicalize_path(dev); + if (!path) + return NULL; + +#ifdef CONFIG_BLKID_VERIFY_UDEV + if (verify_tag(path, token, value)) + goto failed; +#endif + return path; + +failed: + DBG(EVALUATE, ul_debug("failed to evaluate by udev")); + + if (uevent && path) + blkid_send_uevent(path, "change"); + free(path); + return NULL; +} + +static char *evaluate_by_scan(const char *token, const char *value, + blkid_cache *cache, struct blkid_config *conf) +{ + blkid_cache c = cache ? *cache : NULL; + char *res; + + DBG(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value)); + + if (!c) { + char *cachefile = blkid_get_cache_filename(conf); + int rc = blkid_get_cache(&c, cachefile); + free(cachefile); + if (rc < 0) + return NULL; + } + if (!c) + return NULL; + + res = blkid_get_devname(c, token, value); + + if (cache) + *cache = c; + else + blkid_put_cache(c); + + return res; +} + +/** + * blkid_evaluate_tag: + * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo") + * @value: token data (e.g. "foo") + * @cache: pointer to cache (or NULL when you don't want to re-use the cache) + * + * Returns: allocated string with a device name. + */ +char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache) +{ + struct blkid_config *conf = NULL; + char *t = NULL, *v = NULL; + char *ret = NULL; + int i; + + if (!token) + return NULL; + + DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "", + value ? value : "")); + + if (!value) { + if (!strchr(token, '=')) { + ret = strdup(token); + goto out; + } + if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v) + goto out; + token = t; + value = v; + } + + conf = blkid_read_config(NULL); + if (!conf) + goto out; + + for (i = 0; i < conf->nevals; i++) { + if (conf->eval[i] == BLKID_EVAL_UDEV) + ret = evaluate_by_udev(token, value, conf->uevent); + else if (conf->eval[i] == BLKID_EVAL_SCAN) + ret = evaluate_by_scan(token, value, cache, conf); + if (ret) + break; + } + + DBG(EVALUATE, ul_debug("%s=%s evaluated as %s", token, value, ret)); +out: + blkid_free_config(conf); + free(t); + free(v); + return ret; +} + +/** + * blkid_evaluate_spec: + * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0) + * @cache: pointer to cache (or NULL when you don't want to re-use the cache) + * + * All returned paths are canonicalized, device-mapper paths are converted + * to the /dev/mapper/name format. + * + * Returns: allocated string with a device name. + */ +char *blkid_evaluate_spec(const char *spec, blkid_cache *cache) +{ + char *t = NULL, *v = NULL, *res; + + if (!spec) + return NULL; + + if (strchr(spec, '=') && + blkid_parse_tag_string(spec, &t, &v) != 0) /* parse error */ + return NULL; + + if (v) + res = blkid_evaluate_tag(t, v, cache); + else + res = canonicalize_path(spec); + + free(t); + free(v); + return res; +} + + +#ifdef TEST_PROGRAM +int main(int argc, char *argv[]) +{ + blkid_cache cache = NULL; + char *res; + + if (argc < 2) { + fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]); + return EXIT_FAILURE; + } + + res = blkid_evaluate_spec(argv[1], &cache); + if (res) + printf("%s\n", res); + if (cache) + blkid_put_cache(cache); + + return res ? EXIT_SUCCESS : EXIT_FAILURE; +} +#endif diff --git a/libblkid/src/fuzz.c b/libblkid/src/fuzz.c new file mode 100644 index 0000000..772340b --- /dev/null +++ b/libblkid/src/fuzz.c @@ -0,0 +1,52 @@ +#include "blkidP.h" +#include "fuzz.h" + +#include <stdlib.h> +#include <unistd.h> + +static int process_file(const char *name) +{ + int rc = -1; + blkid_probe pr = blkid_new_probe_from_filename(name); + if (pr != NULL) { + blkid_probe_enable_partitions(pr, TRUE); + blkid_probe_set_partitions_flags(pr, FALSE); + blkid_probe_enable_superblocks(pr, TRUE); + blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_DEFAULT | BLKID_SUBLKS_FSINFO | BLKID_SUBLKS_MAGIC | BLKID_SUBLKS_VERSION | BLKID_SUBLKS_BADCSUM); + rc = blkid_do_safeprobe(pr) == -1 ? -1 : 0; + } + blkid_free_probe(pr); + return rc; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + int fd; + char name[] = "/tmp/test-script-fuzz.XXXXXX"; + + fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC); + if (fd == -1) + err(EXIT_FAILURE, "mkostemp() failed"); + + if (write(fd, data, size) != (ssize_t)size) + goto out; + + process_file(name); +out: + close(fd); + unlink(name); + return 0; +} + +#ifndef FUZZ_TARGET +int main(int argc, char **argv) +{ + for (int i = 1; i < argc; i++) { + printf("%s ", argv[i]); + if (process_file(argv[i]) == 0) + printf(" OK\n"); + else + printf(" FAILED\n"); + + } +} +#endif diff --git a/libblkid/src/getsize.c b/libblkid/src/getsize.c new file mode 100644 index 0000000..abe6ebc --- /dev/null +++ b/libblkid/src/getsize.c @@ -0,0 +1,34 @@ +/* + * getsize.c --- get the size of a partition. + * + * Copyright (C) 1995, 1995 Theodore Ts'o. + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "blkidP.h" + +/** + * blkid_get_dev_size: + * @fd: file descriptor + * + * Returns: size (in bytes) of the block device or size of the regular file or 0. + */ +blkid_loff_t blkid_get_dev_size(int fd) +{ + unsigned long long bytes; + + if (blkdev_get_size(fd, &bytes)) + return 0; + + return bytes; +} + diff --git a/libblkid/src/init.c b/libblkid/src/init.c new file mode 100644 index 0000000..172225f --- /dev/null +++ b/libblkid/src/init.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008-2013 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: init + * @title: Library initialization + * @short_description: initialize debugging + */ + +#include <stdarg.h> + +#include "blkidP.h" + +UL_DEBUG_DEFINE_MASK(libblkid); +UL_DEBUG_DEFINE_MASKNAMES(libblkid) = +{ + { "all", BLKID_DEBUG_ALL, "info about all subsystems" }, + { "cache", BLKID_DEBUG_CACHE, "blkid tags cache" }, + { "config", BLKID_DEBUG_CONFIG, "config file utils" }, + { "dev", BLKID_DEBUG_DEV, "device utils" }, + { "devname", BLKID_DEBUG_DEVNAME, "/proc/partitions evaluation" }, + { "devno", BLKID_DEBUG_DEVNO, "conversions to device name" }, + { "evaluate", BLKID_DEBUG_EVALUATE, "tags resolving" }, + { "help", BLKID_DEBUG_HELP, "this help" }, + { "lowprobe", BLKID_DEBUG_LOWPROBE, "superblock/raids/partitions probing" }, + { "buffer", BLKID_DEBUG_BUFFER, "low-probing buffers" }, + { "probe", BLKID_DEBUG_PROBE, "devices verification" }, + { "read", BLKID_DEBUG_READ, "cache parsing" }, + { "save", BLKID_DEBUG_SAVE, "cache writing" }, + { "tag", BLKID_DEBUG_TAG, "tags utils" }, + { NULL, 0, NULL } +}; + +/** + * blkid_init_debug: + * @mask: debug mask (0xffff to enable full debugging) + * + * If the @mask is not specified then this function reads + * LIBBLKID_DEBUG environment variable to get the mask. + * + * Already initialized debugging stuff cannot be changed. It does not + * have effect to call this function twice. + */ +void blkid_init_debug(int mask) +{ + if (libblkid_debug_mask) + return; + + __UL_INIT_DEBUG_FROM_ENV(libblkid, BLKID_DEBUG_, mask, LIBBLKID_DEBUG); + + if (libblkid_debug_mask != BLKID_DEBUG_INIT + && libblkid_debug_mask != (BLKID_DEBUG_HELP|BLKID_DEBUG_INIT)) { + const char *ver = NULL; + const char *date = NULL; + + blkid_get_library_version(&ver, &date); + DBG(INIT, ul_debug("library debug mask: 0x%04x", libblkid_debug_mask)); + DBG(INIT, ul_debug("library version: %s [%s]", ver, date)); + + } + ON_DBG(HELP, ul_debug_print_masks("LIBBLKID_DEBUG", + UL_DEBUG_MASKNAMES(libblkid))); +} + +static void __attribute__ ((constructor)) blkid_init_default_debug(void) +{ + blkid_init_debug(0); +} diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym new file mode 100644 index 0000000..775b024 --- /dev/null +++ b/libblkid/src/libblkid.sym @@ -0,0 +1,189 @@ +/* + * The symbol versioning ensures that a new application requiring symbol 'foo' + * can't run with old library.so not providing 'foo' - the global SONAME + * version info can't enforce this since we never change the SONAME. + * + * The original libblkid from e2fsprogs (<=1.41.4) does not to use + * symbol versioning -- all the original symbols are in BLKID_1.0 now. + * + * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com> + */ +BLKID_1.0 { +global: + blkid_dev_devname; + blkid_dev_has_tag; + blkid_dev_iterate_begin; + blkid_dev_iterate_end; + blkid_dev_next; + blkid_devno_to_devname; + blkid_dev_set_search; + blkid_find_dev_with_tag; + blkid_gc_cache; + blkid_get_cache; + blkid_get_dev; + blkid_get_devname; + blkid_get_dev_size; + blkid_get_library_version; + blkid_get_tag_value; + blkid_known_fstype; + blkid_parse_tag_string; + blkid_parse_version_string; + blkid_probe_all; + blkid_probe_all_new; + blkid_put_cache; + blkid_tag_iterate_begin; + blkid_tag_iterate_end; + blkid_tag_next; + blkid_verify; +local: + *; +}; + + +/* + * symbols since util-linux 2.15 + */ +BLKID_2.15 { +global: + blkid_do_probe; + blkid_do_safeprobe; + blkid_encode_string; + blkid_evaluate_tag; + blkid_free_probe; + blkid_new_probe; + blkid_probe_filter_types; + blkid_probe_filter_usage; + blkid_probe_get_value; + blkid_probe_has_value; + blkid_probe_invert_filter; + blkid_probe_lookup_value; + blkid_probe_numof_values; + blkid_probe_reset_filter; + blkid_probe_set_device; + blkid_probe_set_request; + blkid_reset_probe; + blkid_safe_string; + blkid_send_uevent; +} BLKID_1.0; + +/* + * symbols since util-linux 2.17 + */ +BLKID_2.17 { +global: + blkid_devno_to_wholedisk; + blkid_do_fullprobe; + blkid_known_pttype; + blkid_new_probe_from_filename; + blkid_partition_get_name; + blkid_partition_get_partno; + blkid_partition_get_size; + blkid_partition_get_start; + blkid_partition_get_table; + blkid_partition_get_type; + blkid_partition_get_type_string; + blkid_partition_get_uuid; + blkid_partition_is_extended; + blkid_partition_is_logical; + blkid_partition_is_primary; + blkid_partlist_get_partition; + blkid_partlist_numof_partitions; + blkid_parttable_get_offset; + blkid_parttable_get_parent; + blkid_parttable_get_type; + blkid_probe_enable_partitions; + blkid_probe_enable_superblocks; + blkid_probe_enable_topology; + blkid_probe_filter_partitions_type; + blkid_probe_filter_superblocks_type; + blkid_probe_filter_superblocks_usage; + blkid_probe_get_devno; + blkid_probe_get_partitions; + blkid_probe_get_sectorsize; + blkid_probe_get_sectors; + blkid_probe_get_size; + blkid_probe_get_topology; + blkid_probe_invert_partitions_filter; + blkid_probe_invert_superblocks_filter; + blkid_probe_reset_partitions_filter; + blkid_probe_reset_superblocks_filter; + blkid_probe_set_partitions_flags; + blkid_probe_set_superblocks_flags; + blkid_topology_get_alignment_offset; + blkid_topology_get_logical_sector_size; + blkid_topology_get_minimum_io_size; + blkid_topology_get_optimal_io_size; + blkid_topology_get_physical_sector_size; +} BLKID_2.15; + +/* + * symbols since util-linux 2.18 + */ +BLKID_2.18 { +global: + blkid_partition_get_flags; + blkid_partlist_devno_to_partition; + blkid_partlist_get_table; + blkid_probe_all_removable; + blkid_probe_get_fd; + blkid_probe_get_offset; + blkid_probe_get_wholedisk_devno; + blkid_probe_is_wholedisk; +} BLKID_2.17; + +/* + * symbols since util-linux 2.20 + */ +BLKID_2.20 { +global: + blkid_evaluate_spec; + blkid_superblocks_get_name; +} BLKID_2.18; + +/* + * symbols since util-linux 2.21 + */ +BLKID_2.21 { +global: + blkid_do_wipe; +} BLKID_2.20; + +/* + * symbols since util-linux 2.23 + */ +BLKID_2.23 { +global: + blkid_probe_step_back; + blkid_parttable_get_id; + blkid_init_debug; +} BLKID_2.21; + +/* + * symbols since util-linux 2.25 + */ +BLKID_2.25 { + blkid_partlist_get_partition_by_partno; +} BLKID_2.23; + +BLKID_2.30 { + blkid_probe_set_sectorsize; + blkid_partitions_get_name; +} BLKID_2.25; + +BLKID_2_31 { + blkid_probe_reset_buffers; + blkid_probe_hide_range; +} BLKID_2.30; + +BLKID_2_36 { + blkid_topology_get_dax; +} BLKID_2_31; + +BLKID_2_37 { + blkid_probe_set_hint; + blkid_probe_reset_hints; +} BLKID_2_36; + +BLKID_2_39 { + blkid_topology_get_diskseq; +} BLKID_2_37; diff --git a/libblkid/src/partitions/aix.c b/libblkid/src/partitions/aix.c new file mode 100644 index 0000000..03a311a --- /dev/null +++ b/libblkid/src/partitions/aix.c @@ -0,0 +1,57 @@ +/* + * aix partitions + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "aix.h" + +static int probe_aix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + blkid_partlist ls; + blkid_parttable tab; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + return BLKID_PROBE_NONE; + + tab = blkid_partlist_new_parttable(ls, "aix", 0); + if (!tab) + return -ENOMEM; + + return BLKID_PROBE_OK; +} + +/* + * We know nothing about AIX on-disk structures. Everything what we know is the + * magic number at begin of the disk. + * + * Note, Linux kernel is trying to be smart and AIX signature is ignored when + * there is a valid DOS partitions table. We don't support such behavior. All + * fdisk-like programs has to properly wipe the fist sector. Everything other + * is a bug. + */ +const struct blkid_idinfo aix_pt_idinfo = +{ + .name = "aix", + .probefunc = probe_aix_pt, + .magics = + { + { .magic = BLKID_AIX_MAGIC_STRING, .len = BLKID_AIX_MAGIC_STRLEN }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/aix.h b/libblkid/src/partitions/aix.h new file mode 100644 index 0000000..f767c5a --- /dev/null +++ b/libblkid/src/partitions/aix.h @@ -0,0 +1,7 @@ +#ifndef BLKID_PARTITIONS_AIX_H +#define BLKID_PARTITIONS_AIX_H + +#define BLKID_AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1" +#define BLKID_AIX_MAGIC_STRLEN (sizeof(BLKID_AIX_MAGIC_STRING) - 1) + +#endif diff --git a/libblkid/src/partitions/atari.c b/libblkid/src/partitions/atari.c new file mode 100644 index 0000000..314f047 --- /dev/null +++ b/libblkid/src/partitions/atari.c @@ -0,0 +1,312 @@ +/* + * atari partitions parsing code + * + * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Based on Linux kernel implementation and atari-fdisk + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +struct atari_part_def { + /* + * flags: + * 0 (LSB): active + * 1-6: (reserved) + * 7 (MSB): bootable + */ + unsigned char flags; + char id[3]; + uint32_t start; + uint32_t size; +} __attribute__((packed)); + +struct atari_rootsector { + char unused0[0x156]; /* boot code */ + struct atari_part_def icd_part[8]; /* ICD partition entries */ + char unused1[0xc]; + uint32_t hd_size; + struct atari_part_def part[4]; /* primary partition entries */ + uint32_t bsl_start; /* bad sector list start */ + uint32_t bsl_len; /* bad sector list length */ + uint16_t checksum; +} __attribute__((packed)); + + +/* + * Generated using linux kernel ctype.{c,h} + * + * Since kernel uses isalnum() to detect whether it is Atari PT, we need same + * definition of alnum character to be consistent with kernel. + */ +static const unsigned char _linux_isalnum[] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1 +}; + +static int linux_isalnum(unsigned char c) { + return _linux_isalnum[c]; +} + +#define isalnum linux_isalnum + +#define IS_ACTIVE(partdef) ((partdef).flags & 1) + +static int is_valid_dimension(uint32_t start, uint32_t size, uint32_t maxoff) +{ + uint64_t end = start + size; + + return end >= start + && 0 < start && start <= maxoff + && 0 < size && size <= maxoff + && 0 < end && end <= maxoff; +} + +static int is_valid_partition(struct atari_part_def *part, uint32_t maxoff) +{ + uint32_t start = be32_to_cpu(part->start), + size = be32_to_cpu(part->size); + + return (part->flags & 1) + && isalnum(part->id[0]) + && isalnum(part->id[1]) + && isalnum(part->id[2]) + && is_valid_dimension(start, size, maxoff); +} + +static int is_id_common(char *id) +{ + const char *ids[] = {"GEM", "BGM", "LNX", "SWP", "RAW", }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ids); i++) { + if (!memcmp(ids[i], id, 3)) + return 1; + } + return 0; +} + +static int parse_partition(blkid_partlist ls, blkid_parttable tab, + struct atari_part_def *part, uint32_t offset) +{ + blkid_partition par; + uint32_t start; + uint32_t size; + + start = be32_to_cpu(part->start) + offset; + size = be32_to_cpu(part->size); + + if (blkid_partlist_get_partition_by_start(ls, start)) { + /* Don't increment partno for extended parts */ + if (!offset) + blkid_partlist_increment_partno(ls); + return 0; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type_string(par, (unsigned char *) part->id, + sizeof(part->id)); + return 1; +} + +/* + * \return 1: OK, 0: bad format or -errno + */ +static int parse_extended(blkid_probe pr, blkid_partlist ls, + blkid_parttable tab, struct atari_part_def *part) +{ + uint32_t x0start, xstart; + unsigned ct = 0, i = 0; + int rc; + + x0start = xstart = be32_to_cpu(part->start); + while (1) { + struct atari_rootsector *xrs; + + if (++ct > 100) + break; + + xrs = (struct atari_rootsector *) blkid_probe_get_sector(pr, xstart); + if (!xrs) { + if (errno) + return -errno; + return 0; + } + + /* + * There must be data partition followed by reference to next + * XGM or inactive entry. + */ + for (i=0; ; i++) { + if (i >= ARRAY_SIZE(xrs->part) - 1) + return 0; + if (IS_ACTIVE(xrs->part[i])) + break; + } + + if (!memcmp(xrs->part[i].id, "XGM", 3)) + return 0; + + rc = parse_partition(ls, tab, &xrs->part[i], xstart); + if (rc <= 0) + return rc; + + if (!IS_ACTIVE(xrs->part[i+1])) + break; + + if (memcmp(xrs->part[i+1].id, "XGM", 3) != 0) + return 0; + + xstart = x0start + be32_to_cpu(xrs->part[i+1].start); + } + + return 1; +} + +static int probe_atari_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct atari_rootsector *rs; + + blkid_parttable tab = NULL; + blkid_partlist ls; + + unsigned i; + int has_xgm = 0; + int rc = 0; + uint32_t rssize; /* size in sectors from root sector */ + uint64_t size; /* size in sectors from system */ + + /* Atari partition is not defined for other sector sizes */ + if (blkid_probe_get_sectorsize(pr) != 512) + goto nothing; + + size = blkid_probe_get_size(pr) / 512; + + /* Atari is not well defined to support large disks */ + if (size > INT32_MAX) + goto nothing; + + /* read root sector */ + rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0); + if (!rs) { + if (errno) + return -errno; + goto nothing; + } + + rssize = be32_to_cpu(rs->hd_size); + + /* check number of sectors stored in the root sector */ + if (rssize < 2 || rssize > size) + goto nothing; + + /* check list of bad blocks */ + if ((rs->bsl_start || rs->bsl_len) + && !is_valid_dimension(be32_to_cpu(rs->bsl_start), + be32_to_cpu(rs->bsl_len), + rssize)) + goto nothing; + + /* + * At least one valid partition required + */ + for (i = 0; i < 4; i++) { + if (is_valid_partition(&rs->part[i], rssize)) { + if (blkid_probe_set_magic(pr, + offsetof(struct atari_rootsector, part[i]), + sizeof(rs->part[i].flags) + sizeof(rs->part[i].id), + (unsigned char *) &rs->part[i])) + goto err; + break; + } + } + + if (i == 4) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "atari", 0); + if (!tab) + goto err; + + for (i = 0; i < ARRAY_SIZE(rs->part); i++) { + struct atari_part_def *p = &rs->part[i]; + + if (!IS_ACTIVE(*p)) { + blkid_partlist_increment_partno(ls); + continue; + } + if (!memcmp(p->id, "XGM", 3)) { + has_xgm = 1; + rc = parse_extended(pr, ls, tab, p); + } else { + rc = parse_partition(ls, tab, p, 0); + } + if (rc < 0) + return rc; + } + + /* if there are no XGM partitions, we can try ICD format */ + /* if first ICD partition ID is not valid, assume no ICD format */ + if (!has_xgm && is_id_common(rs->icd_part[0].id)) { + for (i = 0; i < ARRAY_SIZE(rs->icd_part); i++) { + struct atari_part_def *p = &rs->icd_part[i]; + + if (!IS_ACTIVE(*p) || !is_id_common(p->id)) { + blkid_partlist_increment_partno(ls); + continue; + } + + rc = parse_partition(ls, tab, p, 0); + if (rc < 0) + return rc; + } + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo atari_pt_idinfo = +{ + .name = "atari", + .probefunc = probe_atari_pt, + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c new file mode 100644 index 0000000..ba12019 --- /dev/null +++ b/libblkid/src/partitions/bsd.c @@ -0,0 +1,205 @@ +/* + * BSD/OSF partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Inspired by fdisk, partx, Linux kernel, libparted and openbsd header files. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "pt-bsd.h" + +/* Returns 'blkid_idmag' in 512-sectors */ +#define BLKID_MAG_SECTOR(_mag) (((_mag)->kboff / 2) + ((_mag)->sboff >> 9)) + +/* Returns 'blkid_idmag' in bytes */ +#define BLKID_MAG_OFFSET(_mag) ((_mag)->kboff << 10) + ((_mag)->sboff) + +/* Returns 'blkid_idmag' offset in bytes within the last sector */ +#define BLKID_MAG_LASTOFFSET(_mag) \ + (BLKID_MAG_OFFSET(_mag) - (BLKID_MAG_SECTOR(_mag) << 9)) + +static uint16_t bsd_checksum(const struct bsd_disklabel *l) +{ + uint16_t v, csum = 0; + const char *end = (const char *) (l + 1); + + for (const char *c = (const char *) l; c < end; c += sizeof(uint16_t)) { + memcpy(&v, c, sizeof(v)); + csum ^= v; + } + return csum ^ le16_to_cpu(l->d_checksum); +} + +static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bsd_disklabel *l; + struct bsd_partition *p; + const char *name = "bsd" ; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i, nparts = BSD_MAXPARTITIONS; + unsigned char *data; + int rc = BLKID_PROBE_NONE; + uint32_t abs_offset = 0; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return rc; + + data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag)); + if (!data) { + if (errno) + rc = -errno; + goto nothing; + } + + l = (struct bsd_disklabel *) (data + BLKID_MAG_LASTOFFSET(mag)); + + if (!blkid_probe_verify_csum(pr, bsd_checksum(l), le16_to_cpu(l->d_checksum))) { + rc = BLKID_PROBE_NONE; + goto nothing; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* try to determine the real type of BSD system according to + * (parental) primary partition */ + parent = blkid_partlist_get_parent(ls); + if (parent) { + switch(blkid_partition_get_type(parent)) { + case MBR_FREEBSD_PARTITION: + name = "freebsd"; + abs_offset = blkid_partition_get_start(parent); + break; + case MBR_NETBSD_PARTITION: + name = "netbsd"; + break; + case MBR_OPENBSD_PARTITION: + name = "openbsd"; + break; + default: + DBG(LOWPROBE, ul_debug( + "WARNING: BSD label detected on unknown (0x%x) " + "primary partition", + blkid_partition_get_type(parent))); + break; + } + } + + tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag)); + if (!tab) { + rc = -ENOMEM; + goto nothing; + } + + if (le16_to_cpu(l->d_npartitions) < BSD_MAXPARTITIONS) + nparts = le16_to_cpu(l->d_npartitions); + + else if (le16_to_cpu(l->d_npartitions) > BSD_MAXPARTITIONS) + DBG(LOWPROBE, ul_debug( + "WARNING: ignore %d more BSD partitions", + le16_to_cpu(l->d_npartitions) - BSD_MAXPARTITIONS)); + + for (i = 0, p = l->d_partitions; i < nparts; i++, p++) { + blkid_partition par; + uint32_t start, size; + + if (p->p_fstype == BSD_FS_UNUSED) + continue; + + start = le32_to_cpu(p->p_offset); + size = le32_to_cpu(p->p_size); + + /* FreeBSD since version 10 uses relative offsets. We can use + * 3rd partition (special wholedisk partition) to detect this + * situation. + */ + if (abs_offset && nparts >= 3 + && le32_to_cpu(l->d_partitions[2].p_offset) == 0) + start += abs_offset; + + if (parent && blkid_partition_get_start(parent) == start + && blkid_partition_get_size(parent) == size) { + DBG(LOWPROBE, ul_debug( + "WARNING: BSD partition (%d) same like parent, " + "ignore", i)); + continue; + } + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: BSD partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) { + rc = -ENOMEM; + goto nothing; + } + + blkid_partition_set_type(par, p->p_fstype); + } + + return BLKID_PROBE_OK; + +nothing: + return rc; +} + + +/* + * All BSD variants use the same magic string (little-endian), + * and the same disklabel. + * + * The difference between {Free,Open,...}BSD is in the (parental) + * primary partition type. + * + * See also: http://en.wikipedia.org/wiki/BSD_disklabel + * + * The location of BSD disk label is architecture specific and in defined by + * LABELSECTOR and LABELOFFSET macros in the disklabel.h file. The location + * also depends on BSD variant, FreeBSD uses only one location, NetBSD and + * OpenBSD are more creative... + * + * The basic overview: + * + * arch | LABELSECTOR | LABELOFFSET + * ------------------------+-------------+------------ + * amd64 arm hppa hppa64 | | + * i386, macppc, mvmeppc | 1 | 0 + * sgi, aviion, sh, socppc | | + * ------------------------+-------------+------------ + * alpha luna88k mac68k | 0 | 64 + * sparc(OpenBSD) vax | | + * ------------------------+-------------+------------ + * sparc64 sparc(NetBSD) | 0 | 128 + * ------------------------+-------------+------------ + * + * ...and more (see http://fxr.watson.org/fxr/ident?v=NETBSD;i=LABELSECTOR) + * + */ +const struct blkid_idinfo bsd_pt_idinfo = +{ + .name = "bsd", + .probefunc = probe_bsd_pt, + .magics = + { + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 512 }, + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 64 }, + { .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 128 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c new file mode 100644 index 0000000..5c7718c --- /dev/null +++ b/libblkid/src/partitions/dos.c @@ -0,0 +1,375 @@ +/* + * MS-DOS partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Inspired by fdisk, partx, Linux kernel and libparted. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "superblocks/superblocks.h" +#include "aix.h" + +/* see superblocks/vfat.c */ +extern int blkid_probe_is_vfat(blkid_probe pr); +/* see superblocks/exfat.c */ +extern int blkid_probe_is_exfat(blkid_probe pr); + +static const struct dos_subtypes { + unsigned char type; + const struct blkid_idinfo *id; +} dos_nested[] = { + { MBR_FREEBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_NETBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_OPENBSD_PARTITION, &bsd_pt_idinfo }, + { MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo }, + { MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo }, + { MBR_MINIX_PARTITION, &minix_pt_idinfo } +}; + +static inline int is_extended(struct dos_partition *p) +{ + return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION || + p->sys_ind == MBR_W95_EXTENDED_PARTITION || + p->sys_ind == MBR_LINUX_EXTENDED_PARTITION); +} + +static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, + uint32_t ex_start, uint32_t ex_size, int ssf) +{ + blkid_partlist ls = blkid_probe_get_partlist(pr); + uint32_t cur_start = ex_start, cur_size = ex_size; + unsigned char *data; + int ct_nodata = 0; /* count ext.partitions without data partitions */ + int i; + + DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf)); + if (ex_start == 0) { + DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore")); + return 0; + } + + while (1) { + struct dos_partition *p, *p0; + uint32_t start = 0, size; + + if (++ct_nodata > 100) + return BLKID_PROBE_OK; + data = blkid_probe_get_sector(pr, cur_start); + if (!data) { + if (errno) + return -errno; + goto leave; /* malformed partition? */ + } + + if (!mbr_is_valid_magic(data)) + goto leave; + + p0 = mbr_get_partition(data, 0); + + /* Usually, the first entry is the real data partition, + * the 2nd entry is the next extended partition, or empty, + * and the 3rd and 4th entries are unused. + * However, DRDOS sometimes has the extended partition as + * the first entry (when the data partition is empty), + * and OS/2 seems to use all four entries. + * -- Linux kernel fs/partitions/dos.c + * + * See also http://en.wikipedia.org/wiki/Extended_boot_record + */ + + /* Parse data partition */ + for (p = p0, i = 0; i < 4; i++, p++) { + uint32_t abs_start; + blkid_partition par; + + /* the start is relative to the parental ext.partition */ + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + abs_start = cur_start + start; /* absolute start */ + + if (!size || is_extended(p)) + continue; + if (i >= 2) { + /* extra checks to detect real data on + * 3rd and 4th entries */ + if (start + size > cur_size) + continue; + if (abs_start < ex_start) + continue; + if (abs_start + size > ex_start + ex_size) + continue; + } + + /* Avoid recursive non-empty links, see ct_nodata counter */ + if (blkid_partlist_get_partition_by_start(ls, abs_start)) { + DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore", + i + 1, abs_start)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, abs_start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + blkid_partition_gen_uuid(par); + ct_nodata = 0; + } + /* The first nested ext.partition should be a link to the next + * logical partition. Everything other (recursive ext.partitions) + * is junk. + */ + for (p = p0, i = 0; i < 4; i++, p++) { + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (size && is_extended(p)) { + if (start == 0) + DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1)); + else + break; + } + } + if (i == 4) + goto leave; + + cur_start = ex_start + start; + cur_size = size; + } +leave: + return BLKID_PROBE_OK; +} + +static inline int is_lvm(blkid_probe pr) +{ + struct blkid_prval *v = __blkid_probe_lookup_value(pr, "TYPE"); + + return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0); +} + +static inline int is_empty_mbr(unsigned char *mbr) +{ + struct dos_partition *p = mbr_get_partition(mbr, 0); + int i, nparts = 0; + + for (i = 0; i < 4; i++) { + if (dos_partition_get_size(p) > 0) + nparts++; + p++; + } + + return nparts == 0; +} + +static int probe_dos_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int i; + int ssf; + blkid_parttable tab = NULL; + blkid_partlist ls; + struct dos_partition *p0, *p; + unsigned char *data; + uint32_t start, size, id; + char idstr[UUID_STR_LEN]; + + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + /* ignore disks with AIX magic number -- for more details see aix.c */ + if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0) + goto nothing; + + p0 = mbr_get_partition(data, 0); + + /* + * Reject PT where boot indicator is not 0 or 0x80. + */ + for (p = p0, i = 0; i < 4; i++, p++) + if (p->boot_ind != 0 && p->boot_ind != 0x80) { + DBG(LOWPROBE, ul_debug("missing boot indicator -- ignore")); + goto nothing; + } + + /* + * GPT uses valid MBR + */ + for (p = p0, i = 0; i < 4; i++, p++) { + if (p->sys_ind == MBR_GPT_PARTITION) { + DBG(LOWPROBE, ul_debug("probably GPT -- ignore")); + goto nothing; + } + } + + /* + * Now that the 55aa signature is present, this is probably + * either the boot sector of a FAT filesystem or a DOS-type + * partition table. + */ + if (blkid_probe_is_vfat(pr) == 1 || blkid_probe_is_exfat(pr) == 1) { + DBG(LOWPROBE, ul_debug("probably FAT -- ignore")); + goto nothing; + } + + /* Another false positive is NTFS */ + if (blkid_probe_is_ntfs(pr) == 1) { + DBG(LOWPROBE, ul_debug("probably NTFS -- ignore")); + goto nothing; + } + + /* + * Ugly exception, if the device contains a valid LVM physical volume + * and empty MBR (=no partition defined) then it's LVM and MBR should + * be ignored. Crazy people use it to boot from LVM devices. + */ + if (is_lvm(pr) && is_empty_mbr(data)) { + DBG(LOWPROBE, ul_debug("empty MBR on LVM device -- ignore")); + goto nothing; + } + + blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET); + + id = mbr_get_id(data); + if (id) + snprintf(idstr, sizeof(idstr), "%08x", id); + + /* + * Well, all checks pass, it's MS-DOS partition table + */ + if (blkid_partitions_need_typeonly(pr)) { + /* Non-binary interface -- caller does not ask for details + * about partitions, just set generic variables only. */ + if (id) + blkid_partitions_strcpy_ptuuid(pr, idstr); + return 0; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* sector size factor (the start and size are in the real sectors, but + * we need to convert all sizes to 512 logical sectors + */ + ssf = blkid_probe_get_sectorsize(pr) / 512; + + /* allocate a new partition table */ + tab = blkid_partlist_new_parttable(ls, "dos", MBR_PT_OFFSET); + if (!tab) + return -ENOMEM; + + if (id) + blkid_parttable_set_id(tab, (unsigned char *) idstr); + + /* Parse primary partitions */ + for (p = p0, i = 0; i < 4; i++, p++) { + blkid_partition par; + + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (!size) { + /* Linux kernel ignores empty partitions, but partno for + * the empty primary partitions is not reused */ + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + blkid_partition_gen_uuid(par); + } + + /* Linux uses partition numbers greater than 4 + * for all logical partition and all nested partition tables (bsd, ..) + */ + blkid_partlist_set_partno(ls, 5); + + /* Parse logical partitions */ + for (p = p0, i = 0; i < 4; i++, p++) { + start = dos_partition_get_start(p) * ssf; + size = dos_partition_get_size(p) * ssf; + + if (!size) + continue; + if (is_extended(p) && + parse_dos_extended(pr, tab, start, size, ssf) == -1) + goto nothing; + } + + /* Parse subtypes (nested partitions) on large disks */ + if (!blkid_probe_is_tiny(pr)) { + int nparts = blkid_partlist_numof_partitions(ls); + + DBG(LOWPROBE, ul_debug("checking for subtypes")); + + for (i = 0; i < nparts; i++) { + size_t n; + int type; + blkid_partition pa = blkid_partlist_get_partition(ls, i); + + if (pa == NULL + || blkid_partition_get_size(pa) == 0 + || blkid_partition_is_extended(pa) + || blkid_partition_is_logical(pa)) + continue; + + type = blkid_partition_get_type(pa); + + for (n = 0; n < ARRAY_SIZE(dos_nested); n++) { + int rc; + + if (dos_nested[n].type != type) + continue; + + rc = blkid_partitions_do_subprobe(pr, pa, + dos_nested[n].id); + if (rc < 0) + return rc; + break; + } + } + } + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +} + + +const struct blkid_idinfo dos_pt_idinfo = +{ + .name = "dos", + .probefunc = probe_dos_pt, + .magics = + { + /* DOS master boot sector: + * + * 0 | Code Area + * 440 | Optional Disk signature + * 446 | Partition table + * 510 | 0x55 + * 511 | 0xAA + */ + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c new file mode 100644 index 0000000..89e7bb6 --- /dev/null +++ b/libblkid/src/partitions/gpt.c @@ -0,0 +1,473 @@ +/* + * EFI GPT partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * This code is not copy & past from any other implementation. + * + * For more information about GPT start your study at: + * http://en.wikipedia.org/wiki/GUID_Partition_Table + * http://technet.microsoft.com/en-us/library/cc739412(WS.10).aspx + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <limits.h> +#include <inttypes.h> + +#include "partitions.h" +#include "crc32.h" + +#define GPT_PRIMARY_LBA 1 + +/* Signature - “EFI PART” */ +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_SIGNATURE_STR "EFI PART" + +/* basic types */ +typedef uint16_t efi_char16_t; + +/* UUID */ +typedef struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi; + uint8_t clock_seq_low; + uint8_t node[6]; +} efi_guid_t; + + +#define GPT_UNUSED_ENTRY_GUID \ + ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}) +struct gpt_header { + uint64_t signature; /* "EFI PART" */ + uint32_t revision; + uint32_t header_size; /* usually 92 bytes */ + uint32_t header_crc32; /* checksum of header with this + * field zeroed during calculation */ + uint32_t reserved1; + + uint64_t my_lba; /* location of this header copy */ + uint64_t alternate_lba; /* location of the other header copy */ + uint64_t first_usable_lba; /* first usable LBA for partitions */ + uint64_t last_usable_lba; /* last usable LBA for partitions */ + + efi_guid_t disk_guid; /* disk UUID */ + + uint64_t partition_entries_lba; /* always 2 in primary header copy */ + uint32_t num_partition_entries; + uint32_t sizeof_partition_entry; + uint32_t partition_entry_array_crc32; + + /* + * The rest of the block is reserved by UEFI and must be zero. EFI + * standard handles this by: + * + * uint8_t reserved2[ BLKSSZGET - 92 ]; + * + * This definition is useless in practice. It is necessary to read + * whole block from the device rather than sizeof(struct gpt_header) + * only. + */ +} __attribute__ ((packed)); + +/*** not used +struct gpt_entry_attributes { + uint64_t required_to_function:1; + uint64_t reserved:47; + uint64_t type_guid_specific:16; +} __attribute__ ((packed)); +***/ + +struct gpt_entry { + efi_guid_t partition_type_guid; /* type UUID */ + efi_guid_t unique_partition_guid; /* partition UUID */ + uint64_t starting_lba; + uint64_t ending_lba; + + /*struct gpt_entry_attributes attributes;*/ + + uint64_t attributes; + + efi_char16_t partition_name[72 / sizeof(efi_char16_t)]; /* UTF-16LE string*/ +} __attribute__ ((packed)); + + +/* + * EFI uses crc32 with ~0 seed and xor's with ~0 at the end. + */ +static inline uint32_t count_crc32(const unsigned char *buf, size_t len, + size_t exclude_off, size_t exclude_len) +{ + return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len) ^ ~0L); +} + +static inline unsigned char *get_lba_buffer(blkid_probe pr, + uint64_t lba, size_t bytes) +{ + return blkid_probe_get_buffer(pr, + blkid_probe_get_sectorsize(pr) * lba, bytes); +} + +static inline int guidcmp(efi_guid_t left, efi_guid_t right) +{ + return memcmp(&left, &right, sizeof (efi_guid_t)); +} + +/* + * UUID is traditionally 16 byte big-endian array, except Intel EFI + * specification where the UUID is a structure of little-endian fields. + */ +static void swap_efi_guid(efi_guid_t *uid) +{ + uid->time_low = swab32(uid->time_low); + uid->time_mid = swab16(uid->time_mid); + uid->time_hi_and_version = swab16(uid->time_hi_and_version); +} + +static int last_lba(blkid_probe pr, uint64_t *lba) +{ + uint64_t sz = blkid_probe_get_size(pr); + unsigned int ssz = blkid_probe_get_sectorsize(pr); + + if (sz < ssz) + return -1; + + *lba = (sz / ssz) - 1ULL; + return 0; +} + +/* + * Protective (legacy) MBR. + * + * This MBR contains standard DOS partition table with a single partition, type + * of 0xEE. The partition usually encompassing the entire GPT drive - or 2TiB + * for large disks. + * + * Note that Apple uses GPT/MBR hybrid disks, where the DOS partition table is + * synchronized with GPT. This synchronization has many restriction of course + * (due DOS PT limitations). + * + * Note that the PMBR detection is optional (enabled by default) and could be + * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_partitions_set_flags()). + */ +static int is_pmbr_valid(blkid_probe pr, int *has) +{ + int flags = blkid_partitions_get_flags(pr); + unsigned char *data; + struct dos_partition *p; + int i; + + if (has) + *has = 0; + else if (flags & BLKID_PARTS_FORCE_GPT) + return 1; /* skip PMBR check */ + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto failed; + } + + if (!mbr_is_valid_magic(data)) + goto failed; + + for (i = 0, p = mbr_get_partition(data, 0); i < 4; i++, p++) { + if (p->sys_ind == MBR_GPT_PARTITION) { + DBG(LOWPROBE, ul_debug(" #%d valid PMBR partition", i + 1)); + goto ok; + } + } +failed: + return 0; +ok: + if (has) + *has = 1; + return 1; +} + +/* + * Reads GPT header to @hdr and returns a pointer to @hdr or NULL in case of + * error. The function also returns GPT entries in @ents. + * + * Note, this function does not allocate any memory. The GPT header has fixed + * size so we use stack, and @ents returns memory from libblkid buffer (so the + * next blkid_probe_get_buffer() will overwrite this buffer). + * + * This function checks validity of header and entries array. A corrupted + * header is not returned. + */ +static struct gpt_header *get_gpt_header( + blkid_probe pr, struct gpt_header *hdr, + struct gpt_entry **ents, uint64_t lba, + uint64_t lastlba) +{ + struct gpt_header *h; + uint32_t crc; + uint64_t lu, fu; + uint64_t esz; + uint32_t hsz, ssz; + + ssz = blkid_probe_get_sectorsize(pr); + + DBG(LOWPROBE, ul_debug(" checking for GPT header at %"PRIu64, lba)); + + /* whole sector is allocated for GPT header */ + h = (struct gpt_header *) get_lba_buffer(pr, lba, ssz); + if (!h) + return NULL; + + if (le64_to_cpu(h->signature) != GPT_HEADER_SIGNATURE) + return NULL; + + hsz = le32_to_cpu(h->header_size); + + /* EFI: The HeaderSize must be greater than 92 and must be less + * than or equal to the logical block size. + */ + if (hsz > ssz || hsz < sizeof(*h)) + return NULL; + + /* Header has to be verified when header_crc32 is zero */ + crc = count_crc32((unsigned char *) h, hsz, + offsetof(struct gpt_header, header_crc32), + sizeof(h->header_crc32)); + + if (!blkid_probe_verify_csum(pr, crc, le32_to_cpu(h->header_crc32))) { + DBG(LOWPROBE, ul_debug("GPT header corrupted")); + return NULL; + } + + /* Valid header has to be at MyLBA */ + if (le64_to_cpu(h->my_lba) != lba) { + DBG(LOWPROBE, ul_debug( + "GPT->MyLBA mismatch with real position")); + return NULL; + } + + fu = le64_to_cpu(h->first_usable_lba); + lu = le64_to_cpu(h->last_usable_lba); + + /* Check if First and Last usable LBA makes sense */ + if (lu < fu || fu > lastlba || lu > lastlba) { + DBG(LOWPROBE, ul_debug( + "GPT->{First,Last}UsableLBA out of range")); + return NULL; + } + + /* The header has to be outside usable range */ + if (fu < lba && lba < lu) { + DBG(LOWPROBE, ul_debug("GPT header is inside usable area")); + return NULL; + } + + /* Size of blocks with GPT entries */ + esz = (uint64_t)le32_to_cpu(h->num_partition_entries) * + le32_to_cpu(h->sizeof_partition_entry); + + if (esz == 0 || esz >= UINT32_MAX || + le32_to_cpu(h->sizeof_partition_entry) != sizeof(struct gpt_entry)) { + DBG(LOWPROBE, ul_debug("GPT entries undefined")); + return NULL; + } + + /* The header seems valid, save it + * (we don't care about zeros in hdr->reserved2 area) */ + memcpy(hdr, h, sizeof(*h)); + h = hdr; + + /* Read GPT entries */ + *ents = (struct gpt_entry *) get_lba_buffer(pr, + le64_to_cpu(h->partition_entries_lba), esz); + if (!*ents) { + DBG(LOWPROBE, ul_debug("GPT entries unreadable")); + return NULL; + } + + /* Validate entries */ + crc = count_crc32((unsigned char *) *ents, esz, 0, 0); + if (crc != le32_to_cpu(h->partition_entry_array_crc32)) { + DBG(LOWPROBE, ul_debug("GPT entries corrupted")); + return NULL; + } + + return h; +} + +static int probe_gpt_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t lastlba = 0, lba; + struct gpt_header hdr, *h; + struct gpt_entry *e; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint64_t fu, lu; + uint32_t ssf, i; + efi_guid_t guid; + int ret; + + if (last_lba(pr, &lastlba)) + goto nothing; + + ret = is_pmbr_valid(pr, NULL); + if (ret < 0) + return ret; + if (ret == 0) + goto nothing; + + errno = 0; + h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba); + if (!h && !errno) + h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba); + + if (!h) { + if (errno) + return -errno; + goto nothing; + } + + blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8); + + if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba, + sizeof(GPT_HEADER_SIGNATURE_STR) - 1, + (unsigned char *) GPT_HEADER_SIGNATURE_STR)) + goto err; + + guid = h->disk_guid; + swap_efi_guid(&guid); + + if (blkid_partitions_need_typeonly(pr)) { + /* Non-binary interface -- caller does not ask for details + * about partitions, just set generic variables only. */ + blkid_partitions_set_ptuuid(pr, (unsigned char *) &guid); + return BLKID_PROBE_OK; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "gpt", + blkid_probe_get_sectorsize(pr) * lba); + if (!tab) + goto err; + + blkid_parttable_set_uuid(tab, (const unsigned char *) &guid); + + ssf = blkid_probe_get_sectorsize(pr) / 512; + + fu = le64_to_cpu(h->first_usable_lba); + lu = le64_to_cpu(h->last_usable_lba); + + for (i = 0; i < le32_to_cpu(h->num_partition_entries); i++, e++) { + + blkid_partition par; + uint64_t start = le64_to_cpu(e->starting_lba); + uint64_t size = le64_to_cpu(e->ending_lba) - + le64_to_cpu(e->starting_lba) + 1ULL; + + /* 00000000-0000-0000-0000-000000000000 entry */ + if (!guidcmp(e->partition_type_guid, GPT_UNUSED_ENTRY_GUID)) { + blkid_partlist_increment_partno(ls); + continue; + } + /* the partition has to inside usable range */ + if (start < fu || start + size - 1 > lu) { + DBG(LOWPROBE, ul_debug( + "GPT entry[%d] overflows usable area - ignore", + i)); + blkid_partlist_increment_partno(ls); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, + start * ssf, size * ssf); + if (!par) + goto err; + + blkid_partition_set_utf8name(par, + (unsigned char *) e->partition_name, + sizeof(e->partition_name), UL_ENCODE_UTF16LE); + + guid = e->unique_partition_guid; + swap_efi_guid(&guid); + blkid_partition_set_uuid(par, (const unsigned char *) &guid); + + guid = e->partition_type_guid; + swap_efi_guid(&guid); + blkid_partition_set_type_uuid(par, (const unsigned char *) &guid); + + blkid_partition_set_flags(par, le64_to_cpu(e->attributes)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; + +err: + return -ENOMEM; +} + + +const struct blkid_idinfo gpt_pt_idinfo = +{ + .name = "gpt", + .probefunc = probe_gpt_pt, + + /* + * It would be possible to check for DOS signature (0xAA55), but + * unfortunately almost all EFI GPT implementations allow to optionally + * skip the legacy MBR. We follows this behavior and MBR is optional. + * See is_valid_pmbr(). + * + * It means we have to always call probe_gpt_pt(). + */ + .magics = BLKID_NONE_MAGIC +}; + + + +/* probe for *alone* protective MBR */ +static int probe_pmbr_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int has = 0; + struct gpt_entry *e; + uint64_t lastlba = 0; + struct gpt_header hdr; + + if (last_lba(pr, &lastlba)) + goto nothing; + + is_pmbr_valid(pr, &has); + if (!has) + goto nothing; + + if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) && + !get_gpt_header(pr, &hdr, &e, lastlba, lastlba)) + return 0; +nothing: + return 1; +} + +const struct blkid_idinfo pmbr_pt_idinfo = +{ + .name = "PMBR", + .probefunc = probe_pmbr_pt, + .magics = + { + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c new file mode 100644 index 0000000..75a558b --- /dev/null +++ b/libblkid/src/partitions/mac.c @@ -0,0 +1,200 @@ +/* + * mac partitions parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +#define MAC_PARTITION_MAGIC 0x504d +#define MAC_PARTITION_MAGIC_OLD 0x5453 + +/* + * Mac partition entry + * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-126.html + */ +struct mac_partition { + uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ + uint16_t reserved; /* reserved */ + uint32_t map_count; /* # blocks in partition map */ + uint32_t start_block; /* absolute starting block # of partition */ + uint32_t block_count; /* number of blocks in partition */ + char name[32]; /* partition name */ + char type[32]; /* string type description */ + uint32_t data_start; /* rel block # of first data block */ + uint32_t data_count; /* number of data blocks */ + uint32_t status; /* partition status bits */ + uint32_t boot_start; /* first logical block of boot code */ + uint32_t boot_size; /* size of boot code, in bytes */ + uint32_t boot_load; /* boot code load address */ + uint32_t boot_load2; /* reserved */ + uint32_t boot_entry; /* boot code entry point */ + uint32_t boot_entry2; /* reserved */ + uint32_t boot_cksum; /* boot code checksum */ + char processor[16]; /* identifies ISA of boot */ + + /* there is more stuff after this that we don't need */ +} __attribute__((packed)); + +/* + * Driver descriptor structure, in block 0 + * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-121.html + */ +struct mac_driver_desc { + uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ + uint16_t block_size; /* block size of the device */ + uint32_t block_count; /* number of blocks on the device */ + + /* there is more stuff after this that we don't need */ +} __attribute__((packed)); + +static inline unsigned char *get_mac_block( + blkid_probe pr, + uint16_t block_size, + uint32_t num) +{ + return blkid_probe_get_buffer(pr, (uint64_t) num * block_size, block_size); +} + +static inline int has_part_signature(struct mac_partition *p) +{ + return be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC || + be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC_OLD; +} + +static int probe_mac_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct mac_driver_desc *md; + struct mac_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint16_t block_size; + uint16_t ssf; /* sector size fragment */ + uint32_t nblks, nprts, i; + + + /* The driver descriptor record is always located at physical block 0, + * the first block on the disk. + */ + md = (struct mac_driver_desc *) blkid_probe_get_sector(pr, 0); + if (!md) { + if (errno) + return -errno; + goto nothing; + } + + block_size = be16_to_cpu(md->block_size); + if (block_size < sizeof(struct mac_partition)) + goto nothing; + + /* The partition map always begins at physical block 1, + * the second block on the disk. + */ + p = (struct mac_partition *) get_mac_block(pr, block_size, 1); + if (!p) { + if (errno) + return -errno; + goto nothing; + } + + /* check the first partition signature */ + if (!has_part_signature(p)) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return 0; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "mac", 0); + if (!tab) + goto err; + + ssf = block_size / 512; + nblks = be32_to_cpu(p->map_count); + if (nblks > 256) { + nprts = 256; + DBG(LOWPROBE, ul_debug( + "mac: map_count too large, entry[0]: %u, " + "enforcing limit of %u", nblks, nprts)); + } else + nprts = nblks; + + for (i = 0; i < nprts; ++i) { + blkid_partition par; + uint32_t start; + uint32_t size; + + p = (struct mac_partition *) get_mac_block(pr, block_size, i + 1); + if (!p) { + if (errno) + return -errno; + goto nothing; + } + if (!has_part_signature(p)) + goto nothing; + + if (be32_to_cpu(p->map_count) != nblks) { + DBG(LOWPROBE, ul_debug( + "mac: inconsistent map_count in partition map, " + "entry[0]: %u, entry[%u]: %u", + nblks, i, + be32_to_cpu(p->map_count))); + } + + /* + * note that libparted ignores some mac partitions according to + * the partition name (e.g. "Apple_Free" or "Apple_Void"). We + * follows Linux kernel and all partitions are visible + */ + + start = be32_to_cpu(p->start_block) * ssf; + size = be32_to_cpu(p->block_count) * ssf; + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_name(par, (unsigned char *) p->name, + sizeof(p->name)); + + blkid_partition_set_type_string(par, (unsigned char *) p->type, + sizeof(p->type)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +/* + * Mac disk always begin with "Driver Descriptor Record" + * (struct mac_driver_desc) and magic 0x4552. + */ +const struct blkid_idinfo mac_pt_idinfo = +{ + .name = "mac", + .probefunc = probe_mac_pt, + .magics = + { + /* big-endian magic string */ + { .magic = "\x45\x52", .len = 2 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/minix.c b/libblkid/src/partitions/minix.c new file mode 100644 index 0000000..43c9d9a --- /dev/null +++ b/libblkid/src/partitions/minix.c @@ -0,0 +1,102 @@ +/* + * Minix partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "minix.h" + +static int probe_minix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct dos_partition *p; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + unsigned char *data; + int i; + + data = blkid_probe_get_sector(pr, 0); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + /* Parent is required, because Minix uses the same PT as DOS and + * difference is only in primary partition (parent) type. + */ + parent = blkid_partlist_get_parent(ls); + if (!parent) + goto nothing; + + if (blkid_partition_get_type(parent) != MBR_MINIX_PARTITION) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + tab = blkid_partlist_new_parttable(ls, "minix", MBR_PT_OFFSET); + if (!tab) + goto err; + + for (i = 0, p = mbr_get_partition(data, 0); + i < MINIX_MAXPARTITIONS; i++, p++) { + + uint32_t start, size; + blkid_partition par; + + if (p->sys_ind != MBR_MINIX_PARTITION) + continue; + + start = dos_partition_get_start(p); + size = dos_partition_get_size(p); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: minix partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, p->sys_ind); + blkid_partition_set_flags(par, p->boot_ind); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +/* same as DOS */ +const struct blkid_idinfo minix_pt_idinfo = +{ + .name = "minix", + .probefunc = probe_minix_pt, + .magics = + { + { .magic = "\x55\xAA", .len = 2, .sboff = 510 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c new file mode 100644 index 0000000..8ebf480 --- /dev/null +++ b/libblkid/src/partitions/partitions.c @@ -0,0 +1,1532 @@ +/* + * partitions - partition tables parsing + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdint.h> +#include <inttypes.h> +#include <stdarg.h> + +#include "partitions.h" +#include "sysfs.h" +#include "strutils.h" + +/** + * SECTION: partitions + * @title: Partitions probing + * @short_description: partitions tables detection and parsing + * + * This chain supports binary and NAME=value interfaces, but complete PT + * description is provided by binary interface only. The libblkid prober is + * compatible with kernel partition tables parser. The parser does not return + * empty (size=0) partitions or special hidden partitions. + * + * NAME=value interface, supported tags: + * + * @PTTYPE: partition table type (dos, gpt, etc.). + * + * @PTUUID: partition table id (uuid for gpt, hex for dos). + + * @PART_ENTRY_SCHEME: partition table type + * + * @PART_ENTRY_NAME: partition name (gpt and mac only) + * + * @PART_ENTRY_UUID: partition UUID (gpt, or pseudo IDs for MBR) + * + * @PART_ENTRY_TYPE: partition type, 0xNN (e.g. 0x82) or type UUID (gpt only) or type string (mac) + * + * @PART_ENTRY_FLAGS: partition flags (e.g. boot_ind) or attributes (e.g. gpt attributes) + * + * @PART_ENTRY_NUMBER: partition number + * + * @PART_ENTRY_OFFSET: the begin of the partition + * + * @PART_ENTRY_SIZE: size of the partition + * + * @PART_ENTRY_DISK: whole-disk maj:min + * + * Example: + * + * <informalexample> + * <programlisting> + * blkid_probe pr; + * const char *ptname; + * + * pr = blkid_new_probe_from_filename(devname); + * if (!pr) + * err("%s: failed to open device", devname); + * + * blkid_probe_enable_partitions(pr, TRUE); + * blkid_do_fullprobe(pr); + * + * blkid_probe_lookup_value(pr, "PTTYPE", &ptname, NULL); + * printf("%s partition type detected\n", pttype); + * + * blkid_free_probe(pr); + * + * // don't forget to check return codes in your code! + * </programlisting> + * </informalexample> + * + * Binary interface: + * + * <informalexample> + * <programlisting> + * blkid_probe pr; + * blkid_partlist ls; + * int nparts, i; + * + * pr = blkid_new_probe_from_filename(devname); + * if (!pr) + * err("%s: failed to open device", devname); + * + * ls = blkid_probe_get_partitions(pr); + * nparts = blkid_partlist_numof_partitions(ls); + * + * for (i = 0; i < nparts; i++) { + * blkid_partition par = blkid_partlist_get_partition(ls, i); + * printf("#%d: %llu %llu 0x%x", + * blkid_partition_get_partno(par), + * blkid_partition_get_start(par), + * blkid_partition_get_size(par), + * blkid_partition_get_type(par)); + * } + * + * blkid_free_probe(pr); + * + * // don't forget to check return codes in your code! + * </programlisting> + * </informalexample> + */ + +/* + * Chain driver function + */ +static int partitions_probe(blkid_probe pr, struct blkid_chain *chn); +static void partitions_free_data(blkid_probe pr, void *data); + +/* + * Partitions chain probing functions + */ +static const struct blkid_idinfo *idinfos[] = +{ + &aix_pt_idinfo, + &sgi_pt_idinfo, + &sun_pt_idinfo, + &dos_pt_idinfo, + &gpt_pt_idinfo, + &pmbr_pt_idinfo, /* always after GPT */ + &mac_pt_idinfo, + &ultrix_pt_idinfo, + &bsd_pt_idinfo, + &unixware_pt_idinfo, + &solaris_x86_pt_idinfo, + &minix_pt_idinfo, + &atari_pt_idinfo +}; + +/* + * Driver definition + */ +const struct blkid_chaindrv partitions_drv = { + .id = BLKID_CHAIN_PARTS, + .name = "partitions", + .dflt_enabled = FALSE, + .idinfos = idinfos, + .nidinfos = ARRAY_SIZE(idinfos), + .has_fltr = TRUE, + .probe = partitions_probe, + .safeprobe = partitions_probe, + .free_data = partitions_free_data +}; + + +/* + * For compatibility with the rest of libblkid API (with the old high-level + * API) we use completely opaque typedefs for all structs. Don't forget that + * the final blkid_* types are pointers! See blkid.h. + * + * [Just for the record, I hate typedef for pointers --kzak] + */ + +/* exported as opaque type "blkid_parttable" */ +struct blkid_struct_parttable { + const char *type; /* partition table type */ + uint64_t offset; /* begin of the partition table (in bytes) */ + int nparts; /* number of partitions */ + blkid_partition parent; /* parent of nested partition table */ + char id[UUID_STR_LEN]; /* PT identifier (e.g. UUID for GPT) */ + + struct list_head t_tabs; /* all tables */ +}; + +/* exported as opaque type "blkid_partition" */ +struct blkid_struct_partition { + uint64_t start; /* begin of the partition (512-bytes sectors) */ + uint64_t size; /* size of the partitions (512-bytes sectors) */ + + int type; /* partition type */ + char typestr[UUID_STR_LEN]; /* partition type string (GPT and Mac) */ + + unsigned long long flags; /* partition flags / attributes */ + + int partno; /* partition number */ + char uuid[UUID_STR_LEN]; /* UUID (when supported by PT), e.g. GPT */ + unsigned char name[128]; /* Partition in UTF8 name (when supported by PT), e.g. Mac */ + + blkid_parttable tab; /* partition table */ +}; + +/* exported as opaque type "blkid_partlist" */ +struct blkid_struct_partlist { + int next_partno; /* next partition number */ + blkid_partition next_parent; /* next parent if parsing nested PT */ + + int nparts; /* number of partitions */ + int nparts_max; /* max.number of partitions */ + blkid_partition parts; /* array of partitions */ + + struct list_head l_tabs; /* list of partition tables */ +}; + +static int blkid_partitions_probe_partition(blkid_probe pr); + +/** + * blkid_probe_enable_partitions: + * @pr: probe + * @enable: TRUE/FALSE + * + * Enables/disables the partitions probing for non-binary interface. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_enable_partitions(blkid_probe pr, int enable) +{ + pr->chains[BLKID_CHAIN_PARTS].enabled = enable; + return 0; +} + +/** + * blkid_probe_set_partitions_flags: + * @pr: prober + * @flags: BLKID_PARTS_* flags + * + * Sets probing flags to the partitions prober. This function is optional. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_set_partitions_flags(blkid_probe pr, int flags) +{ + pr->chains[BLKID_CHAIN_PARTS].flags = flags; + return 0; +} + +int blkid_probe_get_partitions_flags(blkid_probe pr) +{ + return pr->chains[BLKID_CHAIN_PARTS].flags; +} + +/** + * blkid_probe_reset_partitions_filter: + * @pr: prober + * + * Resets partitions probing filter + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_reset_partitions_filter(blkid_probe pr) +{ + return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS); +} + +/** + * blkid_probe_invert_partitions_filter: + * @pr: prober + * + * Inverts partitions probing filter + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_invert_partitions_filter(blkid_probe pr) +{ + return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS); +} + +/** + * blkid_probe_filter_partitions_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_partitions_type(blkid_probe pr, int flag, char *names[]) +{ + return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names); +} + +/** + * blkid_probe_get_partitions: + * @pr: probe + * + * This is a binary interface for partitions. See also blkid_partlist_* + * functions. + * + * This function is independent on blkid_do_[safe,full]probe() and + * blkid_probe_enable_partitions() calls. + * + * WARNING: the returned object will be overwritten by the next + * blkid_probe_get_partitions() call for the same @pr. If you want to + * use more blkid_partlist objects in the same time you have to create + * more blkid_probe handlers (see blkid_new_probe()). + * + * Returns: list of partitions, or NULL in case of error. + */ +blkid_partlist blkid_probe_get_partitions(blkid_probe pr) +{ + return (blkid_partlist) blkid_probe_get_binary_data(pr, + &pr->chains[BLKID_CHAIN_PARTS]); +} + +/* for internal usage only */ +blkid_partlist blkid_probe_get_partlist(blkid_probe pr) +{ + return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data; +} + +static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls) +{ + pr->chains[BLKID_CHAIN_PARTS].data = ls; +} + +static void ref_parttable(blkid_parttable tab) +{ + if (tab) + tab->nparts++; +} + +static void unref_parttable(blkid_parttable tab) +{ + if (!tab) + return; + + tab->nparts--; + if (tab->nparts <= 0) { + list_del(&tab->t_tabs); + free(tab); + } +} + +/* free all allocated parttables */ +static void free_parttables(blkid_partlist ls) +{ + if (!ls || !ls->l_tabs.next) + return; + + /* remove unassigned partition tables */ + while (!list_empty(&ls->l_tabs)) { + blkid_parttable tab = list_entry(ls->l_tabs.next, + struct blkid_struct_parttable, t_tabs); + unref_parttable(tab); + } +} + +static void reset_partlist(blkid_partlist ls) +{ + if (!ls) + return; + + free_parttables(ls); + + if (ls->next_partno) { + /* already initialized - reset */ + int tmp_nparts = ls->nparts_max; + blkid_partition tmp_parts = ls->parts; + + memset(ls, 0, sizeof(struct blkid_struct_partlist)); + + ls->nparts_max = tmp_nparts; + ls->parts = tmp_parts; + } + + ls->nparts = 0; + ls->next_partno = 1; + INIT_LIST_HEAD(&ls->l_tabs); + + DBG(LOWPROBE, ul_debug("partlist reset")); +} + +static blkid_partlist partitions_init_data(struct blkid_chain *chn) +{ + blkid_partlist ls; + + if (chn->data) + ls = (blkid_partlist) chn->data; + else { + /* allocate the new list of partitions */ + ls = calloc(1, sizeof(struct blkid_struct_partlist)); + if (!ls) + return NULL; + chn->data = (void *) ls; + } + + reset_partlist(ls); + + DBG(LOWPROBE, ul_debug("parts: initialized partitions list (size=%d)", ls->nparts_max)); + return ls; +} + +static void partitions_free_data(blkid_probe pr __attribute__((__unused__)), + void *data) +{ + blkid_partlist ls = (blkid_partlist) data; + + if (!ls) + return; + + free_parttables(ls); + + /* deallocate partitions and partlist */ + free(ls->parts); + free(ls); +} + +blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls, + const char *type, uint64_t offset) +{ + blkid_parttable tab; + + tab = calloc(1, sizeof(struct blkid_struct_parttable)); + if (!tab) + return NULL; + tab->type = type; + tab->offset = offset; + tab->parent = ls->next_parent; + + INIT_LIST_HEAD(&tab->t_tabs); + list_add_tail(&tab->t_tabs, &ls->l_tabs); + + DBG(LOWPROBE, ul_debug("parts: create a new partition table " + "(type=%s, offset=%"PRId64")", type, offset)); + return tab; +} + +static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab) +{ + blkid_partition par; + + if (ls->nparts + 1 > ls->nparts_max) { + /* Linux kernel has DISK_MAX_PARTS=256, but it's too much for + * generic Linux machine -- let start with 32 partitions. + */ + void *tmp = realloc(ls->parts, (ls->nparts_max + 32) * + sizeof(struct blkid_struct_partition)); + if (!tmp) + return NULL; + ls->parts = tmp; + ls->nparts_max += 32; + } + + par = &ls->parts[ls->nparts++]; + memset(par, 0, sizeof(struct blkid_struct_partition)); + + ref_parttable(tab); + par->tab = tab; + par->partno = blkid_partlist_increment_partno(ls); + + return par; +} + +blkid_partition blkid_partlist_add_partition(blkid_partlist ls, + blkid_parttable tab, uint64_t start, uint64_t size) +{ + blkid_partition par = new_partition(ls, tab); + + if (!par) + return NULL; + + par->start = start; + par->size = size; + + DBG(LOWPROBE, ul_debug("parts: add partition (start=%" + PRIu64 ", size=%" PRIu64 ")", + par->start, par->size)); + return par; +} + +/* can be used to modify used partitions numbers (for example for logical partitions) */ +int blkid_partlist_set_partno(blkid_partlist ls, int partno) +{ + if (!ls) + return -1; + ls->next_partno = partno; + return 0; +} + +int blkid_partlist_increment_partno(blkid_partlist ls) +{ + return ls ? ls->next_partno++ : -1; +} + +/* can be used to set "parent" for the next nested partition */ +static int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par) +{ + if (!ls) + return -1; + ls->next_parent = par; + return 0; +} + +blkid_partition blkid_partlist_get_parent(blkid_partlist ls) +{ + if (!ls) + return NULL; + return ls->next_parent; +} + +int blkid_partitions_need_typeonly(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + return chn && chn->data && chn->binary ? FALSE : TRUE; +} + +/* get private chain flags */ +int blkid_partitions_get_flags(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + return chn ? chn->flags : 0; +} + +/* check if @start and @size are within @par partition */ +int blkid_is_nested_dimension(blkid_partition par, + uint64_t start, uint64_t size) +{ + uint64_t pstart; + uint64_t psize; + + if (!par) + return 0; + + pstart = blkid_partition_get_start(par); + psize = blkid_partition_get_size(par); + + if (start < pstart || start + size > pstart + psize) + return 0; + + return 1; +} + +static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id, + struct blkid_chain *chn) +{ + const struct blkid_idmag *mag = NULL; + uint64_t off; + int rc = BLKID_PROBE_NONE; /* default is nothing */ + + if (pr->size <= 0 || (id->minsz && (unsigned)id->minsz > pr->size)) + goto nothing; /* the device is too small */ + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto nothing; + + rc = blkid_probe_get_idmag(pr, id, &off, &mag); + if (rc != BLKID_PROBE_OK) + goto nothing; + + /* final check by probing function */ + if (id->probefunc) { + DBG(LOWPROBE, ul_debug( + "%s: ---> call probefunc()", id->name)); + errno = 0; + rc = id->probefunc(pr, mag); + if (rc < 0) { + /* reset after error */ + reset_partlist(blkid_probe_get_partlist(pr)); + if (chn && !chn->binary) + blkid_probe_chain_reset_values(pr, chn); + DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d", + id->name, rc)); + } + if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary) + rc = blkid_probe_set_magic(pr, off, mag->len, + (const unsigned char *) mag->magic); + + DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc)); + } + + return rc; + +nothing: + return BLKID_PROBE_NONE; +} + +/* + * The blkid_do_probe() backend. + */ +static int partitions_probe(blkid_probe pr, struct blkid_chain *chn) +{ + int rc = BLKID_PROBE_NONE; + size_t i; + + if (!pr || chn->idx < -1) + return -EINVAL; + + blkid_probe_chain_reset_values(pr, chn); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + if (chn->binary) + partitions_init_data(chn); + + if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT)) + goto details_only; + + DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]", + chn->idx)); + + i = chn->idx < 0 ? 0 : chn->idx + 1U; + + for ( ; i < ARRAY_SIZE(idinfos); i++) { + const char *name; + + chn->idx = i; + + /* apply filter */ + if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) + continue; + + /* apply checks from idinfo */ + rc = idinfo_probe(pr, idinfos[i], chn); + if (rc < 0) + break; + if (rc != BLKID_PROBE_OK) + continue; + + name = idinfos[i]->name; + + if (!chn->binary) + /* + * Non-binary interface, set generic variables. Note + * that the another variables could be set in prober + * functions. + */ + blkid_probe_set_value(pr, "PTTYPE", + (const unsigned char *) name, + strlen(name) + 1); + + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]", + name, chn->idx)); + rc = BLKID_PROBE_OK; + break; + } + + if (rc != BLKID_PROBE_OK) { + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]", + rc, chn->idx)); + } + +details_only: + /* + * Gather PART_ENTRY_* values if the current device is a partition. + */ + if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary && + (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) { + + int xrc = blkid_partitions_probe_partition(pr); + + /* partition entry probing is optional, and "not-found" from + * this sub-probing must not to overwrite previous success. */ + if (xrc < 0) + rc = xrc; /* always propagate errors */ + else if (rc == BLKID_PROBE_NONE) + rc = xrc; + } + + DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]", rc)); + return rc; +} + +/* Probe for nested partition table within the parental partition */ +int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent, + const struct blkid_idinfo *id) +{ + blkid_probe prc; + int rc; + blkid_partlist ls; + uint64_t sz, off; + + DBG(LOWPROBE, ul_debug( + "parts: ----> %s subprobe requested)", + id->name)); + + if (!pr || !parent || !parent->size) + return -EINVAL; + if (pr->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + /* range defined by parent */ + sz = parent->size << 9; + off = parent->start << 9; + + if (off < pr->off || pr->off + pr->size < off + sz) { + DBG(LOWPROBE, ul_debug( + "ERROR: parts: <---- '%s' subprobe: overflow detected.", + id->name)); + return -ENOSPC; + } + + /* create private prober */ + prc = blkid_clone_probe(pr); + if (!prc) + return -ENOMEM; + + blkid_probe_set_dimension(prc, off, sz); + + /* clone is always with reset chain, fix it */ + prc->cur_chain = blkid_probe_get_chain(pr); + + /* + * Set 'parent' to the current list of the partitions and use the list + * in cloned prober (so the cloned prober will extend the current list + * of partitions rather than create a new). + */ + ls = blkid_probe_get_partlist(pr); + blkid_partlist_set_parent(ls, parent); + + blkid_probe_set_partlist(prc, ls); + + rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr)); + + blkid_probe_set_partlist(prc, NULL); + blkid_partlist_set_parent(ls, NULL); + + blkid_free_probe(prc); /* free cloned prober */ + + DBG(LOWPROBE, ul_debug( + "parts: <---- %s subprobe done (rc=%d)", + id->name, rc)); + + return rc; +} + +static int blkid_partitions_probe_partition(blkid_probe pr) +{ + blkid_probe disk_pr = NULL; + blkid_partlist ls; + blkid_partition par; + dev_t devno; + + DBG(LOWPROBE, ul_debug("parts: start probing for partition entry")); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto nothing; + + devno = blkid_probe_get_devno(pr); + if (!devno) + goto nothing; + + disk_pr = blkid_probe_get_wholedisk_probe(pr); + if (!disk_pr) + goto nothing; + + /* parse PT */ + ls = blkid_probe_get_partitions(disk_pr); + if (!ls) + goto nothing; + + par = blkid_partlist_devno_to_partition(ls, devno); + if (!par) + goto nothing; + else { + const char *v; + blkid_parttable tab = blkid_partition_get_table(par); + dev_t disk = blkid_probe_get_devno(disk_pr); + + if (tab) { + v = blkid_parttable_get_type(tab); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_SCHEME", + (const unsigned char *) v, strlen(v) + 1); + } + + v = blkid_partition_get_name(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_NAME", + (const unsigned char *) v, strlen(v) + 1); + + v = blkid_partition_get_uuid(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_UUID", + (const unsigned char *) v, strlen(v) + 1); + + /* type */ + v = blkid_partition_get_type_string(par); + if (v) + blkid_probe_set_value(pr, "PART_ENTRY_TYPE", + (const unsigned char *) v, strlen(v) + 1); + else + blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE", + "0x%x", blkid_partition_get_type(par)); + + if (blkid_partition_get_flags(par)) + blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS", + "0x%llx", blkid_partition_get_flags(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER", + "%d", blkid_partition_get_partno(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd", + (intmax_t)blkid_partition_get_start(par)); + blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd", + (intmax_t)blkid_partition_get_size(par)); + + blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u", + major(disk), minor(disk)); + } + + DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]")); + return BLKID_PROBE_OK; + +nothing: + DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]")); + return BLKID_PROBE_NONE; + + +} + +/* + * Returns 1 if the device is whole-disk and the area specified by @offset and + * @size is covered by any partition. + */ +int blkid_probe_is_covered_by_pt(blkid_probe pr, + uint64_t offset, uint64_t size) +{ + blkid_probe prc = NULL; + blkid_partlist ls = NULL; + uint64_t start, end; + int nparts, i, rc = 0; + + DBG(LOWPROBE, ul_debug( + "=> checking if off=%"PRIu64" size=%"PRIu64" covered by PT", + offset, size)); + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + goto done; + + prc = blkid_clone_probe(pr); + if (!prc) + goto done; + + ls = blkid_probe_get_partitions(prc); + if (!ls) + goto done; + + nparts = blkid_partlist_numof_partitions(ls); + if (!nparts) + goto done; + + end = (offset + size) >> 9; + start = offset >> 9; + + /* check if the partition table fits into the device */ + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (par->start + par->size > (pr->size >> 9)) { + DBG(LOWPROBE, ul_debug("partition #%d overflows " + "device (off=%" PRId64 " size=%" PRId64 ")", + par->partno, par->start, par->size)); + goto done; + } + } + + /* check if the requested area is covered by PT */ + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (start >= par->start && end <= par->start + par->size) { + rc = 1; + break; + } + } +done: + blkid_free_probe(prc); + + DBG(LOWPROBE, ul_debug("<= %s covered by PT", rc ? "IS" : "NOT")); + return rc; +} + +/** + * blkid_known_pttype: + * @pttype: partition name + * + * Returns: 1 for known or 0 for unknown partition type. + */ +int blkid_known_pttype(const char *pttype) +{ + size_t i; + + if (!pttype) + return 0; + + for (i = 0; i < ARRAY_SIZE(idinfos); i++) { + const struct blkid_idinfo *id = idinfos[i]; + if (strcmp(id->name, pttype) == 0) + return 1; + } + return 0; +} + +/** + * blkid_partitions_get_name: + * @idx: number >= 0 + * @name: returns name of a supported partition + * + * Since: 2.30 + * + * Returns: -1 if @idx is out of range, or 0 on success. + */ +int blkid_partitions_get_name(const size_t idx, const char **name) +{ + if (idx < ARRAY_SIZE(idinfos)) { + *name = idinfos[idx]->name; + return 0; + } + return -1; +} + +/** + * blkid_partlist_numof_partitions: + * @ls: partitions list + * + * Returns: number of partitions in the list or -1 in case of error. + */ +int blkid_partlist_numof_partitions(blkid_partlist ls) +{ + return ls->nparts; +} + +/** + * blkid_partlist_get_table: + * @ls: partitions list + * + * Returns: top-level partition table or NULL if there is not a partition table + * on the device. + */ +blkid_parttable blkid_partlist_get_table(blkid_partlist ls) +{ + if (list_empty(&ls->l_tabs)) + return NULL; + + return list_entry(ls->l_tabs.next, + struct blkid_struct_parttable, t_tabs); +} + + +/** + * blkid_partlist_get_partition: + * @ls: partitions list + * @n: partition number in range 0..N, where 'N' is blkid_partlist_numof_partitions(). + * + * It's possible that the list of partitions is *empty*, but there is a valid + * partition table on the disk. This happen when on-disk details about + * partitions are unknown or the partition table is empty. + * + * See also blkid_partlist_get_table(). + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n) +{ + if (n < 0 || n >= ls->nparts) + return NULL; + + return &ls->parts[n]; +} + +blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start) +{ + int i, nparts; + blkid_partition par; + + nparts = blkid_partlist_numof_partitions(ls); + for (i = 0; i < nparts; i++) { + par = blkid_partlist_get_partition(ls, i); + if ((uint64_t) blkid_partition_get_start(par) == start) + return par; + } + return NULL; +} + +/** + * blkid_partlist_get_partition_by_partno + * @ls: partitions list + * @n: the partition number (e.g. 'N' from sda'N') + * + * This does not assume any order of the input blkid_partlist. And correctly + * handles "out of order" partition tables. partition N is located after + * partition N+1 on the disk. + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n) +{ + int i, nparts; + blkid_partition par; + + nparts = blkid_partlist_numof_partitions(ls); + for (i = 0; i < nparts; i++) { + par = blkid_partlist_get_partition(ls, i); + if (n == blkid_partition_get_partno(par)) + return par; + } + return NULL; +} + + +/** + * blkid_partlist_devno_to_partition: + * @ls: partitions list + * @devno: requested partition + * + * This function tries to get start and size for @devno from sysfs and + * returns a partition from @ls which matches with the values from sysfs. + * + * This function is necessary when you want to make a relation between an entry + * in the partition table (@ls) and block devices in your system. + * + * Returns: partition object or NULL in case or error. + */ +blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno) +{ + struct path_cxt *pc; + uint64_t start = 0, size; + int i, rc, partno = 0; + + DBG(LOWPROBE, ul_debug("trying to convert devno 0x%llx to partition", + (long long) devno)); + + + pc = ul_new_sysfs_path(devno, NULL, NULL); + if (!pc) { + DBG(LOWPROBE, ul_debug("failed t init sysfs context")); + return NULL; + } + rc = ul_path_read_u64(pc, &size, "size"); + if (!rc) { + rc = ul_path_read_u64(pc, &start, "start"); + if (rc) { + /* try to get partition number from DM uuid. + */ + char *uuid = NULL, *tmp, *prefix; + + ul_path_read_string(pc, &uuid, "dm/uuid"); + tmp = uuid; + prefix = uuid ? strsep(&tmp, "-") : NULL; + + if (prefix && strncasecmp(prefix, "part", 4) == 0) { + char *end = NULL; + + errno = 0; + partno = strtol(prefix + 4, &end, 10); + if (errno || prefix == end || (end && *end)) + partno = 0; + else + rc = 0; /* success */ + } + free(uuid); + } + } + + ul_unref_path(pc); + + if (rc) + return NULL; + + if (partno) { + DBG(LOWPROBE, ul_debug("mapped by DM, using partno %d", partno)); + + /* + * Partition mapped by kpartx does not provide "start" offset + * in /sys, but if we know partno and size of the partition + * that we can probably make the relation between the device + * and an entry in partition table. + */ + for (i = 0; i < ls->nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (partno != blkid_partition_get_partno(par)) + continue; + + if (size == (uint64_t)blkid_partition_get_size(par) || + (blkid_partition_is_extended(par) && size <= 1024ULL)) + return par; + + } + return NULL; + } + + DBG(LOWPROBE, ul_debug("searching by offset/size")); + + for (i = 0; i < ls->nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if ((uint64_t)blkid_partition_get_start(par) == start && + (uint64_t)blkid_partition_get_size(par) == size) + return par; + + /* exception for extended dos partitions */ + if ((uint64_t)blkid_partition_get_start(par) == start && + blkid_partition_is_extended(par) && size <= 1024ULL) + return par; + + } + + DBG(LOWPROBE, ul_debug("not found partition for device")); + return NULL; +} + + +int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id) +{ + if (!tab) + return -1; + + blkid_unparse_uuid(id, tab->id, sizeof(tab->id)); + return 0; +} + +int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id) +{ + if (!tab) + return -1; + + xstrncpy(tab->id, (const char *) id, sizeof(tab->id)); + return 0; +} + +/* set PTUUID variable for non-binary API */ +int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + struct blkid_prval *v; + + if (chn->binary || blkid_uuid_is_empty(uuid, 16)) + return 0; + + v = blkid_probe_assign_value(pr, "PTUUID"); + if (!v) + return -ENOMEM; + + v->len = UUID_STR_LEN; + v->data = calloc(1, v->len); + if (v->data) { + blkid_unparse_uuid(uuid, (char *) v->data, v->len); + return 0; + } + + blkid_probe_free_value(v); + return -ENOMEM; +} + +/* set PTUUID variable for non-binary API for tables where + * the ID is just a string */ +int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (chn->binary || !str || !*str) + return 0; + + if (!blkid_probe_set_value(pr, "PTUUID", (unsigned char *) str, strlen(str) + 1)) + return -ENOMEM; + + return 0; +} + +/** + * blkid_parttable_get_id: + * @tab: partition table + * + * The ID is GPT disk UUID or DOS disk ID (in hex format). + * + * Returns: partition table ID (for example GPT disk UUID) or NULL + */ +const char *blkid_parttable_get_id(blkid_parttable tab) +{ + return *tab->id ? tab->id : NULL; +} + + +int blkid_partition_set_type(blkid_partition par, int type) +{ + par->type = type; + return 0; +} + +/** + * blkid_parttable_get_type: + * @tab: partition table + * + * Returns: partition table type (type name, e.g. "dos", "gpt", ...) + */ +const char *blkid_parttable_get_type(blkid_parttable tab) +{ + return tab->type; +} + +/** + * blkid_parttable_get_parent: + * @tab: partition table + * + * Returns: parent for nested partition tables or NULL. + */ +blkid_partition blkid_parttable_get_parent(blkid_parttable tab) +{ + return tab->parent; +} + +/** + * blkid_parttable_get_offset: + * @tab: partition table + * + * Note the position is relative to begin of the device as defined by + * blkid_probe_set_device() for primary partition table, and relative + * to parental partition for nested partition tables. + * + * <informalexample> + * <programlisting> + * off_t offset; + * blkid_partition parent = blkid_parttable_get_parent(tab); + * + * offset = blkid_parttable_get_offset(tab); + * + * if (parent) + * / * 'tab' is nested partition table * / + * offset += blkid_partition_get_start(parent); + * </programlisting> + * </informalexample> + + * Returns: position (in bytes) of the partition table or -1 in case of error. + * + */ +blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab) +{ + return (blkid_loff_t)tab->offset; +} + +/** + * blkid_partition_get_table: + * @par: partition + * + * The "parttable" describes partition table. The table is usually the same for + * all partitions -- except nested partition tables. + * + * For example bsd, solaris, etc. use a nested partition table within + * standard primary dos partition: + * + * <informalexample> + * <programlisting> + * + * -- dos partition table + * 0: sda1 dos primary partition + * 1: sda2 dos primary partition + * -- bsd partition table (with in sda2) + * 2: sda5 bds partition + * 3: sda6 bds partition + * + * </programlisting> + * </informalexample> + * + * The library does not to use a separate partition table object for dos logical + * partitions (partitions within extended partition). It's possible to + * differentiate between logical, extended and primary partitions by + * + * blkid_partition_is_{extended,primary,logical}(). + * + * Returns: partition table object or NULL in case of error. + */ +blkid_parttable blkid_partition_get_table(blkid_partition par) +{ + return par->tab; +} + +static int partition_get_logical_type(blkid_partition par) +{ + blkid_parttable tab; + + if (!par) + return -1; + + tab = blkid_partition_get_table(par); + if (!tab || !tab->type) + return -1; + + if (tab->parent) + return 'L'; /* report nested partitions as logical */ + + if (!strcmp(tab->type, "dos")) { + if (par->partno > 4) + return 'L'; /* logical */ + + if(par->type == MBR_DOS_EXTENDED_PARTITION || + par->type == MBR_W95_EXTENDED_PARTITION || + par->type == MBR_LINUX_EXTENDED_PARTITION) + return 'E'; + } + return 'P'; +} + +/** + * blkid_partition_is_primary: + * @par: partition + * + * Note, this function returns FALSE for DOS extended partitions and + * all partitions in nested partition tables. + * + * Returns: 1 if the partitions is primary partition or 0 if not. + */ +int blkid_partition_is_primary(blkid_partition par) +{ + return partition_get_logical_type(par) == 'P' ? TRUE : FALSE; +} + +/** + * blkid_partition_is_extended: + * @par: partition + * + * Returns: 1 if the partitions is extended (dos, windows or linux) + * partition or 0 if not. + */ +int blkid_partition_is_extended(blkid_partition par) +{ + return partition_get_logical_type(par) == 'E' ? TRUE : FALSE; +} + +/** + * blkid_partition_is_logical: + * @par: partition + * + * Note that this function returns TRUE for all partitions in all + * nested partition tables (e.g. BSD labels). + * + * Returns: 1 if the partitions is logical partition or 0 if not. + */ +int blkid_partition_is_logical(blkid_partition par) +{ + return partition_get_logical_type(par) == 'L' ? TRUE : FALSE; +} + +static void set_string(unsigned char *item, size_t max, + const unsigned char *data, size_t len) +{ + if (len >= max) + len = max - 1; + + memcpy(item, data, len); + item[len] = '\0'; + + blkid_rtrim_whitespace(item); +} + +int blkid_partition_set_name(blkid_partition par, + const unsigned char *name, size_t len) +{ + if (!par) + return -1; + + set_string(par->name, sizeof(par->name), name, len); + return 0; +} + +int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name, + size_t len, int enc) +{ + if (!par) + return -1; + + ul_encode_to_utf8(enc, par->name, sizeof(par->name), name, len); + blkid_rtrim_whitespace(par->name); + return 0; +} + +int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid) +{ + if (!par) + return -1; + + blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid)); + return 0; +} + +int blkid_partition_gen_uuid(blkid_partition par) +{ + if (!par || !par->tab || !*par->tab->id) + return -1; + + snprintf(par->uuid, sizeof(par->uuid), "%.33s-%02x", + par->tab->id, par->partno); + return 0; +} + +/** + * blkid_partition_get_name: + * @par: partition + * + * Returns: partition name string if supported by PT (e.g. Mac) or NULL. + */ +const char *blkid_partition_get_name(blkid_partition par) +{ + return *par->name ? (char *) par->name : NULL; +} + +/** + * blkid_partition_get_uuid: + * @par: partition + * + * Returns: partition UUID string if supported by PT (e.g. GPT) or NULL. + */ +const char *blkid_partition_get_uuid(blkid_partition par) +{ + return *par->uuid ? par->uuid : NULL; +} + +/** + * blkid_partition_get_partno: + * @par: partition + * + * Returns: proposed partition number (e.g. 'N' from sda'N') or -1 in case of + * error. Note that the number is generated by library independently of your OS. + */ +int blkid_partition_get_partno(blkid_partition par) +{ + return par->partno; +} + +/** + * blkid_partition_get_start: + * @par: partition + * + * Be careful if you _not_ probe whole disk: + * + * 1) the offset is usually relative to begin of the disk -- but if you probe a + * fragment of the disk only -- then the offset could be still relative to + * the begin of the disk rather that relative to the fragment. + * + * 2) the offset for nested partitions could be relative to parent (e.g. Solaris) + * _or_ relative to the begin of the whole disk (e.g. bsd). + * + * You don't have to care about such details if you probe whole disk. In such + * a case libblkid always returns the offset relative to the begin of the disk. + * + * Returns: start of the partition (in 512-sectors). + */ +blkid_loff_t blkid_partition_get_start(blkid_partition par) +{ + return (blkid_loff_t)par->start; +} + +/** + * blkid_partition_get_size: + * @par: partition + * + * WARNING: be very careful when you work with MS-DOS extended partitions. The + * library always returns full size of the partition. If you want to + * add the partition to the Linux system (BLKPG_ADD_PARTITION ioctl) + * you need to reduce the size of the partition to 1 or 2 blocks. The + * rest of the partition has to be inaccessible for mkfs or mkswap + * programs, we need a small space for boot loaders only. + * + * For some unknown reason this (safe) practice is not to used for + * nested BSD, Solaris, ..., partition tables in Linux kernel. + * + * Returns: size of the partition (in 512-sectors). + */ +blkid_loff_t blkid_partition_get_size(blkid_partition par) +{ + return (blkid_loff_t)par->size; +} + +/** + * blkid_partition_get_type: + * @par: partition + * + * Returns: partition type. + */ +int blkid_partition_get_type(blkid_partition par) +{ + return par->type; +} + +/* Sets partition 'type' for PT where the type is defined by string rather + * than by number + */ +int blkid_partition_set_type_string(blkid_partition par, + const unsigned char *type, size_t len) +{ + set_string((unsigned char *) par->typestr, + sizeof(par->typestr), type, len); + return 0; +} + +/* Sets partition 'type' for PT where the type is defined by UUID rather + * than by number + */ +int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid) +{ + blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr)); + return 0; +} + +/** + * blkid_partition_get_type_string: + * @par: partition + * + * The type string is supported by a small subset of partition tables (e.g. Mac + * and EFI GPT). Note that GPT uses type UUID and this function returns this + * UUID as string. + * + * Returns: partition type string or NULL. + */ +const char *blkid_partition_get_type_string(blkid_partition par) +{ + return *par->typestr ? par->typestr : NULL; +} + + +int blkid_partition_set_flags(blkid_partition par, unsigned long long flags) +{ + par->flags = flags; + return 0; +} + +/** + * blkid_partition_get_flags + * @par: partition + * + * Returns: partition flags (or attributes for gpt). + */ +unsigned long long blkid_partition_get_flags(blkid_partition par) +{ + return par->flags; +} + diff --git a/libblkid/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h new file mode 100644 index 0000000..784e0c0 --- /dev/null +++ b/libblkid/src/partitions/partitions.h @@ -0,0 +1,74 @@ +#ifndef BLKID_PARTITIONS_H +#define BLKID_PARTITIONS_H + +#include "blkidP.h" +#include "pt-mbr.h" + +extern int blkid_partitions_get_flags(blkid_probe pr); + +extern blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls, + const char *type, uint64_t offset); + +extern int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id); +extern int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id); + +extern blkid_partition blkid_partlist_add_partition(blkid_partlist ls, + blkid_parttable tab, + uint64_t start, uint64_t size); + +extern int blkid_partlist_set_partno(blkid_partlist ls, int partno); +extern int blkid_partlist_increment_partno(blkid_partlist ls); + +extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls); + +extern blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start); + +extern int blkid_partitions_do_subprobe(blkid_probe pr, + blkid_partition parent, const struct blkid_idinfo *id); + +extern int blkid_partitions_need_typeonly(blkid_probe pr); +extern int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid); +extern int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str); + + +extern int blkid_is_nested_dimension(blkid_partition par, + uint64_t start, uint64_t size); + +extern int blkid_partition_set_name(blkid_partition par, + const unsigned char *name, size_t len); + +extern int blkid_partition_set_utf8name(blkid_partition par, + const unsigned char *name, size_t len, int enc); + +extern int blkid_partition_set_uuid(blkid_partition par, + const unsigned char *uuid); +extern int blkid_partition_gen_uuid(blkid_partition par); + +extern int blkid_partition_set_type(blkid_partition par, int type); + +extern int blkid_partition_set_type_string(blkid_partition par, + const unsigned char *type, size_t len); + +extern int blkid_partition_set_type_uuid(blkid_partition par, + const unsigned char *uuid); + +extern int blkid_partition_set_flags(blkid_partition par, unsigned long long flags); + +/* + * partition probers + */ +extern const struct blkid_idinfo aix_pt_idinfo; +extern const struct blkid_idinfo bsd_pt_idinfo; +extern const struct blkid_idinfo unixware_pt_idinfo; +extern const struct blkid_idinfo solaris_x86_pt_idinfo; +extern const struct blkid_idinfo sun_pt_idinfo; +extern const struct blkid_idinfo sgi_pt_idinfo; +extern const struct blkid_idinfo mac_pt_idinfo; +extern const struct blkid_idinfo dos_pt_idinfo; +extern const struct blkid_idinfo minix_pt_idinfo; +extern const struct blkid_idinfo gpt_pt_idinfo; +extern const struct blkid_idinfo pmbr_pt_idinfo; +extern const struct blkid_idinfo ultrix_pt_idinfo; +extern const struct blkid_idinfo atari_pt_idinfo; + +#endif /* BLKID_PARTITIONS_H */ diff --git a/libblkid/src/partitions/sgi.c b/libblkid/src/partitions/sgi.c new file mode 100644 index 0000000..d4f81bb --- /dev/null +++ b/libblkid/src/partitions/sgi.c @@ -0,0 +1,87 @@ +/* + * sgi partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" +#include "pt-sgi.h" + +static int probe_sgi_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct sgi_disklabel *l; + struct sgi_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + int i; + + l = (struct sgi_disklabel *) blkid_probe_get_sector(pr, 0); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (!blkid_probe_verify_csum(pr, sgi_pt_checksum(l), 0)) { + DBG(LOWPROBE, ul_debug( + "detected corrupted sgi disk label -- ignore")); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sgi", 0); + if (!tab) + goto err; + + for(i = 0, p = &l->partitions[0]; i < SGI_MAXPARTITIONS; i++, p++) { + uint32_t size = be32_to_cpu(p->num_blocks); + uint32_t start = be32_to_cpu(p->first_block); + uint32_t type = be32_to_cpu(p->type); + blkid_partition par; + + if (!size) { + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, type); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo sgi_pt_idinfo = +{ + .name = "sgi", + .probefunc = probe_sgi_pt, + .magics = + { + { .magic = "\x0B\xE5\xA9\x41", .len = 4 }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/solaris_x86.c b/libblkid/src/partitions/solaris_x86.c new file mode 100644 index 0000000..df1def5 --- /dev/null +++ b/libblkid/src/partitions/solaris_x86.c @@ -0,0 +1,154 @@ +/* + * Solaris x86 partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +/* + * Solaris-x86 is always within primary dos partition (nested PT table). The + * solaris-x86 vtoc can be used to split the entire partition to "slices". The + * offset (start) of the slice is always relatively to the primary dos + * partition. + * + * Note that Solaris-SPARC uses entire disk with a different partitioning + * scheme. + */ + +/* some other implementation than Linux kernel assume 8 partitions only */ +#define SOLARIS_MAXPARTITIONS 16 + +/* disklabel (vtoc) location */ +#define SOLARIS_SECTOR 1 /* in 512-sectors */ +#define SOLARIS_OFFSET (SOLARIS_SECTOR << 9) /* in bytes */ +#define SOLARIS_MAGICOFFSET (SOLARIS_OFFSET + 12) /* v_sanity offset in bytes */ + +/* slice tags */ +#define SOLARIS_TAG_WHOLEDISK 5 + +struct solaris_slice { + uint16_t s_tag; /* ID tag of partition */ + uint16_t s_flag; /* permission flags */ + uint32_t s_start; /* start sector no of partition */ + uint32_t s_size; /* # of blocks in partition */ +} __attribute__((packed)); + +struct solaris_vtoc { + unsigned int v_bootinfo[3]; /* info needed by mboot (unsupported) */ + + uint32_t v_sanity; /* to verify vtoc sanity */ + uint32_t v_version; /* layout version */ + char v_volume[8]; /* volume name */ + uint16_t v_sectorsz; /* sector size in bytes */ + uint16_t v_nparts; /* number of partitions */ + unsigned int v_reserved[10]; /* free space */ + + struct solaris_slice v_slice[SOLARIS_MAXPARTITIONS]; /* slices */ + + unsigned int timestamp[SOLARIS_MAXPARTITIONS]; /* timestamp (unsupported) */ + char v_asciilabel[128]; /* for compatibility */ +} __attribute__((packed)); + +static int probe_solaris_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct solaris_vtoc *l; /* disk label */ + struct solaris_slice *p; /* partition */ + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i; + uint16_t nparts; + + l = (struct solaris_vtoc *) blkid_probe_get_sector(pr, SOLARIS_SECTOR); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (le32_to_cpu(l->v_version) != 1) { + DBG(LOWPROBE, ul_debug( + "WARNING: unsupported solaris x86 version %d, ignore", + le32_to_cpu(l->v_version))); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + parent = blkid_partlist_get_parent(ls); + + tab = blkid_partlist_new_parttable(ls, "solaris", SOLARIS_OFFSET); + if (!tab) + goto err; + + nparts = le16_to_cpu(l->v_nparts); + if (nparts > SOLARIS_MAXPARTITIONS) + nparts = SOLARIS_MAXPARTITIONS; + + for (i = 1, p = &l->v_slice[0]; i < nparts; i++, p++) { + + uint32_t start = le32_to_cpu(p->s_start); + uint32_t size = le32_to_cpu(p->s_size); + blkid_partition par; + + if (size == 0 || le16_to_cpu(p->s_tag) == SOLARIS_TAG_WHOLEDISK) + continue; + + if (parent) + /* Solaris slices are relative to the parent (primary + * DOS partition) */ + start += blkid_partition_get_start(parent); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: solaris partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, le16_to_cpu(p->s_tag)); + blkid_partition_set_flags(par, le16_to_cpu(p->s_flag)); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo solaris_x86_pt_idinfo = +{ + .name = "solaris", + .probefunc = probe_solaris_pt, + .magics = + { + { + .magic = "\xEE\xDE\x0D\x60", /* little-endian magic string */ + .len = 4, /* v_sanity size in bytes */ + .sboff = SOLARIS_MAGICOFFSET /* offset of v_sanity */ + }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/sun.c b/libblkid/src/partitions/sun.c new file mode 100644 index 0000000..ce24364 --- /dev/null +++ b/libblkid/src/partitions/sun.c @@ -0,0 +1,125 @@ +/* + * sun (solaris-sparc) partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> + +#include "pt-sun.h" +#include "partitions.h" + +static int probe_sun_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct sun_disklabel *l; + struct sun_partition *p; + blkid_parttable tab = NULL; + blkid_partlist ls; + uint16_t nparts; + uint64_t spc; + int i, use_vtoc; + + l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (!blkid_probe_verify_csum(pr, sun_pt_checksum(l), 0)) { + DBG(LOWPROBE, ul_debug( + "detected corrupted sun disk label -- ignore")); + goto nothing; + } + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "sun", 0); + if (!tab) + goto err; + + /* sectors per cylinder (partition offset is in cylinders...) */ + spc = (uint64_t) be16_to_cpu(l->nhead) * be16_to_cpu(l->nsect); + + DBG(LOWPROBE, ul_debug("Sun VTOC sanity=%u version=%u nparts=%u", + be32_to_cpu(l->vtoc.sanity), + be32_to_cpu(l->vtoc.version), + be16_to_cpu(l->vtoc.nparts))); + + /* Check to see if we can use the VTOC table */ + use_vtoc = ((be32_to_cpu(l->vtoc.sanity) == SUN_VTOC_SANITY) && + (be32_to_cpu(l->vtoc.version) == SUN_VTOC_VERSION) && + (be16_to_cpu(l->vtoc.nparts) <= SUN_MAXPARTITIONS)); + + /* Use 8 partition entries if not specified in validated VTOC */ + nparts = use_vtoc ? be16_to_cpu(l->vtoc.nparts) : SUN_MAXPARTITIONS; + + /* + * So that old Linux-Sun partitions continue to work, + * allow the VTOC to be used under the additional condition ... + */ + use_vtoc = use_vtoc || !(l->vtoc.sanity || l->vtoc.version || l->vtoc.nparts); + + for (i = 0, p = l->partitions; i < nparts; i++, p++) { + + uint64_t start, size; + uint16_t type = 0, flags = 0; + blkid_partition par; + + start = be32_to_cpu(p->start_cylinder) * spc; + size = be32_to_cpu(p->num_sectors); + if (use_vtoc) { + type = be16_to_cpu(l->vtoc.infos[i].id); + flags = be16_to_cpu(l->vtoc.infos[i].flags); + } + + if (type == SUN_TAG_WHOLEDISK || !size) { + blkid_partlist_increment_partno(ls); + continue; + } + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + if (type) + blkid_partition_set_type(par, type); + if (flags) + blkid_partition_set_flags(par, flags); + } + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + + +const struct blkid_idinfo sun_pt_idinfo = +{ + .name = "sun", + .probefunc = probe_sun_pt, + .magics = + { + { + .magic = "\xDA\xBE", /* big-endian magic string */ + .len = 2, + .sboff = offsetof(struct sun_disklabel, magic) + }, + { NULL } + } +}; + diff --git a/libblkid/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c new file mode 100644 index 0000000..9c060be --- /dev/null +++ b/libblkid/src/partitions/ultrix.c @@ -0,0 +1,99 @@ +/* + * uktrix partition parsing code + * + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +#define ULTRIX_MAXPARTITIONS 8 + +#define ULTRIX_MAGIC 0x032957 +#define ULTRIX_MAGIC_STR "\x02\x29\x57" + +/* sector with partition table */ +#define ULTRIX_SECTOR ((16384 - sizeof(struct ultrix_disklabel)) >> 9) +/* position of partition table within ULTRIX_SECTOR */ +#define ULTRIX_OFFSET (512 - sizeof(struct ultrix_disklabel)) + +struct ultrix_disklabel { + int32_t pt_magic; /* magic no. indicating part. info exits */ + int32_t pt_valid; /* set by driver if pt is current */ + struct pt_info { + int32_t pi_nblocks; /* no. of sectors */ + uint32_t pi_blkoff; /* block offset for start */ + } pt_part[ULTRIX_MAXPARTITIONS]; +} __attribute__((packed)); + + +static int probe_ultrix_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + unsigned char *data; + struct ultrix_disklabel *l; + blkid_parttable tab = NULL; + blkid_partlist ls; + int i; + + data = blkid_probe_get_sector(pr, ULTRIX_SECTOR); + if (!data) { + if (errno) + return -errno; + goto nothing; + } + + l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET); + + if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1) + goto nothing; + + if (blkid_probe_set_magic(pr, (ULTRIX_SECTOR << 9) + ULTRIX_OFFSET, + sizeof(ULTRIX_MAGIC_STR) - 1, + (unsigned char *) ULTRIX_MAGIC_STR)) + goto err; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "ultrix", 0); + if (!tab) + goto err; + + for (i = 0; i < ULTRIX_MAXPARTITIONS; i++) { + if (!l->pt_part[i].pi_nblocks) + blkid_partlist_increment_partno(ls); + else { + if (!blkid_partlist_add_partition(ls, tab, + l->pt_part[i].pi_blkoff, + l->pt_part[i].pi_nblocks)) + goto err; + } + } + + return BLKID_PROBE_OK; +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo ultrix_pt_idinfo = +{ + .name = "ultrix", + .probefunc = probe_ultrix_pt, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/partitions/unixware.c b/libblkid/src/partitions/unixware.c new file mode 100644 index 0000000..845924e --- /dev/null +++ b/libblkid/src/partitions/unixware.c @@ -0,0 +1,197 @@ +/* + * unixware partition parsing code + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * + * The interesting information about unixware PT: + * - Linux kernel / partx + * - vtoc(7) SCO UNIX command man page + * - evms source code (http://evms.sourceforge.net/) + * - vxtools source code (http://martin.hinner.info/fs/vxfs/) + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +/* disklabel location */ +#define UNIXWARE_SECTOR 29 +#define UNIXWARE_OFFSET (UNIXWARE_SECTOR << 9) /* offset in bytes */ +#define UNIXWARE_KBOFFSET (UNIXWARE_OFFSET >> 10) /* offset in 1024-blocks */ + +/* disklabel->d_magic offset within the last 1024 block */ +#define UNIXWARE_MAGICOFFSET (UNIXWARE_OFFSET - UNIXWARE_KBOFFSET + 4) + +#define UNIXWARE_VTOCMAGIC 0x600DDEEEUL +#define UNIXWARE_MAXPARTITIONS 16 + +/* unixware_partition->s_label flags */ +#define UNIXWARE_TAG_UNUSED 0x0000 /* unused partition */ +#define UNIXWARE_TAG_BOOT 0x0001 /* boot fs */ +#define UNIXWARE_TAG_ROOT 0x0002 /* root fs */ +#define UNIXWARE_TAG_SWAP 0x0003 /* swap fs */ +#define UNIXWARE_TAG_USER 0x0004 /* user fs */ +#define UNIXWARE_TAG_ENTIRE_DISK 0x0005 /* whole disk */ +#define UNIXWARE_TAG_ALT_S 0x0006 /* alternate sector space */ +#define UNIXWARE_TAG_OTHER 0x0007 /* non unix */ +#define UNIXWARE_TAG_ALT_T 0x0008 /* alternate track space */ +#define UNIXWARE_TAG_STAND 0x0009 /* stand partition */ +#define UNIXWARE_TAG_VAR 0x000a /* var partition */ +#define UNIXWARE_TAG_HOME 0x000b /* home partition */ +#define UNIXWARE_TAG_DUMP 0x000c /* dump partition */ +#define UNIXWARE_TAG_ALT_ST 0x000d /* alternate sector track */ +#define UNIXWARE_TAG_VM_PUBLIC 0x000e /* volume mgt public partition */ +#define UNIXWARE_TAG_VM_PRIVATE 0x000f /* volume mgt private partition */ + + +/* unixware_partition->s_flags flags */ +#define UNIXWARE_FLAG_VALID 0x0200 + +struct unixware_partition { + uint16_t s_label; /* partition label (tag) */ + uint16_t s_flags; /* permission flags */ + uint32_t start_sect; /* starting sector */ + uint32_t nr_sects; /* number of sectors */ +} __attribute__((packed)); + +struct unixware_disklabel { + uint32_t d_type; /* drive type */ + uint32_t d_magic; /* the magic number */ + uint32_t d_version; /* version number */ + char d_serial[12]; /* serial number of the device */ + uint32_t d_ncylinders; /* # of data cylinders per device */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_part_start; /* # of first sector of this partition */ + uint32_t d_unknown1[12]; /* ? */ + uint32_t d_alt_tbl; /* byte offset of alternate table */ + uint32_t d_alt_len; /* byte length of alternate table */ + uint32_t d_phys_cyl; /* # of physical cylinders per device */ + uint32_t d_phys_trk; /* # of physical tracks per cylinder */ + uint32_t d_phys_sec; /* # of physical sectors per track */ + uint32_t d_phys_bytes; /* # of physical bytes per sector */ + uint32_t d_unknown2; /* ? */ + uint32_t d_unknown3; /* ? */ + uint32_t d_pad[8]; /* pad */ + + struct unixware_vtoc { + uint32_t v_magic; /* the magic number */ + uint32_t v_version; /* version number */ + char v_name[8]; /* volume name */ + uint16_t v_nslices; /* # of partitions */ + uint16_t v_unknown1; /* ? */ + uint32_t v_reserved[10]; /* reserved */ + + struct unixware_partition + v_slice[UNIXWARE_MAXPARTITIONS]; /* partition */ + } __attribute__((packed)) vtoc; +}; + +static int probe_unixware_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct unixware_disklabel *l; + struct unixware_partition *p; + blkid_parttable tab = NULL; + blkid_partition parent; + blkid_partlist ls; + int i; + + l = (struct unixware_disklabel *) + blkid_probe_get_sector(pr, UNIXWARE_SECTOR); + if (!l) { + if (errno) + return -errno; + goto nothing; + } + + if (le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_VTOCMAGIC) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + parent = blkid_partlist_get_parent(ls); + + tab = blkid_partlist_new_parttable(ls, "unixware", UNIXWARE_OFFSET); + if (!tab) + goto err; + + /* Skip the first partition that describe whole disk + */ + for (i = 1, p = &l->vtoc.v_slice[1]; + i < UNIXWARE_MAXPARTITIONS; i++, p++) { + + uint32_t start, size; + uint16_t tag, flg; + blkid_partition par; + + tag = le16_to_cpu(p->s_label); + flg = le16_to_cpu(p->s_flags); + + if (tag == UNIXWARE_TAG_UNUSED || + tag == UNIXWARE_TAG_ENTIRE_DISK || + flg != UNIXWARE_FLAG_VALID) + continue; + + start = le32_to_cpu(p->start_sect); + size = le32_to_cpu(p->nr_sects); + + if (parent && !blkid_is_nested_dimension(parent, start, size)) { + DBG(LOWPROBE, ul_debug( + "WARNING: unixware partition (%d) overflow " + "detected, ignore", i)); + continue; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + goto err; + + blkid_partition_set_type(par, tag); + blkid_partition_set_flags(par, flg); + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + + +/* + * The unixware partition table is within primary DOS partition. The PT is + * located on 29 sector, PT magic string is d_magic member of 'struct + * unixware_disklabel'. + */ +const struct blkid_idinfo unixware_pt_idinfo = +{ + .name = "unixware", + .probefunc = probe_unixware_pt, + .minsz = 1024 * 1440 + 1, /* ignore floppies */ + .magics = + { + { + .magic = "\x0D\x60\xE5\xCA", /* little-endian magic string */ + .len = 4, /* d_magic size in bytes */ + .kboff = UNIXWARE_KBOFFSET, + .sboff = UNIXWARE_MAGICOFFSET + }, + { NULL } + } +}; + diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c new file mode 100644 index 0000000..fbf504a --- /dev/null +++ b/libblkid/src/probe.c @@ -0,0 +1,2413 @@ +/* + * 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 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). + * These filters are per-chain. Note that always when you touch the chain + * filter the current probing position is reset 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. + * + * 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. + */ + +/** + * 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) == BLKID_PROBE_OK) + * ... 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_LINUX_CDROM_H +#include <linux/cdrom.h> +#endif +#ifdef HAVE_LINUX_BLKZONED_H +#include <linux/blkzoned.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_LINUX_FD_H +#include <linux/fd.h> +#endif +#include <inttypes.h> +#include <stdint.h> +#include <stdarg.h> +#include <limits.h> +#ifdef HAVE_OPAL_GET_STATUS +#include <linux/sed-opal.h> +#endif + +#include "blkidP.h" +#include "all-io.h" +#include "sysfs.h" +#include "strutils.h" +#include "list.h" +#include "fileutils.h" + +/* + * 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_values(blkid_probe pr); + +/** + * blkid_new_probe: + * + * Returns: a pointer to the newly allocated probe struct or NULL in case of error. + */ +blkid_probe blkid_new_probe(void) +{ + int i; + blkid_probe pr; + + pr = calloc(1, sizeof(struct blkid_struct_probe)); + if (!pr) + return NULL; + + DBG(LOWPROBE, ul_debug("allocate a new probe")); + + /* 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); + INIT_LIST_HEAD(&pr->values); + INIT_LIST_HEAD(&pr->hints); + return pr; +} + +/* + * Clone @parent, the new clone shares all, but except: + * + * - probing result + * - buffers if another device (or offset) is set to the prober + */ +blkid_probe blkid_clone_probe(blkid_probe parent) +{ + blkid_probe pr; + + if (!parent) + return NULL; + + DBG(LOWPROBE, ul_debug("allocate a probe clone")); + + pr = blkid_new_probe(); + if (!pr) + return NULL; + + pr->fd = parent->fd; + pr->off = parent->off; + pr->size = parent->size; + pr->devno = parent->devno; + pr->disk_devno = parent->disk_devno; + pr->blkssz = parent->blkssz; + pr->flags = parent->flags; + pr->zone_size = parent->zone_size; + pr->parent = parent; + + pr->flags &= ~BLKID_FL_PRIVATE_FD; + + 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; + blkid_probe pr = NULL; + + fd = open(filename, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + 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_FL_PRIVATE_FD; + return pr; +err: + 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); + ch->fltr = NULL; + } + + if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0) + close(pr->fd); + blkid_probe_reset_buffers(pr); + blkid_probe_reset_values(pr); + blkid_probe_reset_hints(pr); + blkid_free_probe(pr->disk_probe); + + DBG(LOWPROBE, ul_debug("free probe")); + free(pr); +} + +void blkid_probe_free_value(struct blkid_prval *v) +{ + if (!v) + return; + + list_del(&v->prvals); + free(v->data); + + DBG(LOWPROBE, ul_debug(" free value %s", v->name)); + free(v); +} + +/* + * Removes chain values from probing result. + */ +void blkid_probe_chain_reset_values(blkid_probe pr, struct blkid_chain *chn) +{ + + struct list_head *p, *pnext; + + if (list_empty(&pr->values)) + return; + + DBG(LOWPROBE, ul_debug("Resetting %s values", chn->driver->name)); + + list_for_each_safe(p, pnext, &pr->values) { + struct blkid_prval *v = list_entry(p, + struct blkid_prval, prvals); + + if (v->chain == chn) + blkid_probe_free_value(v); + } +} + +static void blkid_probe_chain_reset_position(struct blkid_chain *chn) +{ + chn->idx = -1; +} + +/* + * Move chain values from probing result to @vals + */ +int blkid_probe_chain_save_values(blkid_probe pr, struct blkid_chain *chn, + struct list_head *vals) +{ + struct list_head *p, *pnext; + struct blkid_prval *v; + + DBG(LOWPROBE, ul_debug("saving %s values", chn->driver->name)); + + list_for_each_safe(p, pnext, &pr->values) { + + v = list_entry(p, struct blkid_prval, prvals); + if (v->chain != chn) + continue; + + list_del_init(&v->prvals); + list_add_tail(&v->prvals, vals); + } + return 0; +} + +/* + * Appends values from @vals to the probing result + */ +void blkid_probe_append_values_list(blkid_probe pr, struct list_head *vals) +{ + DBG(LOWPROBE, ul_debug("appending values")); + + list_splice(vals, &pr->values); + INIT_LIST_HEAD(vals); +} + + +void blkid_probe_free_values_list(struct list_head *vals) +{ + if (!vals) + return; + + DBG(LOWPROBE, ul_debug("freeing values list")); + + while (!list_empty(vals)) { + struct blkid_prval *v = list_entry(vals->next, struct blkid_prval, prvals); + blkid_probe_free_value(v); + } +} + +struct blkid_chain *blkid_probe_get_chain(blkid_probe pr) +{ + return pr->cur_chain; +} + +static const char *blkid_probe_get_probername(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (chn && chn->idx >= 0 && (unsigned)chn->idx < chn->driver->nidinfos) + return chn->driver->idinfos[chn->idx]->name; + + return NULL; +} + +void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn) +{ + int rc, org_prob_flags; + struct blkid_chain *org_chn; + + /* 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; + 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; + + DBG(LOWPROBE, ul_debug("returning %s binary data", 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; + + blkid_probe_reset_values(pr); + blkid_probe_set_wiper(pr, 0, 0); + + 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(LOWPROBE, ul_debug("%d: %s: %s", + 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 (chain < 0 || chain >= BLKID_NCHAINS) + return NULL; + + chn = &pr->chains[chain]; + + /* always when you touch the chain filter all indexes are reset 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) +{ + size_t i; + struct blkid_chain *chn; + + chn = &pr->chains[chain]; + + if (!chn->driver->has_fltr || !chn->fltr) + return -1; + + for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++) + chn->fltr[i] = ~chn->fltr[i]; + + DBG(LOWPROBE, ul_debug("probing filter inverted")); + /* 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; + size_t 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 (has) { + if (flag & BLKID_FLTR_NOTIN) + blkid_bmp_set_item(fltr, i); + } else if (flag & BLKID_FLTR_ONLYIN) + blkid_bmp_set_item(fltr, i); + } + + DBG(LOWPROBE, ul_debug("%s: a new probing type-filter initialized", + chn->driver->name)); + /* blkid_probe_dump_filter(pr, chain); */ + return 0; +} + +static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len) +{ + ssize_t ret; + struct blkid_bufinfo *bf = NULL; + + if (lseek(pr->fd, real_off, SEEK_SET) == (off_t) -1) { + errno = 0; + return NULL; + } + + /* someone trying to overflow some buffers? */ + if (len > ULONG_MAX - sizeof(struct blkid_bufinfo)) { + errno = ENOMEM; + return NULL; + } + + /* allocate info and space for data by one malloc call */ + bf = calloc(1, sizeof(struct blkid_bufinfo) + len); + if (!bf) { + errno = ENOMEM; + return NULL; + } + + bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo); + bf->len = len; + bf->off = real_off; + INIT_LIST_HEAD(&bf->bufs); + + DBG(LOWPROBE, ul_debug("\tread: off=%"PRIu64" len=%"PRIu64"", + real_off, len)); + + ret = read(pr->fd, bf->data, len); + if (ret != (ssize_t) len) { + DBG(LOWPROBE, ul_debug("\tread failed: %m")); + free(bf); + + /* I/O errors on CDROMs are non-fatal to work with hybrid + * audio+data disks */ + if (ret >= 0 || blkid_probe_is_cdrom(pr) || blkdid_probe_is_opal_locked(pr)) + errno = 0; +#ifdef HAVE_OPAL_GET_STATUS + else { + struct opal_status st = { }; + + /* If the device is locked with OPAL, we'll fail to read with I/O + * errors when probing deep into the block device. Do not return + * an error, so that we can move on to different types of checks.*/ + ret = ioctl(pr->fd, IOC_OPAL_GET_STATUS, &st); + if (ret == 0 && (st.flags & OPAL_FL_LOCKED)) { + pr->flags |= BLKID_FL_OPAL_LOCKED; + errno = 0; + } + } +#endif + + return NULL; + } + + return bf; +} + +/* + * Search in buffers we already have in memory + */ +static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uint64_t len) +{ + uint64_t real_off = pr->off + off; + struct list_head *p; + + list_for_each(p, &pr->buffers) { + struct blkid_bufinfo *x = + list_entry(p, struct blkid_bufinfo, bufs); + + if (real_off >= x->off && real_off + len <= x->off + x->len) { + DBG(BUFFER, ul_debug("\treuse: off=%"PRIu64" len=%"PRIu64" (for off=%"PRIu64" len=%"PRIu64")", + x->off, x->len, real_off, len)); + return x; + } + } + return NULL; +} + +/* + * Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer() + * will return modified buffer. This is usable when you want to call the same probing + * function more than once and hide previously detected magic strings. + * + * See blkid_probe_hide_range(). + */ +static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len) +{ + uint64_t real_off = pr->off + off; + struct list_head *p; + int ct = 0; + + if (UINT64_MAX - len < off) { + DBG(BUFFER, ul_debug("\t hide-buffer overflow (ignore)")); + return -EINVAL; + } + + list_for_each(p, &pr->buffers) { + struct blkid_bufinfo *x = + list_entry(p, struct blkid_bufinfo, bufs); + unsigned char *data; + + if (real_off >= x->off && real_off + len <= x->off + x->len) { + + assert(x->off <= real_off); + assert(x->off + x->len >= real_off + len); + + data = real_off ? x->data + (real_off - x->off) : x->data; + + DBG(BUFFER, ul_debug("\thiding: off=%"PRIu64" len=%"PRIu64, + off, len)); + memset(data, 0, len); + ct++; + } + } + return ct == 0 ? -EINVAL : 0; +} + + +/* + * Note that @off is offset within probing area, the probing area is defined by + * pr->off and pr->size. + */ +unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len) +{ + struct blkid_bufinfo *bf = NULL; + uint64_t real_off = pr->off + off; + + /* + DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju", + off, real_off, pr->off, pr->off + pr->size, len)); + */ + if (pr->size == 0) { + errno = EINVAL; + return NULL; + } + + if (UINT64_MAX - len < off || UINT64_MAX - len < real_off) { + DBG(BUFFER, ul_debug("\t read-buffer overflow (ignore)")); + return NULL; + } + + if (len == 0 + || (!S_ISCHR(pr->mode) && (pr->size < off || pr->size < len)) + || (!S_ISCHR(pr->mode) && (pr->off + pr->size < real_off + len))) { + DBG(BUFFER, ul_debug("\t read-buffer out of probing area (ignore)")); + errno = 0; + return NULL; + } + + if (pr->parent && + pr->parent->devno == pr->devno && + pr->parent->off <= pr->off && + pr->parent->off + pr->parent->size >= pr->off + pr->size) { + /* + * This is a cloned prober and points to the same area as + * parent. Let's use parent's buffers. + * + * Note that pr->off (and pr->parent->off) is always from the + * begin of the device. + */ + return blkid_probe_get_buffer(pr->parent, + pr->off + off - pr->parent->off, len); + } + + /* try buffers we already have in memory or read from device */ + bf = get_cached_buffer(pr, off, len); + if (!bf) { + bf = read_buffer(pr, real_off, len); + if (!bf) + return NULL; + + list_add_tail(&bf->bufs, &pr->buffers); + } + + assert(bf->off <= real_off); + assert(bf->off + bf->len >= real_off + len); + + errno = 0; + return real_off ? bf->data + (real_off - bf->off) : bf->data; +} + +/** + * blkid_probe_reset_buffers: + * @pr: prober + * + * libblkid reuse all already read buffers from the device. The buffers may be + * modified by blkid_probe_hide_range(). This function reset and free all + * cached buffers. The next blkid_do_probe() will read all data from the + * device. + * + * Since: 2.31 + * + * Returns: <0 in case of failure, or 0 on success. + */ +int blkid_probe_reset_buffers(blkid_probe pr) +{ + uint64_t ct = 0, len = 0; + + pr->flags &= ~BLKID_FL_MODIF_BUFF; + + if (list_empty(&pr->buffers)) + return 0; + + DBG(BUFFER, ul_debug("Resetting probing buffers")); + + while (!list_empty(&pr->buffers)) { + struct blkid_bufinfo *bf = list_entry(pr->buffers.next, + struct blkid_bufinfo, bufs); + ct++; + len += bf->len; + list_del(&bf->bufs); + + DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]", + bf->off, bf->len)); + free(bf); + } + + DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls", + len, ct)); + + INIT_LIST_HEAD(&pr->buffers); + + return 0; +} + +/** + * blkid_probe_hide_range: + * @pr: prober + * @off: start of the range + * @len: size of the range + * + * This function modifies in-memory cached data from the device. The specified + * range is zeroized. This is usable together with blkid_probe_step_back(). + * The next blkid_do_probe() will not see specified area. + * + * Note that this is usable for already (by library) read data, and this + * function is not a way how to hide any large areas on your device. + * + * The function blkid_probe_reset_buffers() reverts all. + * + * Since: 2.31 + * + * Returns: <0 in case of failure, or 0 on success. + */ +int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len) +{ + int rc = hide_buffer(pr, off, len); + + if (rc == 0) + pr->flags |= BLKID_FL_MODIF_BUFF; + return rc; +} + + +static void blkid_probe_reset_values(blkid_probe pr) +{ + if (list_empty(&pr->values)) + return; + + DBG(LOWPROBE, ul_debug("resetting results")); + + while (!list_empty(&pr->values)) { + struct blkid_prval *v = list_entry(pr->values.next, + struct blkid_prval, prvals); + blkid_probe_free_value(v); + } + + INIT_LIST_HEAD(&pr->values); +} + +/* + * Small devices need a special care. + */ +int blkid_probe_is_tiny(blkid_probe pr) +{ + return (pr->flags & BLKID_FL_TINY_DEV); +} + +/* + * CDROMs may fail when probed for RAID (last sector problem) + */ +int blkid_probe_is_cdrom(blkid_probe pr) +{ + return (pr->flags & BLKID_FL_CDROM_DEV); +} + +int blkdid_probe_is_opal_locked(blkid_probe pr) +{ + return (pr->flags & BLKID_FL_OPAL_LOCKED); +} + +#ifdef CDROM_GET_CAPABILITY + +static int is_sector_readable(int fd, uint64_t sector) +{ + char buf[512]; + ssize_t sz; + + if (lseek(fd, sector * 512, SEEK_SET) == (off_t) -1) + goto failed; + + sz = read(fd, buf, sizeof(buf)); + if (sz != (ssize_t) sizeof(buf)) + goto failed; + + return 1; +failed: + DBG(LOWPROBE, ul_debug("CDROM: read sector %"PRIu64" failed %m", sector)); + errno = 0; + return 0; +} + +/* + * Linux kernel reports (BLKGETSIZE) cdrom device size greater than area + * readable by read(2). We have to reduce the probing area to avoid unwanted + * I/O errors in probing functions. It seems that unreadable are always last 2 + * or 3 CD blocks (CD block size is 2048 bytes, it means 12 in 512-byte + * sectors). Linux kernel reports (CDROM_LAST_WRITTEN) also location of last + * written block, so we will reduce size based on it too. + */ +static void cdrom_size_correction(blkid_probe pr, uint64_t last_written) +{ + uint64_t n, nsectors = pr->size >> 9; + + if (last_written && nsectors > ((last_written+1) << 2)) + nsectors = (last_written+1) << 2; + + for (n = nsectors - 12; n < nsectors; n++) { + if (!is_sector_readable(pr->fd, n)) + goto failed; + } + + DBG(LOWPROBE, ul_debug("CDROM: full size available")); + return; +failed: + /* 'n' is the failed sector, reduce device size to n-1; */ + DBG(LOWPROBE, ul_debug("CDROM: reduce size from %ju to %ju.", + (uintmax_t) pr->size, + (uintmax_t) n << 9)); + pr->size = n << 9; +} + +#endif + +/** + * 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, resets + * the current probing, and close previously associated device (if open by + * libblkid). + * + * If @fd is < 0 than only resets the prober and returns 1. Note that + * blkid_reset_probe() keeps the device associated with the prober, but + * blkid_probe_set_device() does complete reset. + * + * Returns: -1 in case of failure, 0 on success and 1 on reset. + */ +int blkid_probe_set_device(blkid_probe pr, int fd, + blkid_loff_t off, blkid_loff_t size) +{ + struct stat sb; + uint64_t devsiz = 0; + char *dm_uuid = NULL; + int is_floppy = 0; + + blkid_reset_probe(pr); + blkid_probe_reset_buffers(pr); + + if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0) + close(pr->fd); + + if (pr->disk_probe) { + blkid_free_probe(pr->disk_probe); + pr->disk_probe = NULL; + } + + pr->flags &= ~BLKID_FL_PRIVATE_FD; + pr->flags &= ~BLKID_FL_TINY_DEV; + pr->flags &= ~BLKID_FL_CDROM_DEV; + pr->prob_flags = 0; + pr->fd = fd; + pr->off = (uint64_t) off; + pr->size = 0; + pr->devno = 0; + pr->disk_devno = 0; + pr->mode = 0; + pr->blkssz = 0; + pr->wipe_off = 0; + pr->wipe_size = 0; + pr->wipe_chain = NULL; + pr->zone_size = 0; + + if (fd < 0) + return 1; + + +#if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE) + /* Disable read-ahead */ + posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); +#endif + if (fstat(fd, &sb)) + goto err; + + if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode) && !S_ISREG(sb.st_mode)) { + errno = EINVAL; + goto err; + } + + pr->mode = sb.st_mode; + if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) + pr->devno = sb.st_rdev; + + if (S_ISBLK(sb.st_mode)) { + if (blkdev_get_size(fd, (unsigned long long *) &devsiz)) { + DBG(LOWPROBE, ul_debug("failed to get device size")); + goto err; + } + } else if (S_ISCHR(sb.st_mode)) { + char buf[PATH_MAX]; + + if (!sysfs_chrdev_devno_to_devname(sb.st_rdev, buf, sizeof(buf)) + || strncmp(buf, "ubi", 3) != 0) { + DBG(LOWPROBE, ul_debug("no UBI char device")); + errno = EINVAL; + goto err; + } + devsiz = 1; /* UBI devices are char... */ + } else if (S_ISREG(sb.st_mode)) + devsiz = sb.st_size; /* regular file */ + + pr->size = size ? (uint64_t)size : devsiz; + + if (off && size == 0) + /* only offset without size specified */ + pr->size -= (uint64_t) off; + + if (pr->off + pr->size > devsiz) { + DBG(LOWPROBE, ul_debug("area specified by offset and size is bigger than device")); + errno = EINVAL; + goto err; + } + + if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode)) + pr->flags |= BLKID_FL_TINY_DEV; + +#ifdef FDGETFDCSTAT + if (S_ISBLK(sb.st_mode)) { + /* + * Re-open without O_NONBLOCK for floppy device. + * + * Since kernel commit c7e9d0020361f4308a70cdfd6d5335e273eb8717 + * floppy drive works bad when opened with O_NONBLOCK. + */ + struct floppy_fdc_state flst; + + if (ioctl(fd, FDGETFDCSTAT, &flst) >= 0) { + int flags = fcntl(fd, F_GETFL, 0); + + if (flags < 0) + goto err; + if (flags & O_NONBLOCK) { + flags &= ~O_NONBLOCK; + + fd = ul_reopen(fd, flags | O_CLOEXEC); + if (fd < 0) + goto err; + + pr->flags |= BLKID_FL_PRIVATE_FD; + pr->fd = fd; + } + is_floppy = 1; + } + errno = 0; + } +#endif + if (S_ISBLK(sb.st_mode) && + !is_floppy && + sysfs_devno_is_dm_private(sb.st_rdev, &dm_uuid)) { + DBG(LOWPROBE, ul_debug("ignore private device mapper device")); + pr->flags |= BLKID_FL_NOSCAN_DEV; + } + +#ifdef CDROM_GET_CAPABILITY + else if (S_ISBLK(sb.st_mode) && + !blkid_probe_is_tiny(pr) && + !dm_uuid && + !is_floppy && + blkid_probe_is_wholedisk(pr)) { + + long last_written = 0; + + /* + * pktcdvd.ko accepts only these ioctls: + * CDROMEJECT CDROMMULTISESSION CDROMREADTOCENTRY + * CDROM_LAST_WRITTEN CDROM_SEND_PACKET SCSI_IOCTL_SEND_COMMAND + * So CDROM_GET_CAPABILITY cannot be used for detecting pktcdvd + * devices. But CDROM_GET_CAPABILITY and CDROM_DRIVE_STATUS are + * fast so use them for detecting if medium is present. In any + * case use last written block form CDROM_LAST_WRITTEN. + */ + + if (ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) { +# ifdef CDROM_DRIVE_STATUS + switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) { + case CDS_TRAY_OPEN: + case CDS_NO_DISC: + errno = ENOMEDIUM; + goto err; + } +# endif + pr->flags |= BLKID_FL_CDROM_DEV; + } + +# ifdef CDROM_LAST_WRITTEN + if (ioctl(fd, CDROM_LAST_WRITTEN, &last_written) == 0) { + pr->flags |= BLKID_FL_CDROM_DEV; + } else { + if (errno == ENOMEDIUM) + goto err; + } +# endif + + if (pr->flags & BLKID_FL_CDROM_DEV) { + cdrom_size_correction(pr, last_written); + +# ifdef CDROMMULTISESSION + if (!pr->off && blkid_probe_get_hint(pr, "session_offset", NULL) < 0) { + struct cdrom_multisession multisession = { .addr_format = CDROM_LBA }; + if (ioctl(fd, CDROMMULTISESSION, &multisession) == 0 && multisession.xa_flag) + blkid_probe_set_hint(pr, "session_offset", (multisession.addr.lba << 11)); + } +# endif + } + } +#endif + free(dm_uuid); + +# ifdef BLKGETZONESZ + if (S_ISBLK(sb.st_mode) && !is_floppy) { + uint32_t zone_size_sector; + + if (!ioctl(pr->fd, BLKGETZONESZ, &zone_size_sector)) + pr->zone_size = zone_size_sector << 9; + } +# endif + + DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64, + pr->off, pr->size, pr->zone_size)); + DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s", + blkid_probe_is_wholedisk(pr) ?"YES" : "NO", + S_ISREG(pr->mode) ? "YES" : "NO")); + + return 0; +err: + DBG(LOWPROBE, ul_debug("failed to prepare a device for low-probing")); + return -1; + +} + +int blkid_probe_get_dimension(blkid_probe pr, uint64_t *off, uint64_t *size) +{ + *off = pr->off; + *size = pr->size; + return 0; +} + +int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size) +{ + DBG(LOWPROBE, ul_debug( + "changing probing area: size=%"PRIu64", off=%"PRIu64" " + "-to-> size=%"PRIu64", off=%"PRIu64"", + pr->size, pr->off, size, off)); + + pr->off = off; + pr->size = size; + pr->flags &= ~BLKID_FL_TINY_DEV; + + if (pr->size <= 1440ULL * 1024ULL && !S_ISCHR(pr->mode)) + pr->flags |= BLKID_FL_TINY_DEV; + + blkid_probe_reset_buffers(pr); + + return 0; +} + +unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size) +{ + uint64_t hint_offset; + + if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0) + hint_offset = 0; + + return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size); +} + +/* + * Check for matching magic value. + * Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found + * or no magic present, or negative value on error. + */ +int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, + uint64_t *offset, const struct blkid_idmag **res) +{ + const struct blkid_idmag *mag = NULL; + uint64_t off = 0; + + if (id) + mag = &id->magics[0]; + if (res) + *res = NULL; + + /* try to detect by magic string */ + while(mag && mag->magic) { + unsigned char *buf; + uint64_t kboff; + uint64_t hint_offset; + + if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0) + hint_offset = 0; + + /* If the magic is for zoned device, skip non-zoned device */ + if (mag->is_zoned && !pr->zone_size) { + mag++; + continue; + } + + if (!mag->is_zoned) + kboff = mag->kboff; + else + kboff = ((mag->zonenum * pr->zone_size) >> 10) + mag->kboff_inzone; + + off = hint_offset + ((kboff + (mag->sboff >> 10)) << 10); + buf = blkid_probe_get_buffer(pr, off, 1024); + + if (!buf && errno) + return -errno; + + if (buf && !memcmp(mag->magic, + buf + (mag->sboff & 0x3ff), mag->len)) { + + DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%" PRIu64, + mag->sboff, kboff)); + if (offset) + *offset = off + (mag->sboff & 0x3ff); + if (res) + *res = mag; + return BLKID_PROBE_OK; + } + mag++; + } + + if (id && id->magics[0].magic) + /* magic string(s) defined, but not found */ + return BLKID_PROBE_NONE; + + return BLKID_PROBE_OK; +} + +static inline void blkid_probe_start(blkid_probe pr) +{ + DBG(LOWPROBE, ul_debug("start probe")); + pr->cur_chain = NULL; + pr->prob_flags = 0; + blkid_probe_set_wiper(pr, 0, 0); +} + +static inline void blkid_probe_end(blkid_probe pr) +{ + DBG(LOWPROBE, ul_debug("end probe")); + pr->cur_chain = NULL; + pr->prob_flags = 0; + blkid_probe_set_wiper(pr, 0, 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 reset + * 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) == BLKID_PROBE_OK) { + * 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) == BLKID_PROBE_OK) { + * 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->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + do { + struct blkid_chain *chn = pr->cur_chain; + + 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) or the current chain bailed out right at + * the start (chain->idx == -1) + */ + else if (rc == 1 && (chn->enabled == FALSE || + chn->idx + 1 == (int) chn->driver->nidinfos || + chn->idx == -1)) { + + size_t idx = chn->driver->id + 1; + + if (idx < BLKID_NCHAINS) + chn = pr->cur_chain = &pr->chains[idx]; + else { + blkid_probe_end(pr); + return BLKID_PROBE_NONE; /* all chains already probed */ + } + } + + chn->binary = FALSE; /* for sure... */ + + DBG(LOWPROBE, ul_debug("chain probe %s %s (idx=%d)", + 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 == BLKID_PROBE_NONE); + + if (rc < 0) + return BLKID_PROBE_ERROR; + + return rc; +} + +#ifdef HAVE_LINUX_BLKZONED_H +static int is_conventional(blkid_probe pr, uint64_t offset) +{ + struct blk_zone_report *rep = NULL; + int ret; + uint64_t zone_mask; + + if (!pr->zone_size) + return 1; + + zone_mask = ~(pr->zone_size - 1); + rep = blkdev_get_zonereport(blkid_probe_get_fd(pr), + (offset & zone_mask) >> 9, 1); + if (!rep) + return -1; + + if (rep->zones[0].type == BLK_ZONE_TYPE_CONVENTIONAL) + ret = 1; + else + ret = 0; + + free(rep); + + return ret; +} +#else +static inline int is_conventional(blkid_probe pr __attribute__((__unused__)), + uint64_t offset __attribute__((__unused__))) +{ + return 1; +} +#endif + +/** + * blkid_do_wipe: + * @pr: prober + * @dryrun: if TRUE then don't touch the device. + * + * This function erases the current signature detected by @pr. The @pr has to + * be open in O_RDWR mode, BLKID_SUBLKS_MAGIC or/and BLKID_PARTS_MAGIC flags + * has to be enabled (if you want to erase also superblock with broken check + * sums then use BLKID_SUBLKS_BADCSUM too). + * + * After successful signature removing the @pr prober will be moved one step + * back and the next blkid_do_probe() call will again call previously called + * probing function. All in-memory cached data from the device are always + * reset. + * + * <example> + * <title>wipe all filesystems or raids from the device</title> + * <programlisting> + * fd = open(devname, O_RDWR|O_CLOEXEC); + * blkid_probe_set_device(pr, fd, 0, 0); + * + * blkid_probe_enable_superblocks(pr, 1); + * blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC); + * + * while (blkid_do_probe(pr) == 0) + * blkid_do_wipe(pr, FALSE); + * </programlisting> + * </example> + * + * See also blkid_probe_step_back() if you cannot use this built-in wipe + * function, but you want to use libblkid probing as a source for wiping. + * + * Returns: 0 on success, and -1 in case of error. + */ +int blkid_do_wipe(blkid_probe pr, int dryrun) +{ + const char *off = NULL; + size_t len = 0; + uint64_t offset, magoff; + int conventional; + char buf[BUFSIZ]; + int fd, rc = 0; + struct blkid_chain *chn; + + chn = pr->cur_chain; + if (!chn) + return BLKID_PROBE_ERROR; + + switch (chn->driver->id) { + case BLKID_CHAIN_SUBLKS: + rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); + if (!rc) + rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); + break; + case BLKID_CHAIN_PARTS: + rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL); + if (!rc) + rc = blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len); + break; + default: + return BLKID_PROBE_OK; + } + + if (rc || len == 0 || off == NULL) + return BLKID_PROBE_OK; + + errno = 0; + magoff = strtoumax(off, NULL, 10); + if (errno) + return BLKID_PROBE_OK; + + offset = magoff + pr->off; + fd = blkid_probe_get_fd(pr); + if (fd < 0) + return BLKID_PROBE_ERROR; + + if (len > sizeof(buf)) + len = sizeof(buf); + + rc = is_conventional(pr, offset); + if (rc < 0) + return BLKID_PROBE_ERROR; + conventional = rc == 1; + + DBG(LOWPROBE, ul_debug( + "do_wipe [offset=0x%"PRIx64" (%"PRIu64"), len=%zu, chain=%s, idx=%d, dryrun=%s]\n", + offset, offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not")); + + if (lseek(fd, offset, SEEK_SET) == (off_t) -1) + return BLKID_PROBE_ERROR; + + if (!dryrun && len) { + if (conventional) { + memset(buf, 0, len); + + /* wipen on device */ + if (write_all(fd, buf, len)) + return BLKID_PROBE_ERROR; + if (fsync(fd) != 0) + return BLKID_PROBE_ERROR; + } else { +#ifdef HAVE_LINUX_BLKZONED_H + uint64_t zone_mask = ~(pr->zone_size - 1); + struct blk_zone_range range = { + .sector = (offset & zone_mask) >> 9, + .nr_sectors = pr->zone_size >> 9, + }; + + rc = ioctl(fd, BLKRESETZONE, &range); + if (rc < 0) + return BLKID_PROBE_ERROR; +#else + /* Should not reach here */ + assert(0); +#endif + } + + pr->flags &= ~BLKID_FL_MODIF_BUFF; /* be paranoid */ + + return blkid_probe_step_back(pr); + + } + + if (dryrun) { + /* wipe in memory only */ + blkid_probe_hide_range(pr, magoff, len); + return blkid_probe_step_back(pr); + } + + return BLKID_PROBE_OK; +} + +/** + * blkid_probe_step_back: + * @pr: prober + * + * This function move pointer to the probing chain one step back -- it means + * that the previously used probing function will be called again in the next + * blkid_do_probe() call. + * + * This is necessary for example if you erase or modify on-disk superblock + * according to the current libblkid probing result. + * + * Note that blkid_probe_hide_range() changes semantic of this function and + * cached buffers are not reset, but library uses in-memory modified + * buffers to call the next probing function. + * + * <example> + * <title>wipe all superblock, but use libblkid only for probing</title> + * <programlisting> + * pr = blkid_new_probe_from_filename(devname); + * + * blkid_probe_enable_superblocks(pr, 1); + * blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC); + * + * blkid_probe_enable_partitions(pr, 1); + * blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC); + * + * while (blkid_do_probe(pr) == BLKID_PROBE_OK) { + * const char *ostr = NULL; + * size_t len = 0; + * + * // superblocks + * if (blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &ostr, NULL) == 0) + * blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); + * + * // partition tables + * if (len == 0 && blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &ostr, NULL) == 0) + * blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len); + * + * if (!len || !str) + * continue; + * + * // convert ostr to the real offset by off = strtoll(ostr, NULL, 10); + * // use your stuff to erase @len bytes at the @off + * .... + * + * // retry the last probing to check for backup superblocks ..etc. + * blkid_probe_step_back(pr); + * } + * </programlisting> + * </example> + * + * Returns: 0 on success, and -1 in case of error. + */ +int blkid_probe_step_back(blkid_probe pr) +{ + struct blkid_chain *chn; + + chn = pr->cur_chain; + if (!chn) + return -1; + + if (!(pr->flags & BLKID_FL_MODIF_BUFF)) + blkid_probe_reset_buffers(pr); + + if (chn->idx >= 0) { + chn->idx--; + DBG(LOWPROBE, ul_debug("step back: moving %s chain index to %d", + chn->driver->name, + chn->idx)); + } + + if (chn->idx == -1) { + /* blkid_do_probe() goes to the next chain if the index + * of the current chain is -1, so we have to set the + * chain pointer to the previous chain. + */ + size_t idx = chn->driver->id > 0 ? chn->driver->id - 1 : 0; + + DBG(LOWPROBE, ul_debug("step back: moving to previous chain")); + + if (idx > 0) + pr->cur_chain = &pr->chains[idx]; + else if (idx == 0) + pr->cur_chain = NULL; + } + + return 0; +} + +/** + * 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 superblocks 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. 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 ambivalent result is + * detected and -1 on case of error. + */ +int blkid_do_safeprobe(blkid_probe pr) +{ + int i, count = 0, rc = 0; + + if (pr->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + blkid_probe_start(pr); + + for (i = 0; i < BLKID_NCHAINS; i++) { + struct blkid_chain *chn; + + chn = pr->cur_chain = &pr->chains[i]; + chn->binary = FALSE; /* for sure... */ + + DBG(LOWPROBE, ul_debug("chain safeprobe %s %s", + 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: + blkid_probe_end(pr); + if (rc < 0) + return BLKID_PROBE_ERROR; + + return count == 0 ? BLKID_PROBE_NONE : BLKID_PROBE_OK; +} + +/** + * blkid_do_fullprobe: + * @pr: prober + * + * This function gathers probing results from all enabled chains. Same as + * blkid_do_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->flags & BLKID_FL_NOSCAN_DEV) + return BLKID_PROBE_NONE; + + blkid_probe_start(pr); + + for (i = 0; i < BLKID_NCHAINS; i++) { + struct blkid_chain *chn; + + chn = pr->cur_chain = &pr->chains[i]; + chn->binary = FALSE; /* for sure... */ + + DBG(LOWPROBE, ul_debug("chain fullprobe %s: %s", + 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: + blkid_probe_end(pr); + if (rc < 0) + return BLKID_PROBE_ERROR; + + return count == 0 ? BLKID_PROBE_NONE : BLKID_PROBE_OK; +} + +/* same sa blkid_probe_get_buffer() but works with 512-sectors */ +unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) +{ + return blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200); +} + +struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, const char *name) +{ + struct blkid_prval *v; + + v = calloc(1, sizeof(struct blkid_prval)); + if (!v) + return NULL; + + INIT_LIST_HEAD(&v->prvals); + v->name = name; + v->chain = pr->cur_chain; + list_add_tail(&v->prvals, &pr->values); + + DBG(LOWPROBE, ul_debug("assigning %s [%s]", name, v->chain->driver->name)); + return v; +} + +/* Note that value data is always terminated by zero to keep things robust, + * this extra zero is not count to the value length. It's caller responsibility + * to set proper value length (for strings we count terminator to the length, + * for binary data it's without terminator). + */ +int blkid_probe_value_set_data(struct blkid_prval *v, + const unsigned char *data, size_t len) +{ + v->data = calloc(1, len + 1); /* always terminate by \0 */ + + if (!v->data) + return -ENOMEM; + memcpy(v->data, data, len); + v->len = len; + return 0; +} + +int blkid_probe_set_value(blkid_probe pr, const char *name, + const unsigned char *data, size_t len) +{ + struct blkid_prval *v; + + v = blkid_probe_assign_value(pr, name); + if (!v) + return -1; + + return blkid_probe_value_set_data(v, data, len); +} + +int blkid_probe_vsprintf_value(blkid_probe pr, const char *name, + const char *fmt, va_list ap) +{ + struct blkid_prval *v; + ssize_t len; + + v = blkid_probe_assign_value(pr, name); + if (!v) + return -ENOMEM; + + len = vasprintf((char **) &v->data, fmt, ap); + + if (len <= 0) { + blkid_probe_free_value(v); + return len == 0 ? -EINVAL : -ENOMEM; + } + 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; +} + +int blkid_probe_set_magic(blkid_probe pr, uint64_t offset, + size_t len, const unsigned char *magic) +{ + int rc = 0; + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!chn || !len || chn->binary) + return 0; + + switch (chn->driver->id) { + case BLKID_CHAIN_SUBLKS: + if (!(chn->flags & BLKID_SUBLKS_MAGIC)) + return 0; + rc = blkid_probe_set_value(pr, "SBMAGIC", magic, len); + if (!rc) + rc = blkid_probe_sprintf_value(pr, + "SBMAGIC_OFFSET", "%llu", (unsigned long long)offset); + break; + case BLKID_CHAIN_PARTS: + if (!(chn->flags & BLKID_PARTS_MAGIC)) + return 0; + rc = blkid_probe_set_value(pr, "PTMAGIC", magic, len); + if (!rc) + rc = blkid_probe_sprintf_value(pr, + "PTMAGIC_OFFSET", "%llu", (unsigned long long)offset); + break; + default: + break; + } + + return rc; +} + +static void blkid_probe_log_csum_mismatch(blkid_probe pr, size_t n, const void *csum, + const void *expected) +{ + char csum_hex[256]; + char expected_hex[sizeof(csum_hex)]; + int hex_size = min(sizeof(csum_hex), n * 2); + + for (int i = 0; i < hex_size; i+=2) { + sprintf(&csum_hex[i], "%02X", ((const unsigned char *) csum)[i / 2]); + sprintf(&expected_hex[i], "%02X", ((const unsigned char *) expected)[i / 2]); + } + + ul_debug( + "incorrect checksum for type %s," + " got %*s, expected %*s", + blkid_probe_get_probername(pr), + hex_size, csum_hex, hex_size, expected_hex); +} + + +int blkid_probe_verify_csum_buf(blkid_probe pr, size_t n, const void *csum, + const void *expected) +{ + if (memcmp(csum, expected, n) != 0) { + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + ON_DBG(LOWPROBE, blkid_probe_log_csum_mismatch(pr, n, csum, expected)); + + /* + * Accept bad checksum if BLKID_SUBLKS_BADCSUM flags is set + */ + if (chn && chn->driver->id == BLKID_CHAIN_SUBLKS + && (chn->flags & BLKID_SUBLKS_BADCSUM)) { + blkid_probe_set_value(pr, "SBBADCSUM", (unsigned char *) "1", 2); + goto accept; + } + return 0; /* bad checksum */ + } + +accept: + return 1; +} + +int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected) +{ + return blkid_probe_verify_csum_buf(pr, sizeof(csum), &csum, &expected); +} + +/** + * blkid_probe_get_devno: + * @pr: probe + * + * Returns: block device number, or 0 for regular files. + */ +dev_t blkid_probe_get_devno(blkid_probe pr) +{ + return pr->devno; +} + +/** + * blkid_probe_get_wholedisk_devno: + * @pr: probe + * + * Returns: device number of the wholedisk, or 0 for regular 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 (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0) + pr->disk_devno = disk_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; +} + +blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr) +{ + dev_t disk; + + if (blkid_probe_is_wholedisk(pr)) + return NULL; /* this is not partition */ + + if (pr->parent) + /* this is cloned blkid_probe, use parent's stuff */ + return blkid_probe_get_wholedisk_probe(pr->parent); + + disk = blkid_probe_get_wholedisk_devno(pr); + + if (pr->disk_probe && pr->disk_probe->devno != disk) { + /* we have disk prober, but for another disk... close it */ + blkid_free_probe(pr->disk_probe); + pr->disk_probe = NULL; + } + + if (!pr->disk_probe) { + /* Open a new disk prober */ + char *disk_path = blkid_devno_to_devname(disk); + int flags; + + if (!disk_path) + return NULL; + + DBG(LOWPROBE, ul_debug("allocate a wholedisk probe")); + + pr->disk_probe = blkid_new_probe_from_filename(disk_path); + + free(disk_path); + + if (!pr->disk_probe) + return NULL; /* ENOMEM? */ + + flags = blkid_probe_get_partitions_flags(pr); + if (flags & BLKID_PARTS_FORCE_GPT) + blkid_probe_set_partitions_flags(pr->disk_probe, + BLKID_PARTS_FORCE_GPT); + } + + return pr->disk_probe; +} + +/** + * blkid_probe_get_size: + * @pr: probe + * + * This function returns size of probing area as defined by blkid_probe_set_device(). + * If the size of the probing area is unrestricted then this function returns + * the real size of device. See also blkid_get_dev_size(). + * + * Returns: size in bytes or -1 in case of error. + */ +blkid_loff_t blkid_probe_get_size(blkid_probe pr) +{ + return (blkid_loff_t) pr->size; +} + +/** + * blkid_probe_get_offset: + * @pr: probe + * + * This function returns offset of probing area as defined by blkid_probe_set_device(). + * + * Returns: offset in bytes or -1 in case of error. + */ +blkid_loff_t blkid_probe_get_offset(blkid_probe pr) +{ + return (blkid_loff_t) pr->off; +} + +/** + * blkid_probe_get_fd: + * @pr: probe + * + * Returns: file descriptor for assigned device/file or -1 in case of error. + */ +int blkid_probe_get_fd(blkid_probe pr) +{ + return pr->fd; +} + +/** + * blkid_probe_get_sectorsize: + * @pr: probe or NULL (for NULL returns 512) + * + * Returns: block device logical sector size (BLKSSZGET ioctl, default 512). + */ +unsigned int blkid_probe_get_sectorsize(blkid_probe pr) +{ + if (pr->blkssz) + return pr->blkssz; + + if (S_ISBLK(pr->mode) && + blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0) + return pr->blkssz; + + pr->blkssz = DEFAULT_SECTOR_SIZE; + return pr->blkssz; +} + +/** + * blkid_probe_set_sectorsize: + * @pr: probe + * @sz: new size (to overwrite system default) + * + * Note that blkid_probe_set_device() resets this setting. Use it after + * blkid_probe_set_device() and before any probing call. + * + * Since: 2.30 + * + * Returns: 0 or <0 in case of error + */ +int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz) +{ + pr->blkssz = sz; + return 0; +} + +/** + * 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 (blkid_loff_t) (pr->size >> 9); +} + +/** + * 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) +{ + int i = 0; + struct list_head *p; + + list_for_each(p, &pr->values) + ++i; + return i; +} + +/** + * 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(LOWPROBE, ul_debug("returning %s value", 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) +{ + int i = 0; + struct list_head *p; + + if (num < 0) + return NULL; + + list_for_each(p, &pr->values) { + if (i++ != num) + continue; + return list_entry(p, struct blkid_prval, prvals); + } + return NULL; +} + +struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name) +{ + struct list_head *p; + + if (list_empty(&pr->values)) + return NULL; + + list_for_each(p, &pr->values) { + struct blkid_prval *v = list_entry(p, struct blkid_prval, + prvals); + + if (v->name && strcmp(name, v->name) == 0) { + DBG(LOWPROBE, ul_debug("returning %s value", 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) +{ + 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]); +} + +/* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */ +int blkid_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; +} + +/* 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) +{ + return rtrim_whitespace(str); +} + +/* Removes whitespace from the left-hand side of a string. + * + * Returns size of the new string (without \0). + */ +size_t blkid_ltrim_whitespace(unsigned char *str) +{ + return ltrim_whitespace(str); +} + +/* + * 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) within the + * 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" + * can be used 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). + * + * Note that we usually expect only one signature on the device, it means that + * we have to remember only one wiped area from previously successfully + * detected signature. + * + * blkid_probe_set_wiper() -- defines wiped area (e.g. LVM) + * blkid_probe_use_wiper() -- try to use area (e.g. MBR) + * + * Note that there is not relation between _wiper and blkid_to_wipe(). + * + */ +void blkid_probe_set_wiper(blkid_probe pr, uint64_t off, uint64_t size) +{ + struct blkid_chain *chn; + + if (!size) { + DBG(LOWPROBE, ul_debug("zeroize wiper")); + pr->wipe_size = pr->wipe_off = 0; + pr->wipe_chain = NULL; + return; + } + + chn = pr->cur_chain; + + if (!chn || !chn->driver || + chn->idx < 0 || (size_t) chn->idx >= chn->driver->nidinfos) + return; + + pr->wipe_size = size; + pr->wipe_off = off; + pr->wipe_chain = chn; + + DBG(LOWPROBE, + ul_debug("wiper set to %s::%s off=%"PRIu64" size=%"PRIu64"", + chn->driver->name, + chn->driver->idinfos[chn->idx]->name, + pr->wipe_off, pr->wipe_size)); +} + +/* + * Returns 1 if the <@off,@size> area was wiped + */ +int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn, uint64_t off, uint64_t size) +{ + if (!size) + return 0; + + if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) { + *chn = pr->wipe_chain; + return 1; + } + return 0; +} + +/* + * Try to use any area -- if the area has been previously wiped then the + * previous probing result should be ignored (reset). + */ +void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size) +{ + struct blkid_chain *chn = NULL; + + if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) { + DBG(LOWPROBE, ul_debug("previously wiped area modified " + " -- ignore previous results")); + blkid_probe_set_wiper(pr, 0, 0); + blkid_probe_chain_reset_values(pr, chn); + } +} + +static struct blkid_hint *get_hint(blkid_probe pr, const char *name) +{ + struct list_head *p; + + if (list_empty(&pr->hints)) + return NULL; + + list_for_each(p, &pr->hints) { + struct blkid_hint *h = list_entry(p, struct blkid_hint, hints); + + if (h->name && strcmp(name, h->name) == 0) + return h; + } + return NULL; +} + +/** + * blkid_probe_set_hint: + * @pr: probe + * @name: hint name or NAME=value + * @value: offset or another number + * + * Sets extra hint for low-level prober. If the hint is set by NAME=value + * notation than @value is ignored. The functions blkid_probe_set_device() + * and blkid_reset_probe() resets all hints. + * + * The hints are optional way how to force libblkid probing functions to check + * for example another location. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value) +{ + struct blkid_hint *hint = NULL; + char *n = NULL, *v = NULL; + + if (strchr(name, '=')) { + char *end = NULL; + + if (blkid_parse_tag_string(name, &n, &v) != 0) + goto done; + + errno = 0; + value = strtoumax(v, &end, 10); + + if (errno || v == end || (end && *end)) + goto done; + } + + hint = get_hint(pr, n ? n : name); + if (hint) { + /* alter old hint */ + hint->value = value; + DBG(LOWPROBE, + ul_debug("updated hint '%s' to %"PRIu64"", hint->name, hint->value)); + } else { + /* add a new hint */ + if (!n) { + n = strdup(name); + if (!n) + goto done; + } + hint = malloc(sizeof(*hint)); + if (!hint) + goto done; + + hint->name = n; + hint->value = value; + + INIT_LIST_HEAD(&hint->hints); + list_add_tail(&hint->hints, &pr->hints); + + DBG(LOWPROBE, + ul_debug("new hint '%s' is %"PRIu64"", hint->name, hint->value)); + n = NULL; + } +done: + free(n); + free(v); + + if (!hint) + return errno ? -errno : -EINVAL; + return 0; +} + +int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value) +{ + struct blkid_hint *h = get_hint(pr, name); + + if (!h) + return -EINVAL; + if (value) + *value = h->value; + return 0; +} + +/** + * blkid_probe_reset_hints: + * @pr: probe + * + * Removes all previously defined probinig hints. See also blkid_probe_set_hint(). + */ +void blkid_probe_reset_hints(blkid_probe pr) +{ + if (list_empty(&pr->hints)) + return; + + DBG(LOWPROBE, ul_debug("resetting hints")); + + while (!list_empty(&pr->hints)) { + struct blkid_hint *h = list_entry(pr->hints.next, + struct blkid_hint, hints); + list_del(&h->hints); + free(h->name); + free(h); + } + + INIT_LIST_HEAD(&pr->hints); +} diff --git a/libblkid/src/read.c b/libblkid/src/read.c new file mode 100644 index 0000000..d62edfa --- /dev/null +++ b/libblkid/src/read.c @@ -0,0 +1,455 @@ +/* + * read.c - read the blkid cache from disk, to avoid scanning all devices + * + * Copyright (C) 2001, 2003 Theodore Y. Ts'o + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "blkidP.h" + +#ifdef HAVE_STDLIB_H +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 600 /* for inclusion of strtoull */ +# endif +# include <stdlib.h> +#endif + +/* + * File format: + * + * <device [<NAME="value"> ...]>device_name</device> + * + * The following tags are required for each entry: + * <ID="id"> unique (within this file) ID number of this device + * <TIME="sec.usec"> (time_t and suseconds_t) time this entry was last + * read from disk + * <TYPE="type"> (detected) type of filesystem/data for this partition + * + * The following tags may be present, depending on the device contents + * <LABEL="label"> (user supplied) label (volume name, etc) + * <UUID="uuid"> (generated) universally unique identifier (serial no) + */ + +static char *skip_over_blank(char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + return cp; +} + +static char *skip_over_word(char *cp) +{ + char ch; + + while ((ch = *cp)) { + /* If we see a backslash, skip the next character */ + if (ch == '\\') { + cp++; + if (*cp == '\0') + break; + cp++; + continue; + } + if (isspace(ch) || ch == '<' || ch == '>') + break; + cp++; + } + return cp; +} + +static char *strip_line(char *line) +{ + char *p; + + line = skip_over_blank(line); + + p = line + strlen(line) - 1; + + while (*line) { + if (isspace(*p)) + *p-- = '\0'; + else + break; + } + + return line; +} + +#if 0 +static char *parse_word(char **buf) +{ + char *word, *next; + + word = *buf; + if (*word == '\0') + return NULL; + + word = skip_over_blank(word); + next = skip_over_word(word); + if (*next) { + char *end = next - 1; + if (*end == '"' || *end == '\'') + *end = '\0'; + *next++ = '\0'; + } + *buf = next; + + if (*word == '"' || *word == '\'') + word++; + return word; +} +#endif + +/* + * Start parsing a new line from the cache. + * + * line starts with "<device" return 1 -> continue parsing line + * line starts with "<foo", empty, or # return 0 -> skip line + * line starts with other, return -BLKID_ERR_CACHE -> error + */ +static int parse_start(char **cp) +{ + char *p; + + p = strip_line(*cp); + + /* Skip comment or blank lines. We can't just NUL the first '#' char, + * in case it is inside quotes, or escaped. + */ + if (*p == '\0' || *p == '#') + return 0; + + if (!strncmp(p, "<device", 7)) { + DBG(READ, ul_debug("found device header: %8s", p)); + p += 7; + + *cp = p; + return 1; + } + + if (*p == '<') + return 0; + + return -BLKID_ERR_CACHE; +} + +/* Consume the remaining XML on the line (cosmetic only) */ +static int parse_end(char **cp) +{ + *cp = skip_over_blank(*cp); + + if (!strncmp(*cp, "</device>", 9)) { + DBG(READ, ul_debug("found device trailer %9s", *cp)); + *cp += 9; + return 0; + } + + return -BLKID_ERR_CACHE; +} + +/* + * Allocate a new device struct with device name filled in. Will handle + * finding the device on lines of the form: + * <device foo=bar>devname</device> + * <device>devname<foo>bar</foo></device> + */ +static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp) +{ + char *start, *tmp, *end, *name; + int ret; + + if ((ret = parse_start(cp)) <= 0) + return ret; + + start = tmp = strchr(*cp, '>'); + if (!start) { + DBG(READ, ul_debug("blkid: short line parsing dev: %s", *cp)); + return -BLKID_ERR_CACHE; + } + start = skip_over_blank(start + 1); + end = skip_over_word(start); + + DBG(READ, ul_debug("device should be %*s", + (int)(end - start), start)); + + if (**cp == '>') + *cp = end; + else + (*cp)++; + + *tmp = '\0'; + + if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) { + DBG(READ, ul_debug("blkid: missing </device> ending: %s", end)); + } else if (tmp) + *tmp = '\0'; + + if (end - start <= 1) { + DBG(READ, ul_debug("blkid: empty device name: %s", *cp)); + return -BLKID_ERR_CACHE; + } + + name = strndup(start, end - start); + if (name == NULL) + return -BLKID_ERR_MEM; + + DBG(READ, ul_debug("found dev %s", name)); + + if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) { + free(name); + return -BLKID_ERR_MEM; + } + + free(name); + return 1; +} + +/* + * Extract a tag of the form NAME="value" from the line. + */ +static int parse_token(char **name, char **value, char **cp) +{ + char *end; + + if (!name || !value || !cp) + return -BLKID_ERR_PARAM; + + if (!(*value = strchr(*cp, '='))) + return 0; + + **value = '\0'; + *name = strip_line(*cp); + *value = skip_over_blank(*value + 1); + + if (**value == '"') { + char *p = end = *value + 1; + + /* convert 'foo\"bar' to 'foo"bar' */ + while (*p) { + if (*p == '\\') { + p++; + *end = *p; + } else { + *end = *p; + if (*p == '"') + break; + } + p++; + end++; + } + + if (*end != '"') { + DBG(READ, ul_debug("unbalanced quotes at: %s", *value)); + *cp = *value; + return -BLKID_ERR_CACHE; + } + (*value)++; + *end = '\0'; + end = ++p; + } else { + end = skip_over_word(*value); + if (*end) { + *end = '\0'; + end++; + } + } + *cp = end; + + return 1; +} + +/* + * Extract a tag from the line. + * + * Return 1 if a valid tag was found. + * Return 0 if no tag found. + * Return -ve error code. + */ +static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) +{ + char *name = NULL; + char *value = NULL; + int ret; + + if (!cache || !dev) + return -BLKID_ERR_PARAM; + + if ((ret = parse_token(&name, &value, cp)) <= 0) + return ret; + + DBG(READ, ul_debug("tag: %s=\"%s\"", name, value)); + + errno = 0; + + /* Some tags are stored directly in the device struct */ + if (!strcmp(name, "DEVNO")) { + dev->bid_devno = strtoull(value, NULL, 0); + if (errno) + return -errno; + } else if (!strcmp(name, "PRI")) { + dev->bid_pri = strtol(value, NULL, 0); + if (errno) + return -errno; + } else if (!strcmp(name, "TIME")) { + char *end = NULL; + + dev->bid_time = strtoull(value, &end, 0); + if (errno == 0 && end && *end == '.') + dev->bid_utime = strtoull(end + 1, NULL, 0); + if (errno) + return -errno; + } else + ret = blkid_set_tag(dev, name, value, strlen(value)); + + return ret < 0 ? ret : 1; +} + +/* + * Parse a single line of data, and return a newly allocated dev struct. + * Add the new device to the cache struct, if one was read. + * + * Lines are of the form <device [TAG="value" ...]>/dev/foo</device> + * + * Returns -ve value on error. + * Returns 0 otherwise. + * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL + * (e.g. comment lines, unknown XML content, etc). + */ +static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) +{ + blkid_dev dev; + int ret; + + if (!cache || !dev_p) + return -BLKID_ERR_PARAM; + + *dev_p = NULL; + + DBG(READ, ul_debug("line: %s", cp)); + + if ((ret = parse_dev(cache, dev_p, &cp)) <= 0) + return ret; + + dev = *dev_p; + + while ((ret = parse_tag(cache, dev, &cp)) > 0) { + ; + } + + if (dev->bid_type == NULL) { + DBG(READ, ul_debug("blkid: device %s has no TYPE",dev->bid_name)); + blkid_free_dev(dev); + goto done; + } + +done: + return ret; +} + +/* + * Parse the specified filename, and return the data in the supplied or + * a newly allocated cache struct. If the file doesn't exist, return a + * new empty cache struct. + */ +void blkid_read_cache(blkid_cache cache) +{ + FILE *file; + char buf[4096]; + int fd, lineno = 0; + struct stat st; + + /* + * If the file doesn't exist, then we just return an empty + * struct so that the cache can be populated. + */ + if ((fd = open(cache->bic_filename, O_RDONLY|O_CLOEXEC)) < 0) + return; + if (fstat(fd, &st) < 0) + goto errout; + if ((st.st_mtime == cache->bic_ftime) || + (cache->bic_flags & BLKID_BIC_FL_CHANGED)) { + DBG(CACHE, ul_debug("skipping re-read of %s", + cache->bic_filename)); + goto errout; + } + + DBG(CACHE, ul_debug("reading cache file %s", + cache->bic_filename)); + + file = fdopen(fd, "r" UL_CLOEXECSTR); + if (!file) + goto errout; + + while (fgets(buf, sizeof(buf), file)) { + blkid_dev dev; + unsigned int end; + + lineno++; + if (buf[0] == 0) + continue; + end = strlen(buf) - 1; + /* Continue reading next line if it ends with a backslash */ + while (end < (sizeof(buf) - 2) && buf[end] == '\\' && + fgets(buf + end, sizeof(buf) - end, file)) { + end = strlen(buf) - 1; + lineno++; + } + + if (blkid_parse_line(cache, &dev, buf) < 0) { + DBG(READ, ul_debug("blkid: bad format on line %d", lineno)); + continue; + } + } + fclose(file); + + /* + * Initially we do not need to write out the cache file. + */ + cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; + cache->bic_ftime = st.st_mtime; + + return; +errout: + close(fd); +} + +#ifdef TEST_PROGRAM + +int main(int argc, char**argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_init_debug(BLKID_DEBUG_ALL); + if (argc > 2) { + fprintf(stderr, "Usage: %s [filename]\n" + "Test parsing of the cache (filename)\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, argv[1])) < 0) + fprintf(stderr, "error %d reading cache file %s\n", ret, + argv[1] ? argv[1] : blkid_get_cache_filename(NULL)); + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/libblkid/src/resolve.c b/libblkid/src/resolve.c new file mode 100644 index 0000000..16653fa --- /dev/null +++ b/libblkid/src/resolve.c @@ -0,0 +1,129 @@ +/* + * resolve.c - resolve names and tags into specific devices + * + * Copyright (C) 2001, 2003 Theodore Ts'o. + * Copyright (C) 2001 Andreas Dilger + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "blkidP.h" + +/* + * Find a tagname (e.g. LABEL or UUID) on a specific device. + */ +char *blkid_get_tag_value(blkid_cache cache, const char *tagname, + const char *devname) +{ + blkid_tag found; + blkid_dev dev; + blkid_cache c = cache; + char *ret = NULL; + + DBG(TAG, ul_debug("looking for tag %s on %s device", tagname, devname)); + + if (!devname) + return NULL; + if (!cache && blkid_get_cache(&c, NULL) < 0) + return NULL; + + if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) && + (found = blkid_find_tag_dev(dev, tagname))) + ret = found->bit_val ? strdup(found->bit_val) : NULL; + + if (!cache) + blkid_put_cache(c); + + return ret; +} + +/* + * Locate a device name from a token (NAME=value string), or (name, value) + * pair. In the case of a token, value is ignored. If the "token" is not + * of the form "NAME=value" and there is no value given, then it is assumed + * to be the actual devname and a copy is returned. + */ +char *blkid_get_devname(blkid_cache cache, const char *token, + const char *value) +{ + blkid_dev dev; + blkid_cache c = cache; + char *t = NULL, *v = NULL; + char *ret = NULL; + + if (!token) + return NULL; + if (!cache && blkid_get_cache(&c, NULL) < 0) + return NULL; + + DBG(TAG, ul_debug("looking for %s%s%s %s", token, value ? "=" : "", + value ? value : "", cache ? "in cache" : "from disk")); + + if (!value) { + if (!strchr(token, '=')) { + ret = strdup(token); + goto out; + } + if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v) + goto out; + token = t; + value = v; + } + + dev = blkid_find_dev_with_tag(c, token, value); + if (!dev) + goto out; + + ret = dev->bid_name ? strdup(dev->bid_name) : NULL; +out: + free(t); + free(v); + if (!cache) + blkid_put_cache(c); + return ret; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + char *value; + blkid_cache cache; + + blkid_init_debug(BLKID_DEBUG_ALL); + if (argc != 2 && argc != 3) { + fprintf(stderr, "Usage:\t%s tagname=value\n" + "\t%s tagname devname\n" + "Find which device holds a given token or\n" + "Find what the value of a tag is in a device\n", + argv[0], argv[0]); + exit(1); + } + if (blkid_get_cache(&cache, "/dev/null") < 0) { + fprintf(stderr, "Couldn't get blkid cache\n"); + exit(1); + } + + if (argv[2]) { + value = blkid_get_tag_value(cache, argv[1], argv[2]); + printf("%s has tag %s=%s\n", argv[2], argv[1], + value ? value : "<missing>"); + } else { + value = blkid_get_devname(cache, argv[1], NULL); + printf("%s has tag %s\n", value ? value : "<none>", argv[1]); + } + blkid_put_cache(cache); + return value ? 0 : 1; +} +#endif diff --git a/libblkid/src/save.c b/libblkid/src/save.c new file mode 100644 index 0000000..1a617c0 --- /dev/null +++ b/libblkid/src/save.c @@ -0,0 +1,244 @@ +/* + * save.c - write the cache struct to disk + * + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "closestream.h" +#include "fileutils.h" + +#include "blkidP.h" + + +static void save_quoted(const char *data, FILE *file) +{ + const char *p; + + fputc('"', file); + for (p = data; p && *p; p++) { + if ((unsigned char) *p == 0x22 || /* " */ + (unsigned char) *p == 0x5c) /* \ */ + fputc('\\', file); + + fputc(*p, file); + } + fputc('"', file); +} +static int save_dev(blkid_dev dev, FILE *file) +{ + struct list_head *p; + + if (!dev || dev->bid_name[0] != '/') + return 0; + + DBG(SAVE, ul_debug("device %s, type %s", dev->bid_name, dev->bid_type ? + dev->bid_type : "(null)")); + + fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%lld.%lld\"", + (unsigned long) dev->bid_devno, + (long long) dev->bid_time, + (long long) dev->bid_utime); + + if (dev->bid_pri) + fprintf(file, " PRI=\"%d\"", dev->bid_pri); + + list_for_each(p, &dev->bid_tags) { + blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); + + fputc(' ', file); /* space between tags */ + fputs(tag->bit_name, file); /* tag NAME */ + fputc('=', file); /* separator between NAME and VALUE */ + save_quoted(tag->bit_val, file); /* tag "VALUE" */ + } + fprintf(file, ">%s</device>\n", dev->bid_name); + + return 0; +} + +/* + * Write out the cache struct to the cache file on disk. + */ +int blkid_flush_cache(blkid_cache cache) +{ + struct list_head *p; + char *tmp = NULL; + char *opened = NULL; + char *filename; + FILE *file = NULL; + int fd, ret = 0; + struct stat st; + + if (list_empty(&cache->bic_devs) || + !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { + DBG(SAVE, ul_debug("skipping cache file write")); + return 0; + } + + filename = cache->bic_filename ? cache->bic_filename : + blkid_get_cache_filename(NULL); + if (!filename) + return -BLKID_ERR_PARAM; + + if (strncmp(filename, + BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) { + + /* default destination, create the directory if necessary */ + if (stat(BLKID_RUNTIME_DIR, &st) + && errno == ENOENT + && mkdir(BLKID_RUNTIME_DIR, S_IWUSR| + S_IRUSR|S_IRGRP|S_IROTH| + S_IXUSR|S_IXGRP|S_IXOTH) != 0 + && errno != EEXIST) { + DBG(SAVE, ul_debug("can't create %s directory for cache file", + BLKID_RUNTIME_DIR)); + return 0; + } + } + + /* If we can't write to the cache file, then don't even try */ + if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) || + (ret == 0 && access(filename, W_OK) < 0)) { + DBG(SAVE, ul_debug("can't write to cache file %s", filename)); + return 0; + } + + /* + * Try and create a temporary file in the same directory so + * that in case of error we don't overwrite the cache file. + * If the cache file doesn't yet exist, it isn't a regular + * file (e.g. /dev/null or a socket), or we couldn't create + * a temporary file then we open it directly. + */ + if (ret == 0 && S_ISREG(st.st_mode)) { + size_t len = strlen(filename) + 8; + tmp = malloc(len); + if (tmp) { + snprintf(tmp, len, "%s-XXXXXX", filename); + fd = mkstemp_cloexec(tmp); + if (fd >= 0) { + if (fchmod(fd, 0644) != 0) + DBG(SAVE, ul_debug("%s: fchmod failed", filename)); + else if ((file = fdopen(fd, "w" UL_CLOEXECSTR))) + opened = tmp; + if (!file) + close(fd); + } + } + } + + if (!file) { + file = fopen(filename, "w" UL_CLOEXECSTR); + opened = filename; + } + + DBG(SAVE, ul_debug("writing cache file %s (really %s)", + filename, opened)); + + if (!file) { + ret = errno; + goto errout; + } + + list_for_each(p, &cache->bic_devs) { + blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); + if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE)) + continue; + if ((ret = save_dev(dev, file)) < 0) + break; + } + + if (ret >= 0) { + cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; + ret = 1; + } + + if (close_stream(file) != 0) + DBG(SAVE, ul_debug("write failed: %s", filename)); + + if (opened != filename) { + if (ret < 0) { + unlink(opened); + DBG(SAVE, ul_debug("unlinked temp cache %s", opened)); + } else { + char *backup; + size_t len = strlen(filename) + 5; + + backup = malloc(len); + if (backup) { + snprintf(backup, len, "%s.old", filename); + unlink(backup); + if (link(filename, backup)) { + DBG(SAVE, ul_debug("can't link %s to %s", + filename, backup)); + } + free(backup); + } + if (rename(opened, filename)) { + ret = errno; + DBG(SAVE, ul_debug("can't rename %s to %s", + opened, filename)); + } else { + DBG(SAVE, ul_debug("moved temp cache %s", opened)); + } + } + } + +errout: + free(tmp); + if (filename != cache->bic_filename) + free(filename); + return ret; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_cache cache = NULL; + int ret; + + blkid_init_debug(BLKID_DEBUG_ALL); + if (argc != 2) { + fprintf(stderr, "Usage: %s [filename]\n" + "Test loading/saving a cache (filename)\n", argv[0]); + exit(1); + } + + if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + if ((ret = blkid_probe_all(cache)) < 0) { + fprintf(stderr, "error (%d) probing devices\n", ret); + exit(1); + } + cache->bic_filename = strdup(argv[1]); + + if ((ret = blkid_flush_cache(cache)) < 0) { + fprintf(stderr, "error (%d) saving cache\n", ret); + exit(1); + } + + blkid_put_cache(cache); + + return ret; +} +#endif diff --git a/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c new file mode 100644 index 0000000..5fc5fc4 --- /dev/null +++ b/libblkid/src/superblocks/adaptec_raid.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct adaptec_metadata { + + uint32_t b0idcode; + uint8_t lunsave[8]; + uint16_t sdtype; + uint16_t ssavecyl; + uint8_t ssavehed; + uint8_t ssavesec; + uint8_t sb0flags; + uint8_t jbodEnable; + uint8_t lundsave; + uint8_t svpdirty; + uint16_t biosInfo; + uint16_t svwbskip; + uint16_t svwbcln; + uint16_t svwbmax; + uint16_t res3; + uint16_t svwbmin; + uint16_t res4; + uint16_t svrcacth; + uint16_t svwcacth; + uint16_t svwbdly; + uint8_t svsdtime; + uint8_t res5; + uint16_t firmval; + uint16_t firmbln; + uint32_t firmblk; + uint32_t fstrsvrb; + uint16_t svBlockStorageTid; + uint16_t svtid; + uint8_t svseccfl; + uint8_t res6; + uint8_t svhbanum; + uint8_t resver; + uint32_t drivemagic; + uint8_t reserved[20]; + uint8_t testnum; + uint8_t testflags; + uint16_t maxErrorCount; + uint32_t count; + uint32_t startTime; + uint32_t interval; + uint8_t tstxt0; + uint8_t tstxt1; + uint8_t serNum[32]; + uint8_t res8[102]; + uint32_t fwTestMagic; + uint32_t fwTestSeqNum; + uint8_t fwTestRes[8]; + uint32_t smagic; + uint32_t raidtbl; + uint16_t raidline; + uint8_t res9[0xF6]; +} __attribute__((packed)); + +#define AD_SIGNATURE 0x4450544D /* "DPTM" */ +#define AD_MAGIC 0x37FC4D1E + +static int probe_adraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + struct adaptec_metadata *ad; + + if (pr->size < 0x10000) + return BLKID_PROBE_NONE; + + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return BLKID_PROBE_NONE; + + off = ((pr->size / 0x200)-1) * 0x200; + ad = (struct adaptec_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct adaptec_metadata)); + if (!ad) + return errno ? -errno : BLKID_PROBE_NONE; + + if (ad->smagic != be32_to_cpu(AD_SIGNATURE)) + return BLKID_PROBE_NONE; + if (ad->b0idcode != be32_to_cpu(AD_MAGIC)) + return BLKID_PROBE_NONE; + if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0) + return BLKID_PROBE_NONE; + if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode), + (unsigned char *) &ad->b0idcode)) + return BLKID_PROBE_NONE; + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo adraid_idinfo = { + .name = "adaptec_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_adraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/apfs.c b/libblkid/src/superblocks/apfs.c new file mode 100644 index 0000000..048423a --- /dev/null +++ b/libblkid/src/superblocks/apfs.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018 Harry Mallon <hjmallon@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include "superblocks.h" + +#define APFS_CONTAINER_SUPERBLOCK_TYPE 1 +#define APFS_CONTAINER_SUPERBLOCK_SUBTYPE 0 +#define APFS_STANDARD_BLOCK_SIZE 4096 + +/* + * This struct is much longer than this, but this seems + * to contain the useful bits (for now). + * + * All values are little-endian. + */ +struct apfs_super_block { + // Generic part to all APFS objects + uint64_t checksum; + uint64_t oid; + uint64_t xid; + uint16_t type; + uint16_t flags; + uint16_t subtype; + uint16_t pad; + + // Specific to container header + uint32_t magic; // 'NXSB' + uint32_t block_size; + uint64_t block_count; + uint64_t features; + uint64_t read_only_features; + uint64_t incompatible_features; + uint8_t uuid[16]; +}; + +static int probe_apfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct apfs_super_block *sb; + + sb = blkid_probe_get_sb(pr, mag, struct apfs_super_block); + if (!sb) + return errno ? -errno : BLKID_PROBE_NONE; + + if (le16_to_cpu(sb->type) != APFS_CONTAINER_SUPERBLOCK_TYPE) + return BLKID_PROBE_NONE; + + if (le16_to_cpu(sb->subtype) != APFS_CONTAINER_SUPERBLOCK_SUBTYPE) + return BLKID_PROBE_NONE; + + if (le16_to_cpu(sb->pad) != 0) + return BLKID_PROBE_NONE; + + /* + * This check is pretty draconian, but should avoid false + * positives. Can be improved as more APFS documentation + * is published. + */ + if (le32_to_cpu(sb->block_size) != APFS_STANDARD_BLOCK_SIZE) + return BLKID_PROBE_NONE; + + if (blkid_probe_set_uuid(pr, sb->uuid) < 0) + return BLKID_PROBE_NONE; + + blkid_probe_set_fsblocksize(pr, le32_to_cpu(sb->block_size)); + blkid_probe_set_block_size(pr, le32_to_cpu(sb->block_size)); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo apfs_idinfo = +{ + .name = "apfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_apfs, + .magics = + { + { .magic = "NXSB", .len = 4, .sboff = 32 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c new file mode 100644 index 0000000..d3afc41 --- /dev/null +++ b/libblkid/src/superblocks/bcache.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Based on code fragments from bcache-tools by Kent Overstreet: + * http://evilpiepirate.org/git/bcache-tools.git + */ + +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> + +#include "superblocks.h" +#include "crc32c.h" +#include "crc64.h" +#include "xxhash.h" + +#define SB_LABEL_SIZE 32 +#define SB_JOURNAL_BUCKETS 256U + +/* + * The bcache_super_block is adapted from struct cache_sb in kernel. + * https://github.com/torvalds/linux/blob/master/drivers/md/bcache/bcache_ondisk.h + */ +struct bcache_super_block { + uint64_t csum; + uint64_t offset; /* where this super block was written */ + uint64_t version; + uint8_t magic[16]; /* bcache file system identifier */ + uint8_t uuid[16]; /* device identifier */ + uint8_t set_info[16]; /* magic or uuid */ + uint8_t label[SB_LABEL_SIZE]; + uint64_t flags; + uint64_t seq; + + uint64_t feature_compat; + uint64_t feature_incompat; + uint64_t feature_ro_compat; + + uint64_t pad[5]; + + union { + struct { + /* Cache devices */ + uint64_t nbuckets; /* device size */ + + uint16_t block_size; /* sectors */ + uint16_t bucket_size; /* sectors */ + + uint16_t nr_in_set; + uint16_t nr_this_dev; + }; + struct { + /* Backing devices */ + uint64_t data_offset; + }; + }; + + uint32_t last_mount; + + uint16_t first_bucket; + union { + uint16_t njournal_buckets; + uint16_t keys; + }; + uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */ + uint16_t obso_bucket_size_hi; /* obsoleted */ +} __attribute__((packed)); + +struct bcachefs_sb_field { + uint32_t u64s; + uint32_t type; +} __attribute__((packed)); + +struct bcachefs_sb_member { + uint8_t uuid[16]; + uint64_t nbuckets; + uint16_t first_bucket; + uint16_t bucket_size; + uint32_t pad; + uint64_t last_mount; + uint64_t flags[2]; +} __attribute__((packed)); + +struct bcachefs_sb_field_members { + struct bcachefs_sb_field field; + struct bcachefs_sb_member members[]; +} __attribute__((packed)); + +struct bcachefs_sb_disk_group { + uint8_t label[SB_LABEL_SIZE]; + uint64_t flags[2]; +} __attribute__((packed)); + +struct bcachefs_sb_field_disk_groups { + struct bcachefs_sb_field field; + struct bcachefs_sb_disk_group disk_groups[]; +} __attribute__((packed)); + +enum bcachefs_sb_csum_type { + BCACHEFS_SB_CSUM_TYPE_NONE = 0, + BCACHEFS_SB_CSUM_TYPE_CRC32C = 1, + BCACHEFS_SB_CSUM_TYPE_CRC64 = 2, + BCACHEFS_SB_CSUM_TYPE_XXHASH = 7, +}; + +union bcachefs_sb_csum { + uint32_t crc32c; + uint64_t crc64; + XXH64_hash_t xxh64; + uint8_t raw[16]; +} __attribute__((packed)); + +struct bcachefs_sb_layout { + uint8_t magic[16]; + uint8_t layout_type; + uint8_t sb_max_size_bits; + uint8_t nr_superblocks; + uint8_t pad[5]; + uint64_t sb_offset[61]; +} __attribute__((packed)); + +struct bcachefs_super_block { + union bcachefs_sb_csum csum; + uint16_t version; + uint16_t version_min; + uint16_t pad[2]; + uint8_t magic[16]; + uint8_t uuid[16]; + uint8_t user_uuid[16]; + uint8_t label[SB_LABEL_SIZE]; + uint64_t offset; + uint64_t seq; + uint16_t block_size; + uint8_t dev_idx; + uint8_t nr_devices; + uint32_t u64s; + uint64_t time_base_lo; + uint32_t time_base_hi; + uint32_t time_precision; + uint64_t flags[8]; + uint64_t features[2]; + uint64_t compat[2]; + struct bcachefs_sb_layout layout; + struct bcachefs_sb_field _start[]; +} __attribute__((packed)); + +/* magic string */ +#define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81" +#define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef" +/* magic string len */ +#define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1) +/* super block offset */ +#define BCACHE_SB_OFF 0x1000U +/* supper block offset in kB */ +#define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10) +/* magic string offset within super block */ +#define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic) +/* start of checksummed data within superblock */ +#define BCACHE_SB_CSUMMED_START 8U +/* granularity of offset and length fields within superblock */ +#define BCACHEFS_SECTOR_SIZE 512U +/* maximum superblock size shift */ +#define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U +/* maximum superblock size */ +#define BCACHEFS_SB_MAX_SIZE (1U << BCACHEFS_SB_MAX_SIZE_SHIFT) +/* fields offset within super block */ +#define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start) +/* tag value for members field */ +#define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1 +/* tag value for disk_groups field */ +#define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5 +/* version splitting helpers */ +#define BCH_VERSION_MAJOR(_v) ((uint16_t) ((_v) >> 10)) +#define BCH_VERSION_MINOR(_v) ((uint16_t) ((_v) & ~(~0U << 10))) + +#define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8)) + +static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, + const struct bcache_super_block *bcs) +{ + const unsigned char *csummed; + size_t csummed_size; + uint64_t csum; + + if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d)) + return 0; + + /* up to the end of bcs->d[] */ + csummed_size = offsetof(typeof(*bcs), d) + + sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys); + csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); + csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START, + csummed_size - BCACHE_SB_CSUMMED_START); + return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum)); +} + +static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bcache_super_block *bcs; + + bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block); + if (!bcs) + return errno ? -errno : BLKID_PROBE_NONE; + + if (!bcache_verify_checksum(pr, mag, bcs)) + return BLKID_PROBE_NONE; + + if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512) + return BLKID_PROBE_NONE; + + if (blkid_probe_set_uuid(pr, bcs->uuid) < 0) + return BLKID_PROBE_NONE; + + blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); + + return BLKID_PROBE_OK; +} + +static void probe_bcachefs_sb_members(blkid_probe pr, + const struct bcachefs_super_block *bcs, + const struct bcachefs_sb_field *field, + uint8_t dev_idx) +{ + struct bcachefs_sb_field_members *members = + (struct bcachefs_sb_field_members *) field; + uint64_t sectors = 0; + uint8_t i; + + if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices])) + return; + + blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB"); + + for (i = 0; i < bcs->nr_devices; i++) { + struct bcachefs_sb_member *member = &members->members[i]; + sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size); + } + blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE); +} + +static void probe_bcachefs_sb_disk_groups(blkid_probe pr, + const struct bcachefs_super_block *bcs, + const struct bcachefs_sb_field *field, + uint8_t dev_idx) +{ + struct bcachefs_sb_field_disk_groups *disk_groups = + (struct bcachefs_sb_field_disk_groups *) field; + + if (BYTES(field) != offsetof(typeof(*disk_groups), disk_groups[bcs->nr_devices])) + return; + + blkid_probe_set_id_label(pr, "LABEL_SUB", + disk_groups->disk_groups[dev_idx].label, + sizeof(disk_groups->disk_groups[dev_idx].label)); +} + +static int is_within_range(void *start, uint64_t size, void *end) +{ + ptrdiff_t diff; + + if (start >= end) + return 0; // should not happen + + diff = (unsigned char *) end - (unsigned char *) start; + return size <= (uint64_t) diff; +} + +static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs, + unsigned char *sb_start, unsigned char *sb_end) +{ + unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF; + + while (1) { + struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr; + uint64_t field_size; + uint32_t type; + + if (!is_within_range(field, sizeof(*field), sb_end)) + break; + + field_size = BYTES(field); + + if (field_size < sizeof(*field)) + break; + + if (!is_within_range(field, field_size, sb_end)) + break; + + type = le32_to_cpu(field->type); + if (!type) + break; + + if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS) + probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx); + + if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS) + probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx); + + field_addr += BYTES(field); + } +} + +static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs, + unsigned char *sb, unsigned char *sb_end) +{ + uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58; + unsigned char *checksummed_data_start = sb + sizeof(bcs->csum); + size_t checksummed_data_size = sb_end - checksummed_data_start; + + switch (checksum_type) { + case BCACHEFS_SB_CSUM_TYPE_NONE: + return 1; + case BCACHEFS_SB_CSUM_TYPE_CRC32C: { + uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL; + return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c)); + } + case BCACHEFS_SB_CSUM_TYPE_CRC64: { + uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size); + return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64)); + } + case BCACHEFS_SB_CSUM_TYPE_XXHASH: { + XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0); + return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64)); + } + default: + DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type)); + return 1; + } +} + +static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bcachefs_super_block *bcs; + unsigned char *sb, *sb_end; + uint64_t sb_size, blocksize; + uint16_t version; + + bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block); + if (!bcs) + return errno ? -errno : BLKID_PROBE_NONE; + + if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE) + return BLKID_PROBE_NONE; + + if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices) + return BLKID_PROBE_NONE; + + sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs); + + if (sb_size > BCACHEFS_SB_MAX_SIZE) + return BLKID_PROBE_NONE; + + if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT) + return BLKID_PROBE_NONE; + + if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits)) + return BLKID_PROBE_NONE; + + sb = blkid_probe_get_sb_buffer(pr, mag, sb_size); + if (!sb) + return BLKID_PROBE_NONE; + sb_end = sb + sb_size; + + if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end)) + return BLKID_PROBE_NONE; + + blkid_probe_set_uuid(pr, bcs->user_uuid); + blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)); + version = le16_to_cpu(bcs->version); + blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16, + BCH_VERSION_MAJOR(version), + BCH_VERSION_MINOR(version)); + blocksize = le16_to_cpu(bcs->block_size); + blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE); + blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE); + blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); + + probe_bcachefs_sb_fields(pr, bcs, sb, sb_end); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo bcache_idinfo = +{ + .name = "bcache", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_bcache, + .minsz = 8192, + .magics = + { + { + .magic = BCACHE_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF + }, + { NULL } + } +}; + +const struct blkid_idinfo bcachefs_idinfo = +{ + .name = "bcachefs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_bcachefs, + .minsz = 256 * BCACHEFS_SECTOR_SIZE, + .magics = { + { + .magic = BCACHE_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF, + }, + { + .magic = BCACHEFS_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF, + }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c new file mode 100644 index 0000000..5112d44 --- /dev/null +++ b/libblkid/src/superblocks/befs.c @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2010 Jeroen Oortwijn <oortwijn@gmail.com> + * + * Partly based on the Haiku BFS driver by + * Axel Dörfler <axeld@pinc-software.de> + * + * Also inspired by the Linux BeFS driver by + * Will Dyson <will_dyson@pobox.com>, et al. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <inttypes.h> + +#include "superblocks.h" + +#define B_OS_NAME_LENGTH 0x20 +#define SUPER_BLOCK_MAGIC1 0x42465331 /* BFS1 */ +#define SUPER_BLOCK_MAGIC2 0xdd121031 +#define SUPER_BLOCK_MAGIC3 0x15b6830e +#define SUPER_BLOCK_FS_ENDIAN 0x42494745 /* BIGE */ +#define INODE_MAGIC1 0x3bbe0ad9 +#define BPLUSTREE_MAGIC 0x69f6c2e8 +#define BPLUSTREE_NULL -1LL +#define NUM_DIRECT_BLOCKS 12 +#define B_UINT64_TYPE 0x554c4c47 /* ULLG */ +#define KEY_NAME "be:volume_id" +#define KEY_SIZE 8 + +#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \ + : be16_to_cpu(value)) +#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \ + : be32_to_cpu(value)) +#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \ + : be64_to_cpu(value)) + +typedef struct block_run { + int32_t allocation_group; + uint16_t start; + uint16_t len; +} __attribute__((packed)) block_run, inode_addr; + +struct befs_super_block { + char name[B_OS_NAME_LENGTH]; + int32_t magic1; + int32_t fs_byte_order; + uint32_t block_size; + uint32_t block_shift; + int64_t num_blocks; + int64_t used_blocks; + int32_t inode_size; + int32_t magic2; + int32_t blocks_per_ag; + int32_t ag_shift; + int32_t num_ags; + int32_t flags; + block_run log_blocks; + int64_t log_start; + int64_t log_end; + int32_t magic3; + inode_addr root_dir; + inode_addr indices; + int32_t pad[8]; +} __attribute__((packed)); + +typedef struct data_stream { + block_run direct[NUM_DIRECT_BLOCKS]; + int64_t max_direct_range; + block_run indirect; + int64_t max_indirect_range; + block_run double_indirect; + int64_t max_double_indirect_range; + int64_t size; +} __attribute__((packed)) data_stream; + +struct befs_inode { + int32_t magic1; + inode_addr inode_num; + int32_t uid; + int32_t gid; + int32_t mode; + int32_t flags; + int64_t create_time; + int64_t last_modified_time; + inode_addr parent; + inode_addr attributes; + uint32_t type; + int32_t inode_size; + uint32_t etc; + data_stream data; + int32_t pad[4]; + int32_t small_data[0]; +} __attribute__((packed)); + +struct small_data { + uint32_t type; + uint16_t name_size; + uint16_t data_size; + char name[0]; +} __attribute__((packed)); + +struct bplustree_header { + uint32_t magic; + uint32_t node_size; + uint32_t max_number_of_levels; + uint32_t data_type; + int64_t root_node_pointer; + int64_t free_node_pointer; + int64_t maximum_size; +} __attribute__((packed)); + +struct bplustree_node { + int64_t left_link; + int64_t right_link; + int64_t overflow_link; + uint16_t all_key_count; + uint16_t all_key_length; + char name[0]; +} __attribute__((packed)); + +static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs, + const struct block_run *br, int fs_le) +{ + return blkid_probe_get_buffer(pr, + ((uint64_t) FS32_TO_CPU(br->allocation_group, fs_le) + << FS32_TO_CPU(bs->ag_shift, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + + ((uint64_t) FS16_TO_CPU(br->start, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)), + (uint64_t) FS16_TO_CPU(br->len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)); +} + +static unsigned char *get_custom_block_run(blkid_probe pr, + const struct befs_super_block *bs, + const struct block_run *br, + int64_t offset, uint32_t length, int fs_le) +{ + if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + return NULL; + + return blkid_probe_get_buffer(pr, + ((uint64_t) FS32_TO_CPU(br->allocation_group, fs_le) + << FS32_TO_CPU(bs->ag_shift, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + + ((uint64_t) FS16_TO_CPU(br->start, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + + offset, + length); +} + +static unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs, + const struct data_stream *ds, + int64_t start, uint32_t length, int fs_le) +{ + if (start < (int64_t) FS64_TO_CPU(ds->max_direct_range, fs_le)) { + int64_t br_len; + size_t i; + + for (i = 0; i < NUM_DIRECT_BLOCKS; i++) { + br_len = (int64_t) FS16_TO_CPU(ds->direct[i].len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le); + if (start < br_len) + return get_custom_block_run(pr, bs, + &ds->direct[i], + start, length, fs_le); + start -= br_len; + } + } else if (start < (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le)) { + struct block_run *br; + int64_t max_br, br_len, i; + + start -= FS64_TO_CPU(ds->max_direct_range, fs_le); + max_br = ((int64_t) FS16_TO_CPU(ds->indirect.len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + / sizeof(struct block_run); + + br = (struct block_run *) get_block_run(pr, bs, &ds->indirect, + fs_le); + if (!br) + return NULL; + + for (i = 0; i < max_br; i++) { + br_len = (int64_t) FS16_TO_CPU(br[i].len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le); + if (start < br_len) + return get_custom_block_run(pr, bs, &br[i], + start, length, fs_le); + start -= br_len; + } + } else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) { + struct block_run *br; + int64_t max_br, di_br_size, br_per_di_br, di_index, i_index; + + start -= (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le); + + di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len, + fs_le) << FS32_TO_CPU(bs->block_shift, fs_le); + if (di_br_size == 0) + return NULL; + + br_per_di_br = di_br_size / sizeof(struct block_run); + if (br_per_di_br == 0) + return NULL; + + di_index = start / (br_per_di_br * di_br_size); + i_index = (start % (br_per_di_br * di_br_size)) / di_br_size; + start = (start % (br_per_di_br * di_br_size)) % di_br_size; + + if (di_index >= br_per_di_br) + return NULL; /* Corrupt? */ + + br = (struct block_run *) get_block_run(pr, bs, + &ds->double_indirect, fs_le); + if (!br) + return NULL; + + max_br = ((int64_t)FS16_TO_CPU(br[di_index].len, fs_le) + << FS32_TO_CPU(bs->block_shift, fs_le)) + / sizeof(struct block_run); + + if (i_index >= max_br) + return NULL; /* Corrupt? */ + + br = (struct block_run *) get_block_run(pr, bs, &br[di_index], + fs_le); + if (!br) + return NULL; + + return get_custom_block_run(pr, bs, &br[i_index], start, length, + fs_le); + } + return NULL; +} + +#define BAD_KEYS -2 + +static int32_t compare_keys(const char keys1[], uint16_t keylengths1[], + int32_t index, const char *key2, + uint16_t keylength2, uint16_t all_key_length, + int fs_le) +{ + const char *key1; + uint16_t keylength1, keystart1; + int32_t result; + + keystart1 = index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1], fs_le); + keylength1 = FS16_TO_CPU(keylengths1[index], fs_le) - keystart1; + + if (keystart1 + keylength1 > all_key_length) + return BAD_KEYS; /* Corrupt? */ + + key1 = &keys1[keystart1]; + + result = strncmp(key1, key2, min(keylength1, keylength2)); + + if (result == 0) + return keylength1 - keylength2; + + if (result < 0) /* Don't clash with BAD_KEYS */ + result = -1; + + return result; +} + +static int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs, + const struct befs_inode *bi, const char *key, int fs_le) +{ + struct bplustree_header *bh; + struct bplustree_node *bn; + uint16_t *keylengths; + int64_t *values; + int64_t node_pointer; + uint32_t bn_size, all_key_count, all_key_length; + uint32_t keylengths_offset, values_offset; + int32_t first, last, mid, cmp; + int loop_detect = 0; + + errno = 0; + bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0, + sizeof(struct bplustree_header), fs_le); + if (!bh) + return errno ? -errno : -ENOENT; + + if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC) + return -ENOENT; + + node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le); + bn_size = FS32_TO_CPU(bh->node_size, fs_le); + + if (bn_size < sizeof(struct bplustree_node)) + return -ENOENT; /* Corrupt? */ + + do { + errno = 0; + + bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data, + node_pointer, bn_size, fs_le); + if (!bn) + return errno ? -errno : -ENOENT; + + all_key_count = FS16_TO_CPU(bn->all_key_count, fs_le); + all_key_length = FS16_TO_CPU(bn->all_key_length, fs_le); + keylengths_offset = + (sizeof(struct bplustree_node) + all_key_length + + sizeof(int64_t) - 1) & ~(sizeof(int64_t) - 1); + values_offset = keylengths_offset + + all_key_count * sizeof(uint16_t); + + if (values_offset + all_key_count * sizeof(uint64_t) > bn_size) + return -ENOENT; /* Corrupt? */ + + keylengths = (uint16_t *) ((uint8_t *) bn + keylengths_offset); + values = (int64_t *) ((uint8_t *) bn + values_offset); + + first = 0; + mid = 0; + last = all_key_count - 1; + + cmp = compare_keys(bn->name, keylengths, last, key, + strlen(key), all_key_length, fs_le); + if (cmp == BAD_KEYS) + return -ENOENT; + + if (cmp == 0) { + if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le) + == BPLUSTREE_NULL) + return FS64_TO_CPU(values[last], fs_le); + + node_pointer = FS64_TO_CPU(values[last], fs_le); + } else if (cmp < 0) + node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le); + else { + while (first <= last) { + mid = (first + last) / 2; + + cmp = compare_keys(bn->name, keylengths, mid, + key, strlen(key), + all_key_length, fs_le); + if (cmp == BAD_KEYS) + return -ENOENT; + + if (cmp == 0) { + if ((int64_t) FS64_TO_CPU(bn->overflow_link, + fs_le) == BPLUSTREE_NULL) + return FS64_TO_CPU(values[mid], + fs_le); + break; + } + + if (cmp < 0) + first = mid + 1; + else + last = mid - 1; + } + if (cmp < 0) + node_pointer = FS64_TO_CPU(values[mid + 1], + fs_le); + else + node_pointer = FS64_TO_CPU(values[mid], fs_le); + } + } while (++loop_detect < 100 && + (int64_t) FS64_TO_CPU(bn->overflow_link, fs_le) + != BPLUSTREE_NULL); + return 0; +} + +static int get_uuid(blkid_probe pr, const struct befs_super_block *bs, + uint64_t * const uuid, int fs_le) +{ + struct befs_inode *bi; + struct small_data *sd; + uint64_t bi_size, offset, sd_size, sd_total_size; + + bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le); + if (!bi) + return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) + return BLKID_PROBE_NONE; + + bi_size = (uint64_t)FS16_TO_CPU(bs->root_dir.len, fs_le) << + FS32_TO_CPU(bs->block_shift, fs_le); + sd_total_size = min(bi_size - sizeof(struct befs_inode), + (uint64_t)FS32_TO_CPU(bi->inode_size, fs_le)); + + offset = 0; + + while (offset + sizeof(struct small_data) <= sd_total_size) { + sd = (struct small_data *) ((uint8_t *)bi->small_data + offset); + sd_size = sizeof(struct small_data) + + FS16_TO_CPU(sd->name_size, fs_le) + 3 + + FS16_TO_CPU(sd->data_size, fs_le) + 1; + + if (offset + sd_size > sd_total_size) + break; + + if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE + && FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME) + && FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE + && strcmp(sd->name, KEY_NAME) == 0) { + + memcpy(uuid, + sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3, + sizeof(uint64_t)); + + break; + } + + if (FS32_TO_CPU(sd->type, fs_le) == 0 + && FS16_TO_CPU(sd->name_size, fs_le) == 0 + && FS16_TO_CPU(sd->data_size, fs_le) == 0) + break; + + offset += sd_size; + } + + if (*uuid == 0 + && (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0 + || FS16_TO_CPU(bi->attributes.start, fs_le) != 0 + || FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) { + int64_t value; + + bi = (struct befs_inode *) get_block_run(pr, bs, + &bi->attributes, fs_le); + if (!bi) + return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) + return BLKID_PROBE_NONE; + + value = get_key_value(pr, bs, bi, KEY_NAME, fs_le); + if (value < 0) + return value == -ENOENT ? BLKID_PROBE_NONE : value; + + if (value > 0) { + bi = (struct befs_inode *) blkid_probe_get_buffer(pr, + value << FS32_TO_CPU(bs->block_shift, fs_le), + FS32_TO_CPU(bs->block_size, fs_le)); + if (!bi) + return errno ? -errno : BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1) + return 1; + + if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE + && FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE + && FS16_TO_CPU(bi->data.direct[0].len, fs_le) + == 1) { + uint64_t *attr_data; + + attr_data = (uint64_t *) get_block_run(pr, bs, + &bi->data.direct[0], fs_le); + if (!attr_data) + return errno ? -errno : BLKID_PROBE_NONE; + + *uuid = *attr_data; + } + } + } + return 0; +} + +static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct befs_super_block *bs; + const char *version = NULL; + uint64_t volume_id = 0; + uint32_t block_size, block_shift; + int fs_le, ret; + + bs = (struct befs_super_block *) blkid_probe_get_buffer(pr, + mag->sboff - B_OS_NAME_LENGTH, + sizeof(struct befs_super_block)); + if (!bs) + return errno ? -errno : BLKID_PROBE_NONE; + + if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1 + && le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2 + && le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3 + && le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) { + fs_le = 1; + version = "little-endian"; + } else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1 + && be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2 + && be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3 + && be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) { + fs_le = 0; + version = "big-endian"; + } else + return BLKID_PROBE_NONE; + + block_size = FS32_TO_CPU(bs->block_size, fs_le); + block_shift = FS32_TO_CPU(bs->block_shift, fs_le); + + if (block_shift < 10 || block_shift > 13 || + block_size != 1U << block_shift) + return BLKID_PROBE_NONE; + + if (FS32_TO_CPU(bs->ag_shift, fs_le) > 64) + return BLKID_PROBE_NONE; + + ret = get_uuid(pr, bs, &volume_id, fs_le); + + if (ret != 0) + return ret; + + /* + * all checks pass, set LABEL, VERSION and UUID + */ + if (*bs->name != '\0') + blkid_probe_set_label(pr, (unsigned char *) bs->name, + sizeof(bs->name)); + if (version) + blkid_probe_set_version(pr, version); + + if (volume_id) + blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id, + sizeof(volume_id), "%016" PRIx64, + FS64_TO_CPU(volume_id, fs_le)); + + blkid_probe_set_fsblocksize(pr, block_size); + blkid_probe_set_block_size(pr, block_size); + blkid_probe_set_fsendianness(pr, + fs_le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo befs_idinfo = +{ + .name = "befs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_befs, + .minsz = 1024 * 1440, + .magics = { + { .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH }, + { .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH }, + { .magic = "BFS1", .len = 4, .sboff = 0x200 + + B_OS_NAME_LENGTH }, + { .magic = "1SFB", .len = 4, .sboff = 0x200 + + B_OS_NAME_LENGTH }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/bfs.c b/libblkid/src/superblocks/bfs.c new file mode 100644 index 0000000..8a34c58 --- /dev/null +++ b/libblkid/src/superblocks/bfs.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2009 Red Hat, Inc. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include "superblocks.h" + +/* + * BFS actually has two different labels in the superblock, each + * of them only 6 bytes long. Until we find out what their use + * we just ignore them. + */ +const struct blkid_idinfo bfs_idinfo = +{ + .name = "bfs", + .usage = BLKID_USAGE_FILESYSTEM, + .magics = { + { .magic = "\xce\xfa\xad\x1b", .len = 4 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/bitlocker.c b/libblkid/src/superblocks/bitlocker.c new file mode 100644 index 0000000..a9a7bfb --- /dev/null +++ b/libblkid/src/superblocks/bitlocker.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +#define BDE_HDR_SIZE 512 +#define BDE_HDR_OFFSET 0 + +struct bde_header_win7 { +/* 0 */ unsigned char boot_entry_point[3]; +/* 3 */ unsigned char fs_signature[8]; +/* 11 */ unsigned char __dummy1[67 - 11]; +/* 67 */ uint32_t volume_serial; /* NTFS uses 64bit serial number */ +/* 71 */ unsigned char volume_label[11]; /* "NO NAME\x20\x20\x20\x20" only */ +/* 82 */ unsigned char __dummy2[160 - 82]; +/* 160 */ unsigned char guid[16]; /* BitLocker specific GUID */ +/* 176 */ uint64_t fve_metadata_offset; +} __attribute__((packed)); + + +struct bde_header_togo { +/* 0 */ unsigned char boot_entry_point[3]; +/* 3 */ unsigned char fs_signature[8]; +/* 11 */ unsigned char __dummy[424 - 11]; +/* 424 */ unsigned char guid[16]; +/* 440 */ uint64_t fve_metadata_offset; +} __attribute__((packed)); + + +struct bde_fve_metadata { +/* 0 */ unsigned char signature[8]; +/* 8 */ uint16_t size; +/* 10 */ uint16_t version; +}; + +enum { + BDE_VERSION_VISTA = 0, + BDE_VERSION_WIN7, + BDE_VERSION_TOGO +}; + +#define BDE_MAGIC_VISTA "\xeb\x52\x90-FVE-FS-" +#define BDE_MAGIC_WIN7 "\xeb\x58\x90-FVE-FS-" +#define BDE_MAGIC_TOGO "\xeb\x58\x90MSWIN4.1" + +#define BDE_MAGIC_FVE "-FVE-FS-" + +static int get_bitlocker_type(const unsigned char *buf) +{ + size_t i; + static const char *map[] = { + [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA, + [BDE_VERSION_WIN7] = BDE_MAGIC_WIN7, + [BDE_VERSION_TOGO] = BDE_MAGIC_TOGO + }; + + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (memcmp(buf, map[i], 11) == 0) + return (int) i; + } + + return -1; +} + +/* Returns: < 0 error, 1 nothing, 0 success + */ +static int get_bitlocker_headers(blkid_probe pr, + int *type, + const unsigned char **buf_hdr, + const unsigned char **buf_fve) +{ + + const unsigned char *buf; + const struct bde_fve_metadata *fve; + uint64_t off = 0; + int kind; + + if (buf_hdr) + *buf_hdr = NULL; + if (buf_fve) + *buf_fve = NULL; + if (type) + *type = -1; + + buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE); + if (!buf) + return errno ? -errno : 1; + + kind = get_bitlocker_type(buf); + + /* Check BitLocker header */ + switch (kind) { + case BDE_VERSION_WIN7: + off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset); + break; + case BDE_VERSION_TOGO: + off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset); + break; + case BDE_VERSION_VISTA: + goto done; + default: + goto nothing; + } + + if (!off || off % 64) + goto nothing; + if (buf_hdr) + *buf_hdr = buf; + + /* Check Bitlocker FVE metadata header */ + buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata)); + if (!buf) + return errno ? -errno : 1; + + fve = (const struct bde_fve_metadata *) buf; + if (memcmp(fve->signature, BDE_MAGIC_FVE, sizeof(fve->signature)) != 0) + goto nothing; + if (buf_fve) + *buf_fve = buf; +done: + if (type) + *type = kind; + return 0; +nothing: + return 1; +} + +/* + * This is used by vFAT and NTFS prober to avoid collisions with bitlocker. + */ +int blkid_probe_is_bitlocker(blkid_probe pr) +{ + return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0; +} + +static int probe_bitlocker(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const unsigned char *buf_fve = NULL; + const unsigned char *buf_hdr = NULL; + int rc, kind; + + rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve); + if (rc) + return rc; + + if (kind == BDE_VERSION_WIN7) { + const struct bde_header_win7 *hdr = (const struct bde_header_win7 *) buf_hdr; + + /* Unfortunately, it seems volume_serial is always zero */ + blkid_probe_sprintf_uuid(pr, + (const unsigned char *) &hdr->volume_serial, + sizeof(hdr->volume_serial), + "%016d", le32_to_cpu(hdr->volume_serial)); + } + + if (buf_fve) { + const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve; + + blkid_probe_sprintf_version(pr, "%d", fve->version); + } + return 0; +} + +/* See header details: + * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc + */ +const struct blkid_idinfo bitlocker_idinfo = +{ + .name = "BitLocker", + .usage = BLKID_USAGE_CRYPTO, + .probefunc = probe_bitlocker, + .magics = + { + { .magic = BDE_MAGIC_VISTA, .len = 11 }, + { .magic = BDE_MAGIC_WIN7, .len = 11 }, + { .magic = BDE_MAGIC_TOGO, .len = 11 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/bluestore.c b/libblkid/src/superblocks/bluestore.c new file mode 100644 index 0000000..2ff1f35 --- /dev/null +++ b/libblkid/src/superblocks/bluestore.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 by Kenneth Van Alstyne <kvanals@kvanals.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * + * Ceph BlueStore is one of the supported storage + * methods for Object Storage Devices (OSDs). + * This is used to detect the backing block devices + * used for these types of OSDs in a Ceph Cluster. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <stddef.h> + +#include "bitops.h" +#include "superblocks.h" + +#define BLUESTORE_MAGIC_L 22 + +struct bluestore_phdr { + uint8_t magic[BLUESTORE_MAGIC_L]; +} __attribute__((packed)); + +static int probe_bluestore(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bluestore_phdr *header; + + header = blkid_probe_get_sb(pr, mag, struct bluestore_phdr); + if (header == NULL) + return errno ? -errno : 1; + + return 0; +} + +const struct blkid_idinfo bluestore_idinfo = +{ + .name = "ceph_bluestore", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_bluestore, + .magics = + { + { .magic = "bluestore block device", .len = 22 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/btrfs.c b/libblkid/src/superblocks/btrfs.c new file mode 100644 index 0000000..b9cf4bd --- /dev/null +++ b/libblkid/src/superblocks/btrfs.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <inttypes.h> + +#ifdef HAVE_LINUX_BLKZONED_H +#include <linux/blkzoned.h> +#endif + +#include "superblocks.h" +#include "crc32c.h" +#include "sha256.h" +#include "xxhash.h" + +enum btrfs_super_block_csum_type { + BTRFS_SUPER_BLOCK_CSUM_TYPE_CRC32C = 0, + BTRFS_SUPER_BLOCK_CSUM_TYPE_XXHASH = 1, + BTRFS_SUPER_BLOCK_CSUM_TYPE_SHA256 = 2, +}; + +union btrfs_super_block_csum { + uint8_t bytes[32]; + uint32_t crc32c; + XXH64_hash_t xxh64; + uint8_t sha256[UL_SHA256LENGTH]; +}; + +struct btrfs_super_block { + union btrfs_super_block_csum csum; + uint8_t fsid[16]; + uint64_t bytenr; + uint64_t flags; + uint8_t magic[8]; + uint64_t generation; + uint64_t root; + uint64_t chunk_root; + uint64_t log_root; + uint64_t log_root_transid; + uint64_t total_bytes; + uint64_t bytes_used; + uint64_t root_dir_objectid; + uint64_t num_devices; + uint32_t sectorsize; + uint32_t nodesize; + uint32_t leafsize; + uint32_t stripesize; + uint32_t sys_chunk_array_size; + uint64_t chunk_root_generation; + uint64_t compat_flags; + uint64_t compat_ro_flags; + uint64_t incompat_flags; + uint16_t csum_type; + uint8_t root_level; + uint8_t chunk_root_level; + uint8_t log_root_level; + struct btrfs_dev_item { + uint64_t devid; + uint64_t total_bytes; + uint64_t bytes_used; + uint32_t io_align; + uint32_t io_width; + uint32_t sector_size; + uint64_t type; + uint64_t generation; + uint64_t start_offset; + uint32_t dev_group; + uint8_t seek_speed; + uint8_t bandwidth; + uint8_t uuid[16]; + uint8_t fsid[16]; + } __attribute__ ((__packed__)) dev_item; + uint8_t label[256]; + uint8_t padding[3541]; /* pad to BTRFS_SUPER_INFO_SIZE for csum calculation */ +} __attribute__ ((__packed__)); + +#define BTRFS_SUPER_INFO_SIZE 4096 + +/* Number of superblock log zones */ +#define BTRFS_NR_SB_LOG_ZONES 2 + +/* Introduce some macros and types to unify the code with kernel side */ +#define SECTOR_SHIFT 9 + +typedef uint64_t sector_t; + +#ifdef HAVE_LINUX_BLKZONED_H +static int sb_write_pointer(blkid_probe pr, struct blk_zone *zones, uint64_t *wp_ret) +{ + bool empty[BTRFS_NR_SB_LOG_ZONES]; + bool full[BTRFS_NR_SB_LOG_ZONES]; + sector_t sector; + + assert(zones[0].type != BLK_ZONE_TYPE_CONVENTIONAL && + zones[1].type != BLK_ZONE_TYPE_CONVENTIONAL); + + empty[0] = zones[0].cond == BLK_ZONE_COND_EMPTY; + empty[1] = zones[1].cond == BLK_ZONE_COND_EMPTY; + full[0] = zones[0].cond == BLK_ZONE_COND_FULL; + full[1] = zones[1].cond == BLK_ZONE_COND_FULL; + + /* + * Possible states of log buffer zones + * + * Empty[0] In use[0] Full[0] + * Empty[1] * x 0 + * In use[1] 0 x 0 + * Full[1] 1 1 C + * + * Log position: + * *: Special case, no superblock is written + * 0: Use write pointer of zones[0] + * 1: Use write pointer of zones[1] + * C: Compare super blocks from zones[0] and zones[1], use the latest + * one determined by generation + * x: Invalid state + */ + + if (empty[0] && empty[1]) { + /* Special case to distinguish no superblock to read */ + *wp_ret = zones[0].start << SECTOR_SHIFT; + return -ENOENT; + } else if (full[0] && full[1]) { + /* Compare two super blocks */ + struct btrfs_super_block *super[BTRFS_NR_SB_LOG_ZONES]; + int i; + + for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) { + uint64_t bytenr; + + bytenr = ((zones[i].start + zones[i].len) + << SECTOR_SHIFT) - BTRFS_SUPER_INFO_SIZE; + + super[i] = (struct btrfs_super_block *) + blkid_probe_get_buffer(pr, bytenr, BTRFS_SUPER_INFO_SIZE); + if (!super[i]) + return -EIO; + DBG(LOWPROBE, ul_debug("(btrfs) checking #%d zone " + "[start=%" PRIu64", len=%" PRIu64", sb-offset=%" PRIu64"]", + i, (uint64_t) zones[i].start, + (uint64_t) zones[i].len, bytenr)); + } + + if (super[0]->generation > super[1]->generation) + sector = zones[1].start; + else + sector = zones[0].start; + } else if (!full[0] && (empty[1] || full[1])) { + sector = zones[0].wp; + } else if (full[0]) { + sector = zones[1].wp; + } else { + return -EUCLEAN; + } + *wp_ret = sector << SECTOR_SHIFT; + + DBG(LOWPROBE, ul_debug("(btrfs) write pointer: %" PRIu64" sector", sector)); + return 0; +} + +static int sb_log_offset(blkid_probe pr, uint64_t *bytenr_ret) +{ + uint32_t zone_num = 0; + uint32_t zone_size_sector; + struct blk_zone_report *rep; + struct blk_zone *zones; + int ret; + int i; + uint64_t wp; + + + zone_size_sector = pr->zone_size >> SECTOR_SHIFT; + rep = blkdev_get_zonereport(pr->fd, zone_num * zone_size_sector, 2); + if (!rep) { + ret = -errno; + goto out; + } + zones = (struct blk_zone *)(rep + 1); + + /* + * Use the head of the first conventional zone, if the zones + * contain one. + */ + for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) { + if (zones[i].type == BLK_ZONE_TYPE_CONVENTIONAL) { + DBG(LOWPROBE, ul_debug("(btrfs) checking conventional zone")); + *bytenr_ret = zones[i].start << SECTOR_SHIFT; + ret = 0; + goto out; + } + } + + ret = sb_write_pointer(pr, zones, &wp); + if (ret != -ENOENT && ret) { + ret = 1; + goto out; + } + if (ret != -ENOENT) { + if (wp == zones[0].start << SECTOR_SHIFT) + wp = (zones[1].start + zones[1].len) << SECTOR_SHIFT; + wp -= BTRFS_SUPER_INFO_SIZE; + } + *bytenr_ret = wp; + + ret = 0; +out: + free(rep); + + return ret; +} +#endif + +static int btrfs_verify_csum(blkid_probe pr, const struct btrfs_super_block *bfs) +{ + uint16_t csum_type = le16_to_cpu(bfs->csum_type); + const void *csum_data = (char *) bfs + sizeof(bfs->csum); + size_t csum_data_size = sizeof(*bfs) - sizeof(bfs->csum); + switch (csum_type) { + case BTRFS_SUPER_BLOCK_CSUM_TYPE_CRC32C: { + uint32_t crc = ~crc32c(~0L, csum_data, csum_data_size); + return blkid_probe_verify_csum(pr, crc, + le32_to_cpu(bfs->csum.crc32c)); + } + case BTRFS_SUPER_BLOCK_CSUM_TYPE_XXHASH: { + XXH64_hash_t xxh64 = XXH64(csum_data, csum_data_size, 0); + return blkid_probe_verify_csum(pr, xxh64, + le64_to_cpu(bfs->csum.xxh64)); + } + case BTRFS_SUPER_BLOCK_CSUM_TYPE_SHA256: { + uint8_t sha256[UL_SHA256LENGTH]; + ul_SHA256(sha256, csum_data, csum_data_size); + return blkid_probe_verify_csum_buf(pr, UL_SHA256LENGTH, + sha256, bfs->csum.sha256); + } + default: + DBG(LOWPROBE, ul_debug("(btrfs) unknown checksum type %d, skipping validation", + csum_type)); + return 1; + } +} + +static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct btrfs_super_block *bfs; + + if (pr->zone_size) { +#ifdef HAVE_LINUX_BLKZONED_H + uint64_t offset = 0; + int ret; + + ret = sb_log_offset(pr, &offset); + if (ret) + return ret; + bfs = (struct btrfs_super_block *) + blkid_probe_get_buffer(pr, offset, + sizeof(struct btrfs_super_block)); +#else + /* Nothing can be done */ + return 1; +#endif + } else { + bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block); + } + if (!bfs) + return errno ? -errno : 1; + + if (!btrfs_verify_csum(pr, bfs)) + return 1; + + /* Invalid sector size; total_bytes would be bogus. */ + if (!le32_to_cpu(bfs->sectorsize)) + return 1; + + if (*bfs->label) + blkid_probe_set_label(pr, + (unsigned char *) bfs->label, + sizeof(bfs->label)); + + blkid_probe_set_uuid(pr, bfs->fsid); + blkid_probe_set_uuid_as(pr, bfs->dev_item.uuid, "UUID_SUB"); + blkid_probe_set_fsblocksize(pr, le32_to_cpu(bfs->sectorsize)); + blkid_probe_set_block_size(pr, le32_to_cpu(bfs->sectorsize)); + + uint32_t sectorsize_log = 31 - + __builtin_clz(le32_to_cpu(bfs->sectorsize)); + blkid_probe_set_fslastblock(pr, + le64_to_cpu(bfs->total_bytes) >> sectorsize_log); + + /* The size is calculated without the RAID factor. It could not be + * obtained from the superblock as it is property of device tree. + * Without the factor we would show fs size with the redundant data. The + * acquisition of the factor will require additional parsing of btrfs + * tree. + */ + blkid_probe_set_fssize(pr, le64_to_cpu(bfs->total_bytes)); + + return 0; +} + +const struct blkid_idinfo btrfs_idinfo = +{ + .name = "btrfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_btrfs, + .minsz = 1024 * 1024, + .magics = + { + { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 64 }, + /* For zoned btrfs */ + { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, + .is_zoned = 1, .zonenum = 0, .kboff_inzone = 0 }, + { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, + .is_zoned = 1, .zonenum = 1, .kboff_inzone = 0 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c new file mode 100644 index 0000000..2a87acd --- /dev/null +++ b/libblkid/src/superblocks/cramfs.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" +#include "crc32.h" + +struct cramfs_super +{ + uint8_t magic[4]; + uint32_t size; + uint32_t flags; + uint32_t future; + uint8_t signature[16]; + struct cramfs_info + { + uint32_t crc; + uint32_t edition; + uint32_t blocks; + uint32_t files; + } __attribute__((packed)) info; + uint8_t name[16]; +} __attribute__((packed)); + +#define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */ + +static int cramfs_is_little_endian(const struct blkid_idmag *mag) +{ + assert(mag->len == 4); + return memcmp(mag->magic, "\x45\x3d\xcd\x28", 4) == 0; +} + +static uint32_t cfs32_to_cpu(int le, uint32_t value) +{ + if (le) + return le32_to_cpu(value); + else + return be32_to_cpu(value); +} + +static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, + struct cramfs_super *cs, int le) +{ + uint32_t crc, expected, csummed_size; + unsigned char *csummed; + + expected = cfs32_to_cpu(le, cs->info.crc); + csummed_size = cfs32_to_cpu(le, cs->size); + + if (csummed_size > (1 << 16) + || csummed_size < sizeof(struct cramfs_super)) + return 0; + + csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); + if (!csummed) + return 0; + memset(csummed + offsetof(struct cramfs_super, info.crc), 0, sizeof(uint32_t)); + + crc = ~ul_crc32(~0LL, csummed, csummed_size); + + return blkid_probe_verify_csum(pr, crc, expected); +} + +static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct cramfs_super *cs; + + cs = blkid_probe_get_sb(pr, mag, struct cramfs_super); + if (!cs) + return errno ? -errno : 1; + + int le = cramfs_is_little_endian(mag); + int v2 = cfs32_to_cpu(le, cs->flags) & CRAMFS_FLAG_FSID_VERSION_2; + + if (v2 && !cramfs_verify_csum(pr, mag, cs, le)) + return 1; + + blkid_probe_set_label(pr, cs->name, sizeof(cs->name)); + blkid_probe_set_fssize(pr, cfs32_to_cpu(le, cs->size)); + blkid_probe_sprintf_version(pr, "%d", v2 ? 2 : 1); + blkid_probe_set_fsendianness(pr, + le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG); + return 0; +} + +const struct blkid_idinfo cramfs_idinfo = +{ + .name = "cramfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_cramfs, + .magics = + { + { .magic = "\x45\x3d\xcd\x28", .len = 4 }, + { .magic = "\x28\xcd\x3d\x45", .len = 4 }, + { NULL } + } +}; + + diff --git a/libblkid/src/superblocks/cs_fvault2.c b/libblkid/src/superblocks/cs_fvault2.c new file mode 100644 index 0000000..ef2b567 --- /dev/null +++ b/libblkid/src/superblocks/cs_fvault2.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022 Milan Broz <gmazyland@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include "superblocks.h" +#include "crc32c.h" + +/* + * For header details, see: + * https://github.com/libyal/libfvde/blob/main/documentation/FileVault%20Drive%20Encryption%20(FVDE).asciidoc + * https://is.muni.cz/auth/th/p0aok/thesis.pdf + */ + +/* Apple Core Storage magic bytes */ +#define CS_MAGIC "CS" + +struct crc32_checksum { + uint32_t value; + uint32_t seed; +} __attribute__((packed)); + +/* + * The superblock structure describes "physical volume"; Core Storage + * then uses another abstractions above, similar to LVM. + * After activation through dm-crypt, filesystem (usually HFS+) is on top. + * The filesystem block size and used data size cannot be directly derived from + * this superblock structure without parsing other metadata blocks. + */ + +struct cs_fvault2_sb { + struct crc32_checksum checksum; + uint16_t version; + uint16_t block_type; + uint8_t unknown1[52]; + uint64_t ph_vol_size; + uint8_t unknown2[16]; + uint16_t magic; + uint32_t checksum_algo; + uint8_t unknown3[2]; + uint32_t block_size; + uint32_t metadata_size; + uint64_t disklbl_blkoff; + uint64_t other_md_blkoffs[3]; + uint8_t unknown4[32]; + uint32_t key_data_size; + uint32_t cipher; + uint8_t key_data[16]; + uint8_t unknown5[112]; + uint8_t ph_vol_uuid[16]; + uint8_t unknown6[192]; +} __attribute__((packed)); + +static int cs_fvault2_verify_csum(blkid_probe pr, const struct cs_fvault2_sb *sb) +{ + uint32_t seed = le32_to_cpu(sb->checksum.seed); + uint32_t crc = le32_to_cpu(sb->checksum.value); + unsigned char *buf = (unsigned char *)sb + sizeof(sb->checksum); + size_t buf_size = sizeof(*sb) - sizeof(sb->checksum); + + return blkid_probe_verify_csum(pr, crc32c(seed, buf, buf_size), crc); +} + +static int probe_cs_fvault2(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct cs_fvault2_sb *sb; + + sb = blkid_probe_get_sb(pr, mag, struct cs_fvault2_sb); + if (!sb) + return errno ? -errno : BLKID_PROBE_NONE; + + /* Apple Core storage Physical Volume Header; only type 1 checksum is supported */ + if (le16_to_cpu(sb->version) != 1 || + le32_to_cpu(sb->checksum_algo) != 1) + return BLKID_PROBE_NONE; + + if (!cs_fvault2_verify_csum(pr, sb)) + return BLKID_PROBE_NONE; + + /* We support only block type 0x10 as it should be used for FileVault2 */ + if (le16_to_cpu(sb->block_type) != 0x10 || + le32_to_cpu(sb->key_data_size) != 16 || + le32_to_cpu(sb->cipher) != 2 /* AES-XTS */) + return BLKID_PROBE_NONE; + + blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(sb->version)); + blkid_probe_set_uuid(pr, sb->ph_vol_uuid); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo cs_fvault2_idinfo = +{ + .name = "cs_fvault2", + .usage = BLKID_USAGE_CRYPTO, + .probefunc = probe_cs_fvault2, + .magics = + { + { .magic = CS_MAGIC, .len = 2, .sboff = 88 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c new file mode 100644 index 0000000..0b82e73 --- /dev/null +++ b/libblkid/src/superblocks/ddf_raid.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +/* http://www.snia.org/standards/home */ +#define DDF_GUID_LENGTH 24 +#define DDF_REV_LENGTH 8 +#define DDF_MAGIC 0xDE11DE11 + + +struct ddf_header { + uint32_t signature; + uint32_t crc; + uint8_t guid[DDF_GUID_LENGTH]; + char ddf_rev[8]; /* 01.02.00 */ + uint32_t seq; /* starts at '1' */ + uint32_t timestamp; + uint8_t openflag; + uint8_t foreignflag; + uint8_t enforcegroups; + uint8_t pad0; /* 0xff */ + uint8_t pad1[12]; /* 12 * 0xff */ + /* 64 bytes so far */ + uint8_t header_ext[32]; /* reserved: fill with 0xff */ + uint64_t primary_lba; + uint64_t secondary_lba; + uint8_t type; + uint8_t pad2[3]; /* 0xff */ + uint32_t workspace_len; /* sectors for vendor space - + * at least 32768(sectors) */ + uint64_t workspace_lba; + uint16_t max_pd_entries; /* one of 15, 63, 255, 1023, 4095 */ + uint16_t max_vd_entries; /* 2^(4,6,8,10,12)-1 : i.e. as above */ + uint16_t max_partitions; /* i.e. max num of configuration + record entries per disk */ + uint16_t config_record_len; /* 1 +ROUNDUP(max_primary_element_entries + *12/512) */ + uint16_t max_primary_element_entries; /* 16, 64, 256, 1024, or 4096 */ + uint8_t pad3[54]; /* 0xff */ + /* 192 bytes so far */ + uint32_t controller_section_offset; + uint32_t controller_section_length; + uint32_t phys_section_offset; + uint32_t phys_section_length; + uint32_t virt_section_offset; + uint32_t virt_section_length; + uint32_t config_section_offset; + uint32_t config_section_length; + uint32_t data_section_offset; + uint32_t data_section_length; + uint32_t bbm_section_offset; + uint32_t bbm_section_length; + uint32_t diag_space_offset; + uint32_t diag_space_length; + uint32_t vendor_offset; + uint32_t vendor_length; + /* 256 bytes so far */ + uint8_t pad4[256]; /* 0xff */ +} __attribute__((packed)); + +static int probe_ddf(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int hdrs[] = { 1, 257 }; + size_t i; + struct ddf_header *ddf = NULL; + char version[DDF_REV_LENGTH + 1]; + uint64_t off = 0, lba; + + if (pr->size < 0x30000) + return 1; + + for (i = 0; i < ARRAY_SIZE(hdrs); i++) { + off = ((pr->size / 0x200) - hdrs[i]) * 0x200; + + ddf = (struct ddf_header *) blkid_probe_get_buffer(pr, + off, + sizeof(struct ddf_header)); + if (!ddf) + return errno ? -errno : 1; + if (ddf->signature == cpu_to_be32(DDF_MAGIC) || + ddf->signature == cpu_to_le32(DDF_MAGIC)) + break; + ddf = NULL; + } + + if (!ddf) + return 1; + + lba = ddf->signature == cpu_to_be32(DDF_MAGIC) ? + be64_to_cpu(ddf->primary_lba) : + le64_to_cpu(ddf->primary_lba); + + if (lba > 0) { + /* check primary header */ + unsigned char *buf; + + buf = blkid_probe_get_buffer(pr, + lba << 9, sizeof(ddf->signature)); + if (!buf) + return errno ? -errno : 1; + + if (memcmp(buf, &ddf->signature, 4) != 0) + return 1; + } + + blkid_probe_strncpy_uuid(pr, ddf->guid, sizeof(ddf->guid)); + + memcpy(version, ddf->ddf_rev, sizeof(ddf->ddf_rev)); + *(version + sizeof(ddf->ddf_rev)) = '\0'; + + if (blkid_probe_set_version(pr, version) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, + sizeof(ddf->signature), + (unsigned char *) &ddf->signature)) + return 1; + return 0; +} + +const struct blkid_idinfo ddfraid_idinfo = { + .name = "ddf_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_ddf, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/drbd.c b/libblkid/src/superblocks/drbd.c new file mode 100644 index 0000000..f360186 --- /dev/null +++ b/libblkid/src/superblocks/drbd.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2009 by Bastian Friedrich <bastian.friedrich@collax.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * defines, structs taken from drbd source; file names represent drbd source + * files. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <stddef.h> + +#include "superblocks.h" + +/* + * drbd/linux/drbd.h + */ +#define DRBD_MAGIC 0x83740267 + +/* + * user/drbdmeta.c + * We support v08 and v09 + */ +#define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4) +#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5) +#define DRBD_MD_MAGIC_09 (DRBD_MAGIC+6) +/* there is no DRBD_MD_MAGIC_09_UNCLEAN */ + +/* + * drbd/linux/drbd.h + */ +enum drbd_uuid_index { + UI_CURRENT, + UI_BITMAP, + UI_HISTORY_START, + UI_HISTORY_END, + UI_SIZE, /* nl-packet: number of dirty bits */ + UI_FLAGS, /* nl-packet: flags */ + UI_EXTENDED_SIZE /* Everything. */ +}; + + +/* + * Used by libblkid to avoid unnecessary padding at the end of the structs and + * too large unused structs in memory. + */ +#define DRBD_MD_OFFSET 4096 + +/* + * user/shared/drbdmeta.c + * Minor modifications wrt. types + */ +struct md_on_disk_08 { + uint64_t la_sect; /* last agreed size. */ + uint64_t uuid[UI_SIZE]; /* UUIDs */ + uint64_t device_uuid; + uint64_t reserved_u64_1; + uint32_t flags; + uint32_t magic; + uint32_t md_size_sect; + int32_t al_offset; /* signed sector offset to this block */ + uint32_t al_nr_extents; /* important for restoring the AL */ + int32_t bm_offset; /* signed sector offset to the bitmap, from here */ + uint32_t bm_bytes_per_bit; + uint32_t reserved_u32[4]; + + /* Unnecessary for libblkid ** + * char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)]; + */ +}; + +/* + * linux/drbd.h, v9 only + */ +#define DRBD_PEERS_MAX 32 +#define HISTORY_UUIDS DRBD_PEERS_MAX + +/* + * drbd-headers/drbd_meta_data.h + * Minor modifications wrt. types + */ +struct peer_dev_md_on_disk_9 { + uint64_t bitmap_uuid; + uint64_t bitmap_dagtag; + uint32_t flags; + int32_t bitmap_index; + uint32_t reserved_u32[2]; +} __attribute__((packed)); + +struct meta_data_on_disk_9 { + uint64_t effective_size; /* last agreed size */ + uint64_t current_uuid; + uint64_t reserved_u64[4]; /* to have the magic at the same position as in v07, and v08 */ + uint64_t device_uuid; + uint32_t flags; /* MDF */ + uint32_t magic; + uint32_t md_size_sect; + uint32_t al_offset; /* offset to this block */ + uint32_t al_nr_extents; /* important for restoring the AL */ + uint32_t bm_offset; /* offset to the bitmap, from here */ + uint32_t bm_bytes_per_bit; /* BM_BLOCK_SIZE */ + uint32_t la_peer_max_bio_size; /* last peer max_bio_size */ + uint32_t bm_max_peers; + int32_t node_id; + + /* see al_tr_number_to_on_disk_sector() */ + uint32_t al_stripes; + uint32_t al_stripe_size_4k; + + uint32_t reserved_u32[2]; + + struct peer_dev_md_on_disk_9 peers[DRBD_PEERS_MAX]; + uint64_t history_uuids[HISTORY_UUIDS]; + + /* Unnecessary for libblkid ** + * char padding[0] __attribute__((aligned(4096))); + */ +} __attribute__((packed)); + + +static int probe_drbd_84(blkid_probe pr) +{ + struct md_on_disk_08 *md; + off_t off; + + off = pr->size - DRBD_MD_OFFSET; + + /* Small devices cannot be drbd (?) */ + if (pr->size < 0x10000) + return 1; + + md = (struct md_on_disk_08 *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct md_on_disk_08)); + if (!md) + return errno ? -errno : 1; + + if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 && + be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN) + return 1; + + /* + * DRBD does not have "real" uuids; the following resembles DRBD's + * notion of uuids (64 bit, see struct above) + */ + blkid_probe_sprintf_uuid(pr, + (unsigned char *) &md->device_uuid, sizeof(md->device_uuid), + "%" PRIx64, be64_to_cpu(md->device_uuid)); + + blkid_probe_set_version(pr, "v08"); + + if (blkid_probe_set_magic(pr, + off + offsetof(struct md_on_disk_08, magic), + sizeof(md->magic), + (unsigned char *) &md->magic)) + return 1; + + return 0; +} + +static int probe_drbd_90(blkid_probe pr) +{ + struct meta_data_on_disk_9 *md; + off_t off; + + off = pr->size - DRBD_MD_OFFSET; + + /* + * Smaller ones are certainly not DRBD9 devices. + * Recent utils even refuse to generate larger ones, + * keep this as a sufficient lower bound. + */ + if (pr->size < 0x10000) + return 1; + + md = (struct meta_data_on_disk_9 *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct meta_data_on_disk_9)); + if (!md) + return errno ? -errno : 1; + + if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_09) + return 1; + + /* + * DRBD does not have "real" uuids; the following resembles DRBD's + * notion of uuids (64 bit, see struct above) + */ + blkid_probe_sprintf_uuid(pr, + (unsigned char *) &md->device_uuid, sizeof(md->device_uuid), + "%" PRIx64, be64_to_cpu(md->device_uuid)); + + blkid_probe_set_version(pr, "v09"); + + if (blkid_probe_set_magic(pr, + off + offsetof(struct meta_data_on_disk_9, magic), + sizeof(md->magic), + (unsigned char *) &md->magic)) + return 1; + + return 0; +} + +static int probe_drbd(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int ret; + + ret = probe_drbd_84(pr); + if (ret <= 0) /* success or fatal (-errno) */ + return ret; + + return probe_drbd_90(pr); +} + +const struct blkid_idinfo drbd_idinfo = +{ + .name = "drbd", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_drbd, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/superblocks/drbdmanage.c b/libblkid/src/superblocks/drbdmanage.c new file mode 100644 index 0000000..d56c414 --- /dev/null +++ b/libblkid/src/superblocks/drbdmanage.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 by Philipp Marek <philipp.marek@linbit.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * DRBD is a blocklevel replication solution in the Linux kernel, + * upstream since 2.6.33. (See http://drbd.linbit.com/) + * DRBDmanage is a configuration frontend that assists in + * creating/deleting/modifying DRBD resources across multiple machines + * (a DRBDmanage "cluster"); this file detects its control volume, + * which is replicated (via DRBD 9) on some of the nodes. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <stddef.h> + +#include "bitops.h" +#include "superblocks.h" + +struct drbdmanage_hdr { + unsigned char magic[11]; + unsigned char uuid[32]; + unsigned char lf; +} __attribute__ ((packed)); + +struct drbdmanage_pers { + char magic[4]; + uint32_t version_le; +} __attribute__ ((packed)); + + +static const char persistence_magic[4] = { '\x1a', '\xdb', '\x98', '\xa2' }; + + +static int probe_drbdmanage(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct drbdmanage_hdr *hdr; + unsigned char *cp; + struct drbdmanage_pers *prs; + + hdr = (struct drbdmanage_hdr*) + blkid_probe_get_buffer(pr, 0, sizeof(*hdr)); + if (!hdr) + return errno ? -errno : 1; + + for(cp=hdr->uuid; cp<&hdr->lf; cp++) + if (!isxdigit(*cp)) + return 1; + if (hdr->lf != '\n') + return 1; + + if (blkid_probe_strncpy_uuid(pr, + hdr->uuid, sizeof(hdr->uuid))) + return errno ? -errno : 1; + + prs = (struct drbdmanage_pers*) + blkid_probe_get_buffer(pr, 0x1000, sizeof(*prs)); + if (!prs) + return errno ? -errno : 1; + + if (memcmp(prs->magic, persistence_magic, sizeof(prs->magic)) == 0 && + blkid_probe_sprintf_version(pr, "%d", be32_to_cpu(prs->version_le)) != 0) + return errno ? -errno : 1; + + return 0; +} + + +const struct blkid_idinfo drbdmanage_idinfo = +{ + .name = "drbdmanage_control_volume", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_drbdmanage, + .minsz = 64 * 1024, + .magics = { + { .magic = "$DRBDmgr=q", .len = 10, .sboff = 0 }, + { NULL } + }, +}; + diff --git a/libblkid/src/superblocks/drbdproxy_datalog.c b/libblkid/src/superblocks/drbdproxy_datalog.c new file mode 100644 index 0000000..9a4c7db --- /dev/null +++ b/libblkid/src/superblocks/drbdproxy_datalog.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 by Philipp Marek <philipp.marek@linbit.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <stddef.h> + +#include "superblocks.h" + + +struct log_header_t { + uint64_t magic; + uint64_t version; + + unsigned char uuid[16]; + + uint64_t flags; +} __attribute__((packed)); + + +static int probe_drbdproxy_datalog(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct log_header_t *lh; + + lh = (struct log_header_t *) blkid_probe_get_buffer(pr, 0, sizeof(*lh)); + if (!lh) + return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, lh->uuid); + blkid_probe_sprintf_version(pr, "v%"PRIu64, le64_to_cpu(lh->version)); + + return 0; +} + +const struct blkid_idinfo drbdproxy_datalog_idinfo = +{ + .name = "drbdproxy_datalog", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_drbdproxy_datalog, + .minsz = 16*1024, + .magics = + { + { .magic = "DRBDdlh*", .len = 8, .sboff = 0, .kboff = 0 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/erofs.c b/libblkid/src/superblocks/erofs.c new file mode 100644 index 0000000..14d272e --- /dev/null +++ b/libblkid/src/superblocks/erofs.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 Gao Xiang + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License + * + * https://docs.kernel.org/filesystems/erofs.html + */ +#include <stddef.h> +#include <string.h> + +#include "superblocks.h" +#include "crc32c.h" + +#define EROFS_SUPER_OFFSET 1024 +#define EROFS_SB_KBOFF (EROFS_SUPER_OFFSET >> 10) +#define EROFS_FEATURE_SB_CSUM (1 << 0) + +#define EROFS_SUPER_MAGIC_V1 "\xe2\xe1\xf5\xe0" +#define EROFS_MAGIC_OFF 0 + +/* All in little-endian */ +struct erofs_super_block { + uint32_t magic; + uint32_t checksum; + uint32_t feature_compat; + uint8_t blkszbits; + uint8_t reserved; + + uint16_t root_nid; + uint64_t inos; + + uint64_t build_time; + uint32_t build_time_nsec; + uint32_t blocks; + uint32_t meta_blkaddr; + uint32_t xattr_blkaddr; + uint8_t uuid[16]; + uint8_t volume_name[16]; + uint32_t feature_incompat; + uint8_t reserved2[44]; +} __attribute__((packed)); + +static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, + const struct erofs_super_block *sb) +{ + uint32_t expected, csum; + size_t csummed_size; + unsigned char *csummed; + + if (!(le32_to_cpu(sb->feature_compat) & EROFS_FEATURE_SB_CSUM)) + return 1; + + expected = le32_to_cpu(sb->checksum); + csummed_size = (1U << sb->blkszbits) - EROFS_SUPER_OFFSET; + + csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); + if (!csummed) + return 0; + + csum = ul_crc32c_exclude_offset(~0L, csummed, csummed_size, + offsetof(struct erofs_super_block, checksum), + sizeof_member(struct erofs_super_block, checksum)); + + return blkid_probe_verify_csum(pr, csum, expected); +} + +static int probe_erofs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct erofs_super_block *sb; + + sb = blkid_probe_get_sb(pr, mag, struct erofs_super_block); + if (!sb) + return errno ? -errno : BLKID_PROBE_NONE; + + /* EROFS is restricted to 4KiB block size */ + if (sb->blkszbits > 31 || (1U << sb->blkszbits) > 4096) + return BLKID_PROBE_NONE; + + if (!erofs_verify_checksum(pr, mag, sb)) + return BLKID_PROBE_NONE; + + if (sb->volume_name[0]) + blkid_probe_set_label(pr, (unsigned char *)sb->volume_name, + sizeof(sb->volume_name)); + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_set_fsblocksize(pr, 1U << sb->blkszbits); + blkid_probe_set_block_size(pr, 1U << sb->blkszbits); + blkid_probe_set_fssize(pr, (uint64_t) (1U << sb->blkszbits) * le32_to_cpu(sb->blocks)); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo erofs_idinfo = +{ + .name = "erofs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_erofs, + .magics = + { + { + .magic = EROFS_SUPER_MAGIC_V1, + .len = 4, + .kboff = EROFS_SB_KBOFF, + .sboff = EROFS_MAGIC_OFF, + }, { NULL } + } +}; diff --git a/libblkid/src/superblocks/exfat.c b/libblkid/src/superblocks/exfat.c new file mode 100644 index 0000000..fda1ecd --- /dev/null +++ b/libblkid/src/superblocks/exfat.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include "superblocks.h" + +struct exfat_super_block { + uint8_t JumpBoot[3]; + uint8_t FileSystemName[8]; + uint8_t MustBeZero[53]; + uint64_t PartitionOffset; + uint64_t VolumeLength; + uint32_t FatOffset; + uint32_t FatLength; + uint32_t ClusterHeapOffset; + uint32_t ClusterCount; + uint32_t FirstClusterOfRootDirectory; + uint8_t VolumeSerialNumber[4]; + struct { + uint8_t vermin; + uint8_t vermaj; + } FileSystemRevision; + uint16_t VolumeFlags; + uint8_t BytesPerSectorShift; + uint8_t SectorsPerClusterShift; + uint8_t NumberOfFats; + uint8_t DriveSelect; + uint8_t PercentInUse; + uint8_t Reserved[7]; + uint8_t BootCode[390]; + uint16_t BootSignature; +} __attribute__((__packed__)); + +struct exfat_entry_label { + uint8_t type; + uint8_t length; + uint8_t name[22]; + uint8_t reserved[8]; +} __attribute__((__packed__)); + +#define BLOCK_SIZE(sb) ((sb)->BytesPerSectorShift < 32 ? (1u << (sb)->BytesPerSectorShift) : 0) +#define CLUSTER_SIZE(sb) ((sb)->SectorsPerClusterShift < 32 ? (BLOCK_SIZE(sb) << (sb)->SectorsPerClusterShift) : 0) +#define EXFAT_FIRST_DATA_CLUSTER 2 +#define EXFAT_LAST_DATA_CLUSTER 0xffffff6 +#define EXFAT_ENTRY_SIZE 32 + +#define EXFAT_ENTRY_EOD 0x00 +#define EXFAT_ENTRY_LABEL 0x83 + +#define EXFAT_MAX_DIR_SIZE (256 * 1024 * 1024) + +static uint64_t block_to_offset(const struct exfat_super_block *sb, + uint64_t block) +{ + return block << sb->BytesPerSectorShift; +} + +static uint64_t cluster_to_block(const struct exfat_super_block *sb, + uint32_t cluster) +{ + return le32_to_cpu(sb->ClusterHeapOffset) + + ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) + << sb->SectorsPerClusterShift); +} + +static uint64_t cluster_to_offset(const struct exfat_super_block *sb, + uint32_t cluster) +{ + return block_to_offset(sb, cluster_to_block(sb, cluster)); +} + +static uint32_t next_cluster(blkid_probe pr, + const struct exfat_super_block *sb, uint32_t cluster) +{ + uint32_t *nextp, next; + uint64_t fat_offset; + + fat_offset = block_to_offset(sb, le32_to_cpu(sb->FatOffset)) + + (uint64_t) cluster * sizeof(cluster); + nextp = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset, + sizeof(uint32_t)); + if (!nextp) + return 0; + memcpy(&next, nextp, sizeof(next)); + return le32_to_cpu(next); +} + +static struct exfat_entry_label *find_label(blkid_probe pr, + const struct exfat_super_block *sb) +{ + uint32_t cluster = le32_to_cpu(sb->FirstClusterOfRootDirectory); + uint64_t offset = cluster_to_offset(sb, cluster); + uint8_t *entry; + const size_t max_iter = EXFAT_MAX_DIR_SIZE / EXFAT_ENTRY_SIZE; + size_t i = 0; + + for (; i < max_iter; i++) { + entry = (uint8_t *) blkid_probe_get_buffer(pr, offset, + EXFAT_ENTRY_SIZE); + if (!entry) + return NULL; + if (entry[0] == EXFAT_ENTRY_EOD) + return NULL; + if (entry[0] == EXFAT_ENTRY_LABEL) + return (struct exfat_entry_label *) entry; + + offset += EXFAT_ENTRY_SIZE; + if (CLUSTER_SIZE(sb) && (offset % CLUSTER_SIZE(sb)) == 0) { + cluster = next_cluster(pr, sb, cluster); + if (cluster < EXFAT_FIRST_DATA_CLUSTER) + return NULL; + if (cluster > EXFAT_LAST_DATA_CLUSTER) + return NULL; + offset = cluster_to_offset(sb, cluster); + } + } + + return NULL; +} + +/* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */ +static uint32_t exfat_boot_checksum(unsigned char *sectors, + size_t sector_size) +{ + uint32_t n_bytes = sector_size * 11; + uint32_t checksum = 0; + + for (size_t i = 0; i < n_bytes; i++) { + if ((i == 106) || (i == 107) || (i == 112)) + continue; + + checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + + (uint32_t) sectors[i]; + } + + return checksum; +} + +static int exfat_validate_checksum(blkid_probe pr, + const struct exfat_super_block *sb) +{ + size_t sector_size = BLOCK_SIZE(sb); + /* 11 sectors will be checksummed, the 12th contains the expected */ + unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12); + if (!data) + return 0; + + uint32_t checksum = exfat_boot_checksum(data, sector_size); + + /* The expected checksum is repeated, check all of them */ + for (size_t i = 0; i < sector_size / sizeof(uint32_t); i++) { + size_t offset = sector_size * 11 + i * 4; + uint32_t *expected_addr = (uint32_t *) &data[offset]; + uint32_t expected = le32_to_cpu(*expected_addr); + if (!blkid_probe_verify_csum(pr, checksum, expected)) + return 0; + }; + + return 1; +} + +static int exfat_valid_superblock(blkid_probe pr, const struct exfat_super_block *sb) +{ + if (le16_to_cpu(sb->BootSignature) != 0xAA55) + return 0; + + if (!CLUSTER_SIZE(sb)) + return 0; + + if (memcmp(sb->JumpBoot, "\xEB\x76\x90", 3) != 0) + return 0; + + for (size_t i = 0; i < sizeof(sb->MustBeZero); i++) + if (sb->MustBeZero[i] != 0x00) + return 0; + + if (!exfat_validate_checksum(pr, sb)) + return 0; + + return 1; +} + +/* function prototype to avoid warnings (duplicate in partitions/dos.c) */ +extern int blkid_probe_is_exfat(blkid_probe pr); + +/* + * This function is used by MBR partition table parser to avoid + * misinterpretation of exFAT filesystem. + */ +int blkid_probe_is_exfat(blkid_probe pr) +{ + struct exfat_super_block *sb; + const struct blkid_idmag *mag = NULL; + int rc; + + rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag); + if (rc < 0) + return rc; /* error */ + if (rc != BLKID_PROBE_OK || !mag) + return 0; + + sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); + if (!sb) + return 0; + + if (memcmp(sb->FileSystemName, "EXFAT ", 8) != 0) + return 0; + + return exfat_valid_superblock(pr, sb); +} + +static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct exfat_super_block *sb; + struct exfat_entry_label *label; + + sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); + if (!sb) + return errno ? -errno : BLKID_PROBE_NONE; + + if (!exfat_valid_superblock(pr, sb)) + return BLKID_PROBE_NONE; + + label = find_label(pr, sb); + if (label) + blkid_probe_set_utf8label(pr, label->name, + min((size_t) label->length * 2, sizeof(label->name)), + UL_ENCODE_UTF16LE); + else if (errno) + return -errno; + + blkid_probe_sprintf_uuid(pr, sb->VolumeSerialNumber, 4, + "%02hhX%02hhX-%02hhX%02hhX", + sb->VolumeSerialNumber[3], sb->VolumeSerialNumber[2], + sb->VolumeSerialNumber[1], sb->VolumeSerialNumber[0]); + + blkid_probe_sprintf_version(pr, "%u.%u", + sb->FileSystemRevision.vermaj, sb->FileSystemRevision.vermin); + + blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb)); + blkid_probe_set_block_size(pr, BLOCK_SIZE(sb)); + blkid_probe_set_fssize(pr, BLOCK_SIZE(sb) * le64_to_cpu(sb->VolumeLength)); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo exfat_idinfo = +{ + .name = "exfat", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_exfat, + .magics = + { + { .magic = "EXFAT ", .len = 8, .sboff = 3 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/exfs.c b/libblkid/src/superblocks/exfs.c new file mode 100644 index 0000000..87dc0a0 --- /dev/null +++ b/libblkid/src/superblocks/exfs.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2013 Eric Sandeen <sandeen@redhat.com> + * Copyright (C) 2017 Hewlett Packard Enterprise Development LP + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct exfs_super_block { + uint32_t sb_magicnum; /* magic number == EXFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + uint64_t sb_dblocks; /* number of data blocks */ + uint64_t sb_rblocks; /* number of realtime blocks */ + uint64_t sb_rextents; /* number of realtime extents */ + unsigned char sb_uuid[16]; /* file system unique id */ + uint64_t sb_logstart; /* starting block of log if internal */ + uint64_t sb_rootino; /* root inode number */ + uint64_t sb_rbmino; /* bitmap inode for realtime extents */ + uint64_t sb_rsumino; /* summary inode for rt bitmap */ + uint32_t sb_rextsize; /* realtime extent size, blocks */ + uint32_t sb_agblocks; /* size of an allocation group */ + uint32_t sb_agcount; /* number of allocation groups */ + uint32_t sb_rbmblocks; /* number of rt bitmap blocks */ + uint32_t sb_logblocks; /* number of log blocks */ + + uint16_t sb_versionnum; /* header version == EXFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + + /* this is not all... but enough for libblkid */ + +} __attribute__((packed)); + +#define EXFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ +#define EXFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ +#define EXFS_MIN_BLOCKSIZE (1 << EXFS_MIN_BLOCKSIZE_LOG) +#define EXFS_MAX_BLOCKSIZE (1 << EXFS_MAX_BLOCKSIZE_LOG) +#define EXFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ +#define EXFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ +#define EXFS_MIN_SECTORSIZE (1 << EXFS_MIN_SECTORSIZE_LOG) +#define EXFS_MAX_SECTORSIZE (1 << EXFS_MAX_SECTORSIZE_LOG) + +#define EXFS_DINODE_MIN_LOG 8 +#define EXFS_DINODE_MAX_LOG 11 +#define EXFS_DINODE_MIN_SIZE (1 << EXFS_DINODE_MIN_LOG) +#define EXFS_DINODE_MAX_SIZE (1 << EXFS_DINODE_MAX_LOG) + +#define EXFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */ +#define EXFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */ +#define EXFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */ + +#define EXFS_MIN_AG_BLOCKS 64 +#define EXFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks) +#define EXFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \ + (s)->sb_agblocks + EXFS_MIN_AG_BLOCKS) + + +static void sb_from_disk(struct exfs_super_block *from, + struct exfs_super_block *to) +{ + + to->sb_magicnum = be32_to_cpu(from->sb_magicnum); + to->sb_blocksize = be32_to_cpu(from->sb_blocksize); + to->sb_dblocks = be64_to_cpu(from->sb_dblocks); + to->sb_rblocks = be64_to_cpu(from->sb_rblocks); + to->sb_rextents = be64_to_cpu(from->sb_rextents); + to->sb_logstart = be64_to_cpu(from->sb_logstart); + to->sb_rootino = be64_to_cpu(from->sb_rootino); + to->sb_rbmino = be64_to_cpu(from->sb_rbmino); + to->sb_rsumino = be64_to_cpu(from->sb_rsumino); + to->sb_rextsize = be32_to_cpu(from->sb_rextsize); + to->sb_agblocks = be32_to_cpu(from->sb_agblocks); + to->sb_agcount = be32_to_cpu(from->sb_agcount); + to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks); + to->sb_logblocks = be32_to_cpu(from->sb_logblocks); + to->sb_versionnum = be16_to_cpu(from->sb_versionnum); + to->sb_sectsize = be16_to_cpu(from->sb_sectsize); + to->sb_inodesize = be16_to_cpu(from->sb_inodesize); + to->sb_inopblock = be16_to_cpu(from->sb_inopblock); + to->sb_blocklog = from->sb_blocklog; + to->sb_sectlog = from->sb_sectlog; + to->sb_inodelog = from->sb_inodelog; + to->sb_inopblog = from->sb_inopblog; + to->sb_agblklog = from->sb_agblklog; + to->sb_rextslog = from->sb_rextslog; + to->sb_inprogress = from->sb_inprogress; + to->sb_imax_pct = from->sb_imax_pct; + to->sb_icount = be64_to_cpu(from->sb_icount); + to->sb_ifree = be64_to_cpu(from->sb_ifree); + to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks); + to->sb_frextents = be64_to_cpu(from->sb_frextents); +} + +static int exfs_verify_sb(struct exfs_super_block *ondisk) +{ + struct exfs_super_block sb, *sbp = &sb; + + /* beXX_to_cpu(), but don't convert UUID and fsname! */ + sb_from_disk(ondisk, sbp); + + /* sanity checks, we don't want to rely on magic string only */ + if (sbp->sb_agcount <= 0 || + sbp->sb_sectsize < EXFS_MIN_SECTORSIZE || + sbp->sb_sectsize > EXFS_MAX_SECTORSIZE || + sbp->sb_sectlog < EXFS_MIN_SECTORSIZE_LOG || + sbp->sb_sectlog > EXFS_MAX_SECTORSIZE_LOG || + sbp->sb_sectsize != (1 << sbp->sb_sectlog) || + sbp->sb_blocksize < EXFS_MIN_BLOCKSIZE || + sbp->sb_blocksize > EXFS_MAX_BLOCKSIZE || + sbp->sb_blocklog < EXFS_MIN_BLOCKSIZE_LOG || + sbp->sb_blocklog > EXFS_MAX_BLOCKSIZE_LOG || + sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) || + sbp->sb_inodesize < EXFS_DINODE_MIN_SIZE || + sbp->sb_inodesize > EXFS_DINODE_MAX_SIZE || + sbp->sb_inodelog < EXFS_DINODE_MIN_LOG || + sbp->sb_inodelog > EXFS_DINODE_MAX_LOG || + sbp->sb_inodesize != (1 << sbp->sb_inodelog) || + (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) || + (sbp->sb_rextsize * sbp->sb_blocksize > EXFS_MAX_RTEXTSIZE) || + (sbp->sb_rextsize * sbp->sb_blocksize < EXFS_MIN_RTEXTSIZE) || + (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) || + sbp->sb_dblocks == 0 || + sbp->sb_dblocks > EXFS_MAX_DBLOCKS(sbp) || + sbp->sb_dblocks < EXFS_MIN_DBLOCKS(sbp)) + return 0; + return 1; +} + +static int probe_exfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct exfs_super_block *xs; + + xs = blkid_probe_get_sb(pr, mag, struct exfs_super_block); + if (!xs) + return errno ? -errno : 1; + + if (!exfs_verify_sb(xs)) + return 1; + + if (*xs->sb_fname != '\0') + blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname, + sizeof(xs->sb_fname)); + + blkid_probe_set_uuid(pr, xs->sb_uuid); + + blkid_probe_set_fsblocksize(pr, be32_to_cpu(xs->sb_blocksize)); + blkid_probe_set_block_size(pr, be32_to_cpu(xs->sb_blocksize)); + + return 0; +} + +const struct blkid_idinfo exfs_idinfo = +{ + .name = "exfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_exfs, + .magics = + { + { .magic = "EXFS", .len = 4 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/ext.c b/libblkid/src/superblocks/ext.c new file mode 100644 index 0000000..885fec2 --- /dev/null +++ b/libblkid/src/superblocks/ext.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#ifdef __linux__ +#include <sys/utsname.h> +#endif +#include <time.h> + +#include "superblocks.h" +#include "crc32c.h" + +struct ext2_super_block { + uint32_t s_inodes_count; + uint32_t s_blocks_count; + uint32_t s_r_blocks_count; + uint32_t s_free_blocks_count; + uint32_t s_free_inodes_count; + uint32_t s_first_data_block; + uint32_t s_log_block_size; + uint32_t s_dummy3[7]; + unsigned char s_magic[2]; + uint16_t s_state; + uint16_t s_errors; + uint16_t s_minor_rev_level; + uint32_t s_lastcheck; + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint32_t s_rev_level; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + uint16_t s_inode_size; + uint16_t s_block_group_nr; + uint32_t s_feature_compat; + uint32_t s_feature_incompat; + uint32_t s_feature_ro_compat; + unsigned char s_uuid[16]; + char s_volume_name[16]; + char s_last_mounted[64]; + uint32_t s_algorithm_usage_bitmap; + uint8_t s_prealloc_blocks; + uint8_t s_prealloc_dir_blocks; + uint16_t s_reserved_gdt_blocks; + uint8_t s_journal_uuid[16]; + uint32_t s_journal_inum; + uint32_t s_journal_dev; + uint32_t s_last_orphan; + uint32_t s_hash_seed[4]; + uint8_t s_def_hash_version; + uint8_t s_jnl_backup_type; + uint16_t s_reserved_word_pad; + uint32_t s_default_mount_opts; + uint32_t s_first_meta_bg; + uint32_t s_mkfs_time; + uint32_t s_jnl_blocks[17]; + uint32_t s_blocks_count_hi; + uint32_t s_r_blocks_count_hi; + uint32_t s_free_blocks_hi; + uint16_t s_min_extra_isize; + uint16_t s_want_extra_isize; + uint32_t s_flags; + uint16_t s_raid_stride; + uint16_t s_mmp_interval; + uint64_t s_mmp_block; + uint32_t s_raid_stripe_width; + uint32_t s_reserved[162]; + uint32_t s_checksum; +} __attribute__((packed)); + +/* magic string */ +#define EXT_SB_MAGIC "\123\357" +/* supper block offset */ +#define EXT_SB_OFF 0x400 +/* supper block offset in kB */ +#define EXT_SB_KBOFF (EXT_SB_OFF >> 10) +/* magic string offset within super block */ +#define EXT_MAG_OFF 0x38 + + + +/* for s_flags */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 + +/* for s_feature_compat */ +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 + +/* for s_feature_ro_compat */ +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 + +/* for s_feature_incompat */ +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 + +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP +#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP + +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT3_FEATURE_INCOMPAT_RECOVER| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP +#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP + +/* + * Starting in 2.6.29, ext4 can be used to support filesystems + * without a journal. + */ +#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) + +/* + * reads superblock and returns: + * fc = feature_compat + * fi = feature_incompat + * frc = feature_ro_compat + */ +static struct ext2_super_block *ext_get_super( + blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) +{ + struct ext2_super_block *es; + + es = (struct ext2_super_block *) + blkid_probe_get_buffer(pr, EXT_SB_OFF, sizeof(struct ext2_super_block)); + if (!es) + return NULL; + if (le32_to_cpu(es->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { + uint32_t csum = crc32c(~0, es, offsetof(struct ext2_super_block, s_checksum)); + if (!blkid_probe_verify_csum(pr, csum, le32_to_cpu(es->s_checksum))) + return NULL; + } + if (fc) + *fc = le32_to_cpu(es->s_feature_compat); + if (fi) + *fi = le32_to_cpu(es->s_feature_incompat); + if (frc) + *frc = le32_to_cpu(es->s_feature_ro_compat); + + return es; +} + +static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + uint32_t s_feature_incompat = le32_to_cpu(es->s_feature_incompat); + + DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X", + le32_to_cpu(es->s_feature_compat), + s_feature_incompat, + le32_to_cpu(es->s_feature_ro_compat))); + + if (*es->s_volume_name != '\0') + blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name, + sizeof(es->s_volume_name)); + blkid_probe_set_uuid(pr, es->s_uuid); + + if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL"); + + if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) && + ((s_feature_incompat & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) + blkid_probe_set_value(pr, "SEC_TYPE", + (unsigned char *) "ext2", + sizeof("ext2")); + + blkid_probe_sprintf_version(pr, "%u.%u", + le32_to_cpu(es->s_rev_level), + le16_to_cpu(es->s_minor_rev_level)); + + uint32_t block_size = 0; + if (le32_to_cpu(es->s_log_block_size) < 32){ + block_size = 1024U << le32_to_cpu(es->s_log_block_size); + blkid_probe_set_fsblocksize(pr, block_size); + blkid_probe_set_block_size(pr, block_size); + } + + uint64_t fslastblock = le32_to_cpu(es->s_blocks_count) | + ((s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? + (uint64_t) le32_to_cpu(es->s_blocks_count_hi) << 32 : 0); + blkid_probe_set_fslastblock(pr, fslastblock); + + /* The total number of blocks is taken without substraction of overhead + * (journal, metadata). The ext4 has non-trivial overhead calculation + * viz. ext4_calculate_overhead(). Thefore, the FSSIZE would show number + * slightly higher than the real value (for example, calculated via + * statfs()). + */ + uint64_t fssize = (uint64_t) block_size * le32_to_cpu(es->s_blocks_count); + blkid_probe_set_fssize(pr, fssize); +} + + +static int probe_jbd(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fi; + + es = ext_get_super(pr, NULL, &fi, NULL); + if (!es) + return errno ? -errno : 1; + if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) + return 1; + + ext_get_info(pr, 2, es); + blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID"); + + return 0; +} + +static int probe_ext2(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return errno ? -errno : 1; + + /* Distinguish between ext3 and ext2 */ + if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + return 1; + + /* Any features which ext2 doesn't understand */ + if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) + return 1; + + ext_get_info(pr, 2, es); + return 0; +} + +static int probe_ext3(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return errno ? -errno : 1; + + /* ext3 requires journal */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return 1; + + /* Any features which ext3 doesn't understand */ + if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return 1; + + ext_get_info(pr, 3, es); + return 0; +} + + +static int probe_ext4dev(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return errno ? -errno : 1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return 1; + + if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) + return 1; + + ext_get_info(pr, 4, es); + return 0; +} + +static int probe_ext4(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return errno ? -errno : 1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return 1; + + /* Ext4 has at least one feature which ext3 doesn't understand */ + if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && + !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return 1; + + /* + * If the filesystem is a OK for use by in-development + * filesystem code, and ext4dev is supported or ext4 is not + * supported, then don't call ourselves ext4, so we can redo + * the detection and mark the filesystem as ext4dev. + * + * If the filesystem is marked as in use by production + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) + return 1; + + ext_get_info(pr, 4, es); + return 0; +} + +#define BLKID_EXT_MAGICS \ + { \ + { \ + .magic = EXT_SB_MAGIC, \ + .len = sizeof(EXT_SB_MAGIC) - 1, \ + .kboff = EXT_SB_KBOFF, \ + .sboff = EXT_MAG_OFF \ + }, \ + { NULL } \ + } + +const struct blkid_idinfo jbd_idinfo = +{ + .name = "jbd", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_jbd, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext2_idinfo = +{ + .name = "ext2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext2, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext3_idinfo = +{ + .name = "ext3", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext3, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4_idinfo = +{ + .name = "ext4", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4dev_idinfo = +{ + .name = "ext4dev", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4dev, + .magics = BLKID_EXT_MAGICS +}; + diff --git a/libblkid/src/superblocks/f2fs.c b/libblkid/src/superblocks/f2fs.c new file mode 100644 index 0000000..980111e --- /dev/null +++ b/libblkid/src/superblocks/f2fs.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2013 Alejandro Martinez Ruiz <alex@nowcomputing.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License + */ + +#include <stddef.h> +#include <string.h> + +#include "superblocks.h" +#include "crc32.h" + +#define F2FS_MAGIC "\x10\x20\xF5\xF2" +#define F2FS_MAGIC_OFF 0 +#define F2FS_UUID_SIZE 16 +#define F2FS_LABEL_SIZE 512 +#define F2FS_SB1_OFF 0x400 +#define F2FS_SB1_KBOFF (F2FS_SB1_OFF >> 10) +#define F2FS_SB2_OFF 0x1400 +#define F2FS_SB2_KBOFF (F2FS_SB2_OFF >> 10) + +struct f2fs_super_block { /* According to version 1.1 */ +/* 0x00 */ uint32_t magic; /* Magic Number */ +/* 0x04 */ uint16_t major_ver; /* Major Version */ +/* 0x06 */ uint16_t minor_ver; /* Minor Version */ +/* 0x08 */ uint32_t log_sectorsize; /* log2 sector size in bytes */ +/* 0x0C */ uint32_t log_sectors_per_block; /* log2 # of sectors per block */ +/* 0x10 */ uint32_t log_blocksize; /* log2 block size in bytes */ +/* 0x14 */ uint32_t log_blocks_per_seg; /* log2 # of blocks per segment */ +/* 0x18 */ uint32_t segs_per_sec; /* # of segments per section */ +/* 0x1C */ uint32_t secs_per_zone; /* # of sections per zone */ +/* 0x20 */ uint32_t checksum_offset; /* checksum offset inside super block */ +/* 0x24 */ uint64_t block_count; /* total # of user blocks */ +/* 0x2C */ uint32_t section_count; /* total # of sections */ +/* 0x30 */ uint32_t segment_count; /* total # of segments */ +/* 0x34 */ uint32_t segment_count_ckpt; /* # of segments for checkpoint */ +/* 0x38 */ uint32_t segment_count_sit; /* # of segments for SIT */ +/* 0x3C */ uint32_t segment_count_nat; /* # of segments for NAT */ +/* 0x40 */ uint32_t segment_count_ssa; /* # of segments for SSA */ +/* 0x44 */ uint32_t segment_count_main; /* # of segments for main area */ +/* 0x48 */ uint32_t segment0_blkaddr; /* start block address of segment 0 */ +/* 0x4C */ uint32_t cp_blkaddr; /* start block address of checkpoint */ +/* 0x50 */ uint32_t sit_blkaddr; /* start block address of SIT */ +/* 0x54 */ uint32_t nat_blkaddr; /* start block address of NAT */ +/* 0x58 */ uint32_t ssa_blkaddr; /* start block address of SSA */ +/* 0x5C */ uint32_t main_blkaddr; /* start block address of main area */ +/* 0x60 */ uint32_t root_ino; /* root inode number */ +/* 0x64 */ uint32_t node_ino; /* node inode number */ +/* 0x68 */ uint32_t meta_ino; /* meta inode number */ +/* 0x6C */ uint8_t uuid[F2FS_UUID_SIZE]; /* 128-bit uuid for volume */ +/* 0x7C */ uint16_t volume_name[F2FS_LABEL_SIZE]; /* volume name */ +#if 0 +/* 0x47C */ uint32_t extension_count; /* # of extensions below */ +/* 0x480 */ uint8_t extension_list[64][8]; /* extension array */ +#endif +} __attribute__((packed)); + +static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off, + const struct f2fs_super_block *sb) +{ + uint32_t csum_off = le32_to_cpu(sb->checksum_offset); + if (!csum_off) + return 1; + if (csum_off % sizeof(uint32_t) != 0) + return 0; + if (csum_off + sizeof(uint32_t) > 4096) + return 0; + + unsigned char *csum_data = blkid_probe_get_buffer(pr, + sb_off + csum_off, sizeof(uint32_t)); + if (!csum_data) + return 0; + + uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data); + + unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off); + if (!csummed) + return 0; + + uint32_t csum = ul_crc32(0xF2F52010, csummed, csum_off); + + return blkid_probe_verify_csum(pr, csum, expected); +} + +static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct f2fs_super_block *sb; + uint16_t vermaj, vermin; + + sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block); + if (!sb) + return errno ? -errno : 1; + + vermaj = le16_to_cpu(sb->major_ver); + vermin = le16_to_cpu(sb->minor_ver); + + /* For version 1.0 we cannot know the correct sb structure */ + if (vermaj == 1 && vermin == 0) + return 0; + + if (!f2fs_validate_checksum(pr, mag->kboff << 10, sb)) + return 1; + + if (*((unsigned char *) sb->volume_name)) + blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name, + sizeof(sb->volume_name), + UL_ENCODE_UTF16LE); + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin); + if (le32_to_cpu(sb->log_blocksize) < 32){ + uint32_t blocksize = 1U << le32_to_cpu(sb->log_blocksize); + blkid_probe_set_fsblocksize(pr, blocksize); + blkid_probe_set_block_size(pr, blocksize); + blkid_probe_set_fssize(pr, le64_to_cpu(sb->block_count) * blocksize); + } + return 0; +} + +const struct blkid_idinfo f2fs_idinfo = +{ + .name = "f2fs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_f2fs, + .magics = + { + { + .magic = F2FS_MAGIC, + .len = 4, + .kboff = F2FS_SB1_KBOFF, + .sboff = F2FS_MAGIC_OFF + }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/gfs.c b/libblkid/src/superblocks/gfs.c new file mode 100644 index 0000000..445c0da --- /dev/null +++ b/libblkid/src/superblocks/gfs.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +/* Common gfs/gfs2 constants: */ +#define GFS_LOCKNAME_LEN 64 + +/* gfs1 constants: */ +#define GFS_FORMAT_FS 1309 +#define GFS_FORMAT_MULTI 1401 + +struct gfs2_meta_header { + uint32_t mh_magic; + uint32_t mh_type; + uint64_t __pad0; /* Was generation number in gfs1 */ + uint32_t mh_format; + uint32_t __pad1; /* Was incarnation number in gfs1 */ +}; + +struct gfs2_inum { + uint64_t no_formal_ino; + uint64_t no_addr; +}; + +struct gfs2_sb { + struct gfs2_meta_header sb_header; + + uint32_t sb_fs_format; + uint32_t sb_multihost_format; + uint32_t __pad0; /* Was superblock flags in gfs1 */ + + uint32_t sb_bsize; + uint32_t sb_bsize_shift; + uint32_t __pad1; /* Was journal segment size in gfs1 */ + + struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */ + struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */ + struct gfs2_inum sb_root_dir; + + char sb_lockproto[GFS_LOCKNAME_LEN]; + char sb_locktable[GFS_LOCKNAME_LEN]; + + struct gfs2_inum __pad3; /* Was quota inode in gfs1 */ + struct gfs2_inum __pad4; /* Was license inode in gfs1 */ + uint8_t sb_uuid[16]; /* The UUID maybe 0 for backwards compat */ +} __attribute__((packed)); + + + +static int probe_gfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct gfs2_sb *sbd; + + sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); + if (!sbd) + return errno ? -errno : 1; + + if (be32_to_cpu(sbd->sb_fs_format) == GFS_FORMAT_FS && + be32_to_cpu(sbd->sb_multihost_format) == GFS_FORMAT_MULTI) + { + if (*sbd->sb_locktable) + blkid_probe_set_label(pr, + (unsigned char *) sbd->sb_locktable, + sizeof(sbd->sb_locktable)); + + blkid_probe_set_uuid(pr, sbd->sb_uuid); + return 0; + } + + return 1; +} + +static inline int gfs2_format_is_valid(uint32_t format) +{ + return (format >= 1800 && format < 1900); +} +static inline int gfs2_multiformat_is_valid(uint32_t multi) +{ + return (multi >= 1900 && multi < 2000); +} + +static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct gfs2_sb *sbd; + + sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); + if (!sbd) + return errno ? -errno : 1; + + if (gfs2_format_is_valid(be32_to_cpu(sbd->sb_fs_format)) && + gfs2_multiformat_is_valid(be32_to_cpu(sbd->sb_multihost_format))) + { + if (*sbd->sb_locktable) + blkid_probe_set_label(pr, + (unsigned char *) sbd->sb_locktable, + sizeof(sbd->sb_locktable)); + blkid_probe_set_uuid(pr, sbd->sb_uuid); + blkid_probe_set_version(pr, "1"); + blkid_probe_set_fsblocksize(pr, be32_to_cpu(sbd->sb_bsize)); + blkid_probe_set_block_size(pr, be32_to_cpu(sbd->sb_bsize)); + return 0; + } + return 1; +} + +const struct blkid_idinfo gfs_idinfo = +{ + .name = "gfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_gfs, + .minsz = 32 * 1024 * 1024, /* minimal size of GFS journal */ + .magics = + { + { .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 }, + { NULL } + } +}; + +const struct blkid_idinfo gfs2_idinfo = +{ + .name = "gfs2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_gfs2, + .minsz = 32 * 1024 * 1024, /* minimal size of GFS journal */ + .magics = + { + { .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/hfs.c b/libblkid/src/superblocks/hfs.c new file mode 100644 index 0000000..68cb30e --- /dev/null +++ b/libblkid/src/superblocks/hfs.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <inttypes.h> + +#include "superblocks.h" +#include "md5.h" + +/* HFS / HFS+ */ +struct hfs_finder_info { + uint32_t boot_folder; + uint32_t start_app; + uint32_t open_folder; + uint32_t os9_folder; + uint32_t reserved; + uint32_t osx_folder; + uint8_t id[8]; +} __attribute__((packed)); + +#define HFS_SECTOR_SIZE 512 + +struct hfs_mdb { + uint8_t signature[2]; + uint32_t cr_date; + uint32_t ls_Mod; + uint16_t atrb; + uint16_t nm_fls; + uint16_t vbm_st; + uint16_t alloc_ptr; + uint16_t nm_al_blks; + uint32_t al_blk_size; + uint32_t clp_size; + uint16_t al_bl_st; + uint32_t nxt_cnid; + uint16_t free_bks; + uint8_t label_len; + uint8_t label[27]; + uint32_t vol_bkup; + uint16_t vol_seq_num; + uint32_t wr_cnt; + uint32_t xt_clump_size; + uint32_t ct_clump_size; + uint16_t num_root_dirs; + uint32_t file_count; + uint32_t dir_count; + struct hfs_finder_info finder_info; + uint8_t embed_sig[2]; + uint16_t embed_startblock; + uint16_t embed_blockcount; +} __attribute__((packed)); + + +#define HFS_NODE_LEAF 0xff +#define HFSPLUS_POR_CNID 1 + +struct hfsplus_bnode_descriptor { + uint32_t next; + uint32_t prev; + uint8_t type; + uint8_t height; + uint16_t num_recs; + uint16_t reserved; +} __attribute__((packed)); + +struct hfsplus_bheader_record { + uint16_t depth; + uint32_t root; + uint32_t leaf_count; + uint32_t leaf_head; + uint32_t leaf_tail; + uint16_t node_size; +} __attribute__((packed)); + +struct hfsplus_catalog_key { + uint16_t key_len; + uint32_t parent_id; + uint16_t unicode_len; + uint8_t unicode[255 * 2]; +} __attribute__((packed)); + +struct hfsplus_extent { + uint32_t start_block; + uint32_t block_count; +} __attribute__((packed)); + +#define HFSPLUS_EXTENT_COUNT 8 +struct hfsplus_fork { + uint64_t total_size; + uint32_t clump_size; + uint32_t total_blocks; + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; +} __attribute__((packed)); + +struct hfsplus_vol_header { + uint8_t signature[2]; + uint16_t version; + uint32_t attributes; + uint32_t last_mount_vers; + uint32_t reserved; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + uint32_t file_count; + uint32_t folder_count; + uint32_t blocksize; + uint32_t total_blocks; + uint32_t free_blocks; + uint32_t next_alloc; + uint32_t rsrc_clump_sz; + uint32_t data_clump_sz; + uint32_t next_cnid; + uint32_t write_count; + uint64_t encodings_bmp; + struct hfs_finder_info finder_info; + struct hfsplus_fork alloc_file; + struct hfsplus_fork ext_file; + struct hfsplus_fork cat_file; + struct hfsplus_fork attr_file; + struct hfsplus_fork start_file; +} __attribute__((packed)); + +#define HFSPLUS_SECTOR_SIZE 512 + +static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len) +{ + static unsigned char const hash_init[UL_MD5LENGTH] = { + 0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6, + 0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac + }; + unsigned char uuid[UL_MD5LENGTH]; + struct UL_MD5Context md5c; + + if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0) + return -1; + + ul_MD5Init(&md5c); + ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH); + ul_MD5Update(&md5c, hfs_info, len); + ul_MD5Final(uuid, &md5c); + + uuid[6] = 0x30 | (uuid[6] & 0x0f); + uuid[8] = 0x80 | (uuid[8] & 0x3f); + return blkid_probe_set_uuid(pr, uuid); +} + +static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct hfs_mdb *hfs; + int size; + + hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb); + if (!hfs) + return errno ? -errno : 1; + + if ((memcmp(hfs->embed_sig, "H+", 2) == 0) || + (memcmp(hfs->embed_sig, "HX", 2) == 0)) + return 1; /* Not hfs, but an embedded HFS+ */ + + size = be32_to_cpu(hfs->al_blk_size); + if (!size || (size & (HFS_SECTOR_SIZE - 1))) { + DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore")); + return 1; + } + + hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id)); + + size = hfs->label_len; + if ((size_t) size > sizeof(hfs->label)) + size = sizeof(hfs->label); + blkid_probe_set_label(pr, hfs->label, size); + return 0; +} + +static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; + struct hfsplus_bnode_descriptor *descr; + struct hfsplus_bheader_record *bnode; + struct hfsplus_catalog_key *key; + struct hfsplus_vol_header *hfsplus; + struct hfs_mdb *sbd; + unsigned int alloc_block_size; + unsigned int alloc_first_block; + unsigned int embed_first_block; + unsigned int off = 0; + unsigned int blocksize; + unsigned int cat_block; + unsigned int ext_block_start = 0; + unsigned int ext_block_count; + unsigned int record_count; + unsigned int leaf_node_head; + unsigned int leaf_node_count; + unsigned int leaf_node_size; + unsigned int leaf_block; + int ext; + uint64_t leaf_off; + unsigned char *buf; + + sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb); + if (!sbd) + return errno ? -errno : 1; + + /* Check for a HFS+ volume embedded in a HFS volume */ + if (memcmp(sbd->signature, "BD", 2) == 0) { + if ((memcmp(sbd->embed_sig, "H+", 2) != 0) && + (memcmp(sbd->embed_sig, "HX", 2) != 0)) + /* This must be an HFS volume, so fail */ + return 1; + + alloc_block_size = be32_to_cpu(sbd->al_blk_size); + alloc_first_block = be16_to_cpu(sbd->al_bl_st); + embed_first_block = be16_to_cpu(sbd->embed_startblock); + off = (alloc_first_block * 512) + + (embed_first_block * alloc_block_size); + + buf = blkid_probe_get_buffer(pr, + off + (mag->kboff * 1024), + sizeof(struct hfsplus_vol_header)); + hfsplus = (struct hfsplus_vol_header *) buf; + + } else + hfsplus = blkid_probe_get_sb(pr, mag, + struct hfsplus_vol_header); + + if (!hfsplus) + return errno ? -errno : 1; + + if ((memcmp(hfsplus->signature, "H+", 2) != 0) && + (memcmp(hfsplus->signature, "HX", 2) != 0)) + return 1; + + hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id)); + + blocksize = be32_to_cpu(hfsplus->blocksize); + if (blocksize < HFSPLUS_SECTOR_SIZE) + return 1; + + blkid_probe_set_fsblocksize(pr, blocksize); + blkid_probe_set_block_size(pr, blocksize); + + memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); + cat_block = be32_to_cpu(extents[0].start_block); + + buf = blkid_probe_get_buffer(pr, + off + ((uint64_t) cat_block * blocksize), 0x2000); + if (!buf) + return errno ? -errno : 0; + + bnode = (struct hfsplus_bheader_record *) + &buf[sizeof(struct hfsplus_bnode_descriptor)]; + + leaf_node_head = be32_to_cpu(bnode->leaf_head); + leaf_node_size = be16_to_cpu(bnode->node_size); + leaf_node_count = be32_to_cpu(bnode->leaf_count); + + if (leaf_node_size < sizeof(struct hfsplus_bnode_descriptor) + + sizeof(struct hfsplus_catalog_key) || leaf_node_count == 0) + return 0; + + leaf_block = (leaf_node_head * leaf_node_size) / blocksize; + + /* get physical location */ + for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { + ext_block_start = be32_to_cpu(extents[ext].start_block); + ext_block_count = be32_to_cpu(extents[ext].block_count); + if (ext_block_count == 0) + return 0; + + /* this is our extent */ + if (leaf_block < ext_block_count) + break; + + leaf_block -= ext_block_count; + } + if (ext == HFSPLUS_EXTENT_COUNT) + return 0; + + leaf_off = ((uint64_t) ext_block_start + leaf_block) * blocksize; + + buf = blkid_probe_get_buffer(pr, + (uint64_t) off + leaf_off, + leaf_node_size); + if (!buf) + return errno ? -errno : 0; + + descr = (struct hfsplus_bnode_descriptor *) buf; + record_count = be16_to_cpu(descr->num_recs); + if (record_count == 0) + return 0; + + if (descr->type != HFS_NODE_LEAF) + return 0; + + key = (struct hfsplus_catalog_key *) + &buf[sizeof(struct hfsplus_bnode_descriptor)]; + + if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID || + be16_to_cpu(key->unicode_len) > 255) + return 0; + + blkid_probe_set_utf8label(pr, key->unicode, + be16_to_cpu(key->unicode_len) * 2, + UL_ENCODE_UTF16BE); + return 0; +} + +const struct blkid_idinfo hfs_idinfo = +{ + .name = "hfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_hfs, + .flags = BLKID_IDINFO_TOLERANT, + .magics = + { + { .magic = "BD", .len = 2, .kboff = 1 }, + { NULL } + } +}; + +const struct blkid_idinfo hfsplus_idinfo = +{ + .name = "hfsplus", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_hfsplus, + .magics = + { + { .magic = "BD", .len = 2, .kboff = 1 }, + { .magic = "H+", .len = 2, .kboff = 1 }, + { .magic = "HX", .len = 2, .kboff = 1 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/highpoint_raid.c b/libblkid/src/superblocks/highpoint_raid.c new file mode 100644 index 0000000..2487930 --- /dev/null +++ b/libblkid/src/superblocks/highpoint_raid.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct hpt45x_metadata { + uint32_t magic; +}; + +#define HPT45X_MAGIC_OK 0x5a7816f3 +#define HPT45X_MAGIC_BAD 0x5a7816fd + +static int probe_highpoint45x(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct hpt45x_metadata *hpt; + uint64_t off; + uint32_t magic; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200) - 11) * 0x200; + hpt = (struct hpt45x_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct hpt45x_metadata)); + if (!hpt) + return errno ? -errno : 1; + magic = le32_to_cpu(hpt->magic); + if (magic != HPT45X_MAGIC_OK && magic != HPT45X_MAGIC_BAD) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(hpt->magic), + (unsigned char *) &hpt->magic)) + return 1; + return 0; +} + +static int probe_highpoint37x(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + return 0; +} + + +const struct blkid_idinfo highpoint45x_idinfo = { + .name = "hpt45x_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_highpoint45x, + .magics = BLKID_NONE_MAGIC +}; + +const struct blkid_idinfo highpoint37x_idinfo = { + .name = "hpt37x_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_highpoint37x, + .magics = { + /* + * Superblock offset: 4608 bytes (9 sectors) + * Magic string offset within superblock: 32 bytes + * + * kboff = (4608 + 32) / 1024 + * sboff = (4608 + 32) % kboff + */ + { .magic = "\xf0\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 }, + { .magic = "\xfd\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 }, + { NULL } + } +}; + + diff --git a/libblkid/src/superblocks/hpfs.c b/libblkid/src/superblocks/hpfs.c new file mode 100644 index 0000000..09bf975 --- /dev/null +++ b/libblkid/src/superblocks/hpfs.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct hpfs_boot_block +{ + uint8_t jmp[3]; + uint8_t oem_id[8]; + uint8_t bytes_per_sector[2]; + uint8_t sectors_per_cluster; + uint8_t n_reserved_sectors[2]; + uint8_t n_fats; + uint8_t n_rootdir_entries[2]; + uint8_t n_sectors_s[2]; + uint8_t media_byte; + uint16_t sectors_per_fat; + uint16_t sectors_per_track; + uint16_t heads_per_cyl; + uint32_t n_hidden_sectors; + uint32_t n_sectors_l; + uint8_t drive_number; + uint8_t mbz; + uint8_t sig_28h; + uint8_t vol_serno[4]; + uint8_t vol_label[11]; + uint8_t sig_hpfs[8]; + uint8_t pad[448]; + uint8_t magic[2]; +} __attribute__((packed)); + +struct hpfs_super_block +{ + uint8_t magic[4]; + uint8_t magic1[4]; + uint8_t version; +} __attribute__((packed)); + +struct hpfs_spare_super +{ + uint8_t magic[4]; + uint8_t magic1[4]; +} __attribute__((packed)); + + +#define HPFS_SB_OFFSET 0x2000 +#define HPFS_SBSPARE_OFFSET 0x2200 + +static int probe_hpfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct hpfs_super_block *hs; + struct hpfs_spare_super *hss; + struct hpfs_boot_block *hbb; + uint8_t version; + + /* super block */ + hs = blkid_probe_get_sb(pr, mag, struct hpfs_super_block); + if (!hs) + return errno ? -errno : 1; + version = hs->version; + + /* spare super block */ + hss = (struct hpfs_spare_super *) + blkid_probe_get_buffer(pr, + HPFS_SBSPARE_OFFSET, + sizeof(struct hpfs_spare_super)); + if (!hss) + return errno ? -errno : 1; + if (memcmp(hss->magic, "\x49\x18\x91\xf9", 4) != 0) + return 1; + + /* boot block (with UUID and LABEL) */ + hbb = (struct hpfs_boot_block *) + blkid_probe_get_buffer(pr, + 0, + sizeof(struct hpfs_boot_block)); + if (!hbb) + return errno ? -errno : 1; + if (memcmp(hbb->magic, "\x55\xaa", 2) == 0 && + memcmp(hbb->sig_hpfs, "HPFS", 4) == 0 && + hbb->sig_28h == 0x28) { + blkid_probe_set_label(pr, hbb->vol_label, sizeof(hbb->vol_label)); + blkid_probe_sprintf_uuid(pr, + hbb->vol_serno, sizeof(hbb->vol_serno), + "%02X%02X-%02X%02X", + hbb->vol_serno[3], hbb->vol_serno[2], + hbb->vol_serno[1], hbb->vol_serno[0]); + } + blkid_probe_sprintf_version(pr, "%u", version); + blkid_probe_set_fsblocksize(pr, 512); + blkid_probe_set_block_size(pr, 512); + + return 0; +} + +const struct blkid_idinfo hpfs_idinfo = +{ + .name = "hpfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_hpfs, + .magics = + { + { + .magic = "\x49\xe8\x95\xf9", + .len = 4, + .kboff = (HPFS_SB_OFFSET >> 10) + }, + { NULL } + } +}; + + diff --git a/libblkid/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c new file mode 100644 index 0000000..536704b --- /dev/null +++ b/libblkid/src/superblocks/iso9660.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired also by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <ctype.h> + +#include "superblocks.h" +#include "cctype.h" +#include "iso9660.h" + +struct iso9660_date { + unsigned char year[4]; + unsigned char month[2]; + unsigned char day[2]; + unsigned char hour[2]; + unsigned char minute[2]; + unsigned char second[2]; + unsigned char hundredth[2]; + unsigned char offset; +} __attribute__ ((packed)); + +/* PVD - Primary volume descriptor */ +struct iso_volume_descriptor { + unsigned char vd_type; + unsigned char vd_id[5]; + unsigned char vd_version; + unsigned char flags; + unsigned char system_id[32]; + unsigned char volume_id[32]; + unsigned char unused[8]; + unsigned char space_size[8]; + unsigned char escape_sequences[8]; + unsigned char unused2[32]; + unsigned char logical_block_size[4]; + unsigned char unused3[58]; + unsigned char volume_set_id[128]; + unsigned char publisher_id[128]; + unsigned char data_preparer_id[128]; + unsigned char application_id[128]; + unsigned char unused4[111]; + struct iso9660_date created; + struct iso9660_date modified; +} __attribute__((packed)); + +/* Boot Record */ +struct boot_record { + unsigned char vd_type; + unsigned char vd_id[5]; + unsigned char vd_version; + unsigned char boot_system_id[32]; + unsigned char boot_id[32]; + unsigned char unused[1]; +} __attribute__((packed)); + +#define ISO_SUPERBLOCK_OFFSET 0x8000 +#define ISO_SECTOR_SIZE 0x800 +#define ISO_VD_BOOT_RECORD 0x0 +#define ISO_VD_PRIMARY 0x1 +#define ISO_VD_SUPPLEMENTARY 0x2 +#define ISO_VD_END 0xff +#define ISO_VD_MAX 16 +/* maximal string field size used anywhere in ISO; update if necessary */ +#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, volume_set_id) + +struct high_sierra_volume_descriptor { + unsigned char foo[8]; + unsigned char type; + unsigned char id[5]; + unsigned char version; + unsigned char unused1; + unsigned char system_id[32]; + unsigned char volume_id[32]; +} __attribute__((packed)); + +/* old High Sierra format */ +static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct high_sierra_volume_descriptor *iso; + + iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor); + if (!iso) + return errno ? -errno : 1; + + blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE); + blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE); + blkid_probe_set_version(pr, "High Sierra"); + blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id)); + return 0; +} + +static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date) +{ + unsigned char buffer[16]; + unsigned int i, zeros = 0; + + buffer[0] = date->year[0]; + buffer[1] = date->year[1]; + buffer[2] = date->year[2]; + buffer[3] = date->year[3]; + buffer[4] = date->month[0]; + buffer[5] = date->month[1]; + buffer[6] = date->day[0]; + buffer[7] = date->day[1]; + buffer[8] = date->hour[0]; + buffer[9] = date->hour[1]; + buffer[10] = date->minute[0]; + buffer[11] = date->minute[1]; + buffer[12] = date->second[0]; + buffer[13] = date->second[1]; + buffer[14] = date->hundredth[0]; + buffer[15] = date->hundredth[1]; + + /* count the number of zeros ('0') in the date buffer */ + for (i = 0, zeros = 0; i < sizeof(buffer); i++) + if (buffer[i] == '0') + zeros++; + + /* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */ + if (zeros == sizeof(buffer) && date->offset == 0) + return 0; + + /* generate an UUID using this date and return success */ + blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer), + "%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c", + buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], + buffer[6], buffer[7], + buffer[8], buffer[9], + buffer[10], buffer[11], + buffer[12], buffer[13], + buffer[14], buffer[15]); + + return 1; +} + +static int is_str_empty(const unsigned char *str, size_t len) +{ + size_t i; + + if (!str || !*str) + return 1; + + for (i = 0; i < len; i++) + if (!isspace(str[i])) + return 0; + return 1; +} + +static int is_utf16be_str_empty(unsigned char *utf16, size_t len) +{ + size_t i; + + for (i = 0; i < len; i += 2) { + if (utf16[i] != 0x0 || !isspace(utf16[i + 1])) + return 0; + } + return 1; +} + +/* if @utf16 is prefix of @ascii (ignoring non-representable characters and upper-case conversion) + * then reconstruct prefix from @utf16 and @ascii, append suffix from @ascii, fill it into @out + * and returns length of bytes written into @out; otherwise returns zero */ +static size_t merge_utf16be_ascii(unsigned char *out, size_t out_len, const unsigned char *utf16, const unsigned char *ascii, size_t len) +{ + size_t o, a, u; + + for (o = 0, a = 0, u = 0; u + 1 < len && a < len && o + 1 < out_len; o += 2, a++, u += 2) { + /* Surrogate pair with code point above U+FFFF */ + if (utf16[u] >= 0xD8 && utf16[u] <= 0xDB && u + 3 < len && + utf16[u + 2] >= 0xDC && utf16[u + 2] <= 0xDF) { + out[o++] = utf16[u++]; + out[o++] = utf16[u++]; + } + /* Value '_' is replacement for non-representable character */ + if (ascii[a] == '_') { + out[o] = utf16[u]; + out[o + 1] = utf16[u + 1]; + } else if (utf16[u] == 0x00 && utf16[u + 1] == '_') { + out[o] = 0x00; + out[o + 1] = ascii[a]; + } else if (utf16[u] == 0x00 && c_toupper(ascii[a]) == c_toupper(utf16[u + 1])) { + out[o] = 0x00; + out[o + 1] = c_isupper(ascii[a]) ? utf16[u + 1] : ascii[a]; + } else { + return 0; + } + } + + for (; a < len && o + 1 < out_len; o += 2, a++) { + out[o] = 0x00; + out[o + 1] = ascii[a]; + } + + return o; +} + +/* iso9660 [+ Microsoft Joliet Extension] */ +static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct boot_record *boot = NULL; + struct iso_volume_descriptor *pvd = NULL; + struct iso_volume_descriptor *joliet = NULL; + /* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */ + unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2]; + size_t len; + int is_unicode_empty; + int is_ascii_empty; + int i; + uint64_t off; + + if (blkid_probe_get_hint(pr, mag->hoff, &off) < 0) + off = 0; + + if (off % ISO_SECTOR_SIZE) + return 1; + + if (strcmp(mag->magic, "CDROM") == 0) + return probe_iso9660_hsfs(pr, mag); + + for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) { + unsigned char *desc = + blkid_probe_get_buffer(pr, + off, + max(sizeof(struct boot_record), + sizeof(struct iso_volume_descriptor))); + + if (desc == NULL || desc[0] == ISO_VD_END) + break; + else if (!boot && desc[0] == ISO_VD_BOOT_RECORD) + boot = (struct boot_record *)desc; + else if (!pvd && desc[0] == ISO_VD_PRIMARY) + pvd = (struct iso_volume_descriptor *)desc; + else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) { + joliet = (struct iso_volume_descriptor *)desc; + if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 && + memcmp(joliet->escape_sequences, "%/C", 3) != 0 && + memcmp(joliet->escape_sequences, "%/E", 3) != 0) + joliet = NULL; + } + } + + if (!pvd) + return errno ? -errno : 1; + + uint16_t logical_block_size = isonum_723(pvd->logical_block_size, false); + uint32_t space_size = isonum_733(pvd->space_size, false); + + blkid_probe_set_fsblocksize(pr, logical_block_size); + blkid_probe_set_block_size(pr, logical_block_size); + blkid_probe_set_fssize(pr, (uint64_t) space_size * logical_block_size); + + if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", buf, len, UL_ENCODE_UTF16BE); + else if (joliet) + blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", joliet->system_id, sizeof(joliet->system_id), UL_ENCODE_UTF16BE); + else + blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id)); + + if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE); + else if (joliet) + blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE); + else + blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id)); + + is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE); + else if (!is_unicode_empty) + blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_empty) + blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id)); + + is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE); + else if (!is_unicode_empty) + blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_empty) + blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id)); + + is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE); + else if (!is_unicode_empty) + blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_empty) + blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id)); + + /* create an UUID using the modified/created date */ + if (! probe_iso9660_set_uuid(pr, &pvd->modified)) + probe_iso9660_set_uuid(pr, &pvd->created); + + if (boot) + blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID", + boot->boot_system_id, + sizeof(boot->boot_system_id)); + + if (joliet) + blkid_probe_set_version(pr, "Joliet Extension"); + + /* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is + * subset of ASCII but can contain up to the 32 characters. Non-representable characters are + * stored as replacement character '_'. Label in Joliet is in most cases trimmed but UNICODE + * version of label in PVD. Based on these facts try to reconstruct original label if label + * in Joliet is prefix of the label in PVD (ignoring non-representable characters). + */ + if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0) + blkid_probe_set_utf8label(pr, buf, len, UL_ENCODE_UTF16BE); + else if (joliet) + blkid_probe_set_utf8label(pr, joliet->volume_id, sizeof(joliet->volume_id), UL_ENCODE_UTF16BE); + else + blkid_probe_set_label(pr, pvd->volume_id, sizeof(pvd->volume_id)); + + return 0; +} + +const struct blkid_idinfo iso9660_idinfo = +{ + .name = "iso9660", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_iso9660, + .flags = BLKID_IDINFO_TOLERANT, + .magics = + { + { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c new file mode 100644 index 0000000..81d53a1 --- /dev/null +++ b/libblkid/src/superblocks/isw_raid.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct isw_metadata { + uint8_t sig[32]; + uint32_t check_sum; + uint32_t mpb_size; + uint32_t family_num; + uint32_t generation_num; +}; + +#define ISW_SIGNATURE "Intel Raid ISM Cfg Sig. " + +static int probe_iswraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + unsigned int sector_size; + struct isw_metadata *isw; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + sector_size = blkid_probe_get_sectorsize(pr); + off = ((pr->size / sector_size) - 2) * sector_size; + + isw = (struct isw_metadata *)blkid_probe_get_buffer(pr, + off, sizeof(struct isw_metadata)); + if (!isw) + return errno ? -errno : 1; + + if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0) + return 1; + + if (blkid_probe_sprintf_version(pr, "%6s", + &isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(isw->sig), + (unsigned char *) isw->sig)) + return 1; + return 0; +} + +const struct blkid_idinfo iswraid_idinfo = { + .name = "isw_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_iswraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/jfs.c b/libblkid/src/superblocks/jfs.c new file mode 100644 index 0000000..7b75ecb --- /dev/null +++ b/libblkid/src/superblocks/jfs.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct jfs_super_block { + unsigned char js_magic[4]; + uint32_t js_version; + uint64_t js_size; + uint32_t js_bsize; /* 4: aggregate block size in bytes */ + uint16_t js_l2bsize; /* 2: log2 of s_bsize */ + uint16_t js_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + uint32_t js_pbsize; /* 4: hardware/LVM block size in bytes */ + uint16_t js_l2pbsize; /* 2: log2 of s_pbsize */ + uint16_t js_pad; /* 2: padding necessary for alignment */ + uint32_t js_dummy2[26]; + unsigned char js_uuid[16]; + unsigned char js_label[16]; + unsigned char js_loguuid[16]; +}; + +static int probe_jfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct jfs_super_block *js; + + js = blkid_probe_get_sb(pr, mag, struct jfs_super_block); + if (!js) + return errno ? -errno : 1; + if (le16_to_cpu(js->js_l2bsize) > 31 || le16_to_cpu(js->js_l2pbsize) > 31) + return 1; + if (le32_to_cpu(js->js_bsize) != (1U << le16_to_cpu(js->js_l2bsize))) + return 1; + if (le32_to_cpu(js->js_pbsize) != (1U << le16_to_cpu(js->js_l2pbsize))) + return 1; + if ((le16_to_cpu(js->js_l2bsize) - le16_to_cpu(js->js_l2pbsize)) != + le16_to_cpu(js->js_l2bfactor)) + return 1; + + if (*((char *) js->js_label) != '\0') + blkid_probe_set_label(pr, js->js_label, sizeof(js->js_label)); + blkid_probe_set_uuid(pr, js->js_uuid); + blkid_probe_set_fsblocksize(pr, le32_to_cpu(js->js_bsize)); + blkid_probe_set_block_size(pr, le32_to_cpu(js->js_bsize)); + return 0; +} + + +const struct blkid_idinfo jfs_idinfo = +{ + .name = "jfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_jfs, + .minsz = 16 * 1024 * 1024, + .magics = + { + { .magic = "JFS1", .len = 4, .kboff = 32 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/jmicron_raid.c b/libblkid/src/superblocks/jmicron_raid.c new file mode 100644 index 0000000..65e05b6 --- /dev/null +++ b/libblkid/src/superblocks/jmicron_raid.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +#define JM_SIGNATURE "JM" +#define JM_MINOR_VERSION(_x) (le16_to_cpu((_x)->version) & 0xFF) +#define JM_MAJOR_VERSION(_x) (le16_to_cpu((_x)->version) >> 8) +#define JM_SPARES 2 +#define JM_MEMBERS 8 + +struct jm_metadata { + int8_t signature[2]; /* 0x0 - 0x01 */ + + uint16_t version; /* 0x03 - 0x04 JMicron version */ + + uint16_t checksum; /* 0x04 - 0x05 */ + uint8_t filler[10]; + + uint32_t identity; /* 0x10 - 0x13 */ + + struct { + uint32_t base; /* 0x14 - 0x17 */ + uint32_t range; /* 0x18 - 0x1B range */ + uint16_t range2; /* 0x1C - 0x1D range2 */ + } segment; + + int8_t name[16]; /* 0x20 - 0x2F */ + + uint8_t mode; /* 0x30 RAID level */ + uint8_t block; /* 0x31 stride size (2=4K, 3=8K, ...) */ + uint16_t attribute; /* 0x32 - 0x33 */ + uint8_t filler1[4]; + + uint32_t spare[JM_SPARES]; /* 0x38 - 0x3F */ + uint32_t member[JM_MEMBERS]; /* 0x40 - 0x5F */ + + uint8_t filler2[0x20]; +} __attribute__ ((packed)); + +static int jm_checksum(blkid_probe pr, const struct jm_metadata *jm) +{ + size_t count = sizeof(*jm) / sizeof(uint16_t); + uint16_t sum = 0; + unsigned char *ptr = (unsigned char *) jm; + + while (count--) { + uint16_t val; + + memcpy(&val, ptr, sizeof(uint16_t)); + sum += le16_to_cpu(val); + + ptr += sizeof(uint16_t); + } + + return blkid_probe_verify_csum(pr, sum == 0 || sum == 1, 1); +} + +static int probe_jmraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + const struct jm_metadata *jm; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + jm = (struct jm_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct jm_metadata)); + if (!jm) + return errno ? -errno : 1; + + if (memcmp(jm->signature, JM_SIGNATURE, sizeof(JM_SIGNATURE) - 1) != 0) + return 1; + + if (!jm_checksum(pr, jm)) + return 1; + + if (jm->mode > 5) + return 1; + + if (blkid_probe_sprintf_version(pr, "%u.%u", + JM_MAJOR_VERSION(jm), JM_MINOR_VERSION(jm)) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(jm->signature), + (unsigned char *) jm->signature)) + return 1; + return 0; +} + +const struct blkid_idinfo jmraid_idinfo = { + .name = "jmicron_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_jmraid, + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/superblocks/linux_raid.c b/libblkid/src/superblocks/linux_raid.c new file mode 100644 index 0000000..360cd4e --- /dev/null +++ b/libblkid/src/superblocks/linux_raid.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct mdp0_super_block { + uint32_t md_magic; + uint32_t major_version; + uint32_t minor_version; + uint32_t patch_version; + uint32_t gvalid_words; + uint32_t set_uuid0; + uint32_t ctime; + uint32_t level; + uint32_t size; + uint32_t nr_disks; + uint32_t raid_disks; + uint32_t md_minor; + uint32_t not_persistent; + uint32_t set_uuid1; + uint32_t set_uuid2; + uint32_t set_uuid3; +}; + +/* + * Version-1, little-endian. + */ +struct mdp1_super_block { + /* constant array information - 128 bytes */ + uint32_t magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian */ + uint32_t major_version; /* 1 */ + uint32_t feature_map; /* 0 for now */ + uint32_t pad0; /* always set to 0 when writing */ + + uint8_t set_uuid[16]; /* user-space generated. */ + unsigned char set_name[32]; /* set and interpreted by user-space */ + + uint64_t ctime; /* lo 40 bits are seconds, top 24 are microseconds or 0*/ + uint32_t level; /* -4 (multipath), -1 (linear), 0,1,4,5 */ + uint32_t layout; /* only for raid5 currently */ + uint64_t size; /* used size of component devices, in 512byte sectors */ + + uint32_t chunksize; /* in 512byte sectors */ + uint32_t raid_disks; + uint32_t bitmap_offset; /* sectors after start of superblock that bitmap starts + * NOTE: signed, so bitmap can be before superblock + * only meaningful of feature_map[0] is set. + */ + + /* These are only valid with feature bit '4' */ + uint32_t new_level; /* new level we are reshaping to */ + uint64_t reshape_position; /* next address in array-space for reshape */ + uint32_t delta_disks; /* change in number of raid_disks */ + uint32_t new_layout; /* new layout */ + uint32_t new_chunk; /* new chunk size (bytes) */ + uint8_t pad1[128-124]; /* set to 0 when written */ + + /* constant this-device information - 64 bytes */ + uint64_t data_offset; /* sector start of data, often 0 */ + uint64_t data_size; /* sectors in this device that can be used for data */ + uint64_t super_offset; /* sector start of this superblock */ + uint64_t recovery_offset;/* sectors before this offset (from data_offset) have been recovered */ + uint32_t dev_number; /* permanent identifier of this device - not role in raid */ + uint32_t cnt_corrected_read; /* number of read errors that were corrected by re-writing */ + uint8_t device_uuid[16]; /* user-space setable, ignored by kernel */ + uint8_t devflags; /* per-device flags. Only one defined...*/ + uint8_t pad2[64-57]; /* set to 0 when writing */ + + /* array state information - 64 bytes */ + uint64_t utime; /* 40 bits second, 24 bits microseconds */ + uint64_t events; /* incremented when superblock updated */ + uint64_t resync_offset; /* data before this offset (from data_offset) known to be in sync */ + uint32_t sb_csum; /* checksum up to dev_roles[max_dev] */ + uint32_t max_dev; /* size of dev_roles[] array to consider */ + uint8_t pad3[64-32]; /* set to 0 when writing */ + + /* device state information. Indexed by dev_number. + * 2 bytes per device + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + uint16_t dev_roles[0]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */ +}; + + +#define MD_RESERVED_BYTES 0x10000 +#define MD_SB_MAGIC 0xa92b4efc + +static int probe_raid0(blkid_probe pr, uint64_t off) +{ + struct mdp0_super_block *mdp0; + union { + uint32_t ints[4]; + uint8_t bytes[16]; + } uuid; + uint32_t ma, mi, pa; + uint64_t size; + + if (pr->size < MD_RESERVED_BYTES) + return 1; + mdp0 = (struct mdp0_super_block *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct mdp0_super_block)); + if (!mdp0) + return errno ? -errno : 1; + + memset(uuid.ints, 0, sizeof(uuid.ints)); + + if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { + uuid.ints[0] = swab32(mdp0->set_uuid0); + if (le32_to_cpu(mdp0->minor_version) >= 90) { + uuid.ints[1] = swab32(mdp0->set_uuid1); + uuid.ints[2] = swab32(mdp0->set_uuid2); + uuid.ints[3] = swab32(mdp0->set_uuid3); + } + ma = le32_to_cpu(mdp0->major_version); + mi = le32_to_cpu(mdp0->minor_version); + pa = le32_to_cpu(mdp0->patch_version); + size = le32_to_cpu(mdp0->size); + + } else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { + uuid.ints[0] = mdp0->set_uuid0; + if (be32_to_cpu(mdp0->minor_version) >= 90) { + uuid.ints[1] = mdp0->set_uuid1; + uuid.ints[2] = mdp0->set_uuid2; + uuid.ints[3] = mdp0->set_uuid3; + } + ma = be32_to_cpu(mdp0->major_version); + mi = be32_to_cpu(mdp0->minor_version); + pa = be32_to_cpu(mdp0->patch_version); + size = be32_to_cpu(mdp0->size); + } else + return 1; + + size <<= 10; /* convert KiB to bytes */ + + if (pr->size < size + MD_RESERVED_BYTES) + /* device is too small */ + return 1; + + if (off < size) + /* no space before superblock */ + return 1; + + /* + * Check for collisions between RAID and partition table + * + * For example the superblock is at the end of the last partition, it's + * the same position as at the end of the disk... + */ + if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && + blkid_probe_is_covered_by_pt(pr, + off - size, /* min. start */ + size + MD_RESERVED_BYTES)) { /* min. length */ + + /* ignore this superblock, it's within any partition and + * we are working with whole-disk now */ + return 1; + } + + if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0) + return 1; + if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic), + (unsigned char *) &mdp0->md_magic)) + return 1; + return 0; +} + +static int raid1_verify_csum(blkid_probe pr, off_t off, + const struct mdp1_super_block *mdp1) +{ + size_t csummed_size = sizeof(struct mdp1_super_block) + + le32_to_cpu(mdp1->max_dev) * sizeof(mdp1->dev_roles[0]); + unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size); + if (!csummed) + return 1; + + memset(csummed + offsetof(struct mdp1_super_block, sb_csum), 0, + sizeof(mdp1->sb_csum)); + + uint64_t csum = 0; + + while (csummed_size >= 4) { + csum += le32_to_cpu(*(uint32_t *) csummed); + csummed_size -= 4; + csummed += 4; + } + if (csummed_size == 2) + csum += le16_to_cpu(*(uint16_t *) csummed); + + csum = (csum >> 32) + (uint32_t) csum; + return blkid_probe_verify_csum(pr, csum, le32_to_cpu(mdp1->sb_csum)); +} + +static int probe_raid1(blkid_probe pr, off_t off) +{ + struct mdp1_super_block *mdp1; + + mdp1 = (struct mdp1_super_block *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct mdp1_super_block)); + if (!mdp1) + return errno ? -errno : 1; + if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC) + return 1; + if (le32_to_cpu(mdp1->major_version) != 1U) + return 1; + if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9) + return 1; + if (!raid1_verify_csum(pr, off, mdp1)) + return 1; + if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0) + return 1; + if (blkid_probe_set_uuid_as(pr, + (unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0) + return 1; + if (blkid_probe_set_label(pr, mdp1->set_name, + sizeof(mdp1->set_name)) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic), + (unsigned char *) &mdp1->magic)) + return 1; + return 0; +} + +static int probe_raid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const char *ver = NULL; + int ret = BLKID_PROBE_NONE; + + if (pr->size > MD_RESERVED_BYTES) { + /* version 0 at the end of the device */ + uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1)) + - MD_RESERVED_BYTES; + ret = probe_raid0(pr, sboff); + if (ret < 1) + return ret; /* error */ + + /* version 1.0 at the end of the device */ + sboff = (pr->size & ~(0x1000 - 1)) - 0x2000; + ret = probe_raid1(pr, sboff); + if (ret < 0) + return ret; /* error */ + if (ret == 0) + ver = "1.0"; + } + + if (!ver) { + /* version 1.1 at the start of the device */ + ret = probe_raid1(pr, 0); + if (ret == 0) + ver = "1.1"; + + /* version 1.2 at 4k offset from the start */ + else if (ret == BLKID_PROBE_NONE) { + ret = probe_raid1(pr, 0x1000); + if (ret == 0) + ver = "1.2"; + } + } + + if (ver) { + blkid_probe_set_version(pr, ver); + return BLKID_PROBE_OK; + } + return ret; +} + + +const struct blkid_idinfo linuxraid_idinfo = { + .name = "linux_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_raid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/lsi_raid.c b/libblkid/src/superblocks/lsi_raid.c new file mode 100644 index 0000000..697b0fe --- /dev/null +++ b/libblkid/src/superblocks/lsi_raid.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct lsi_metadata { + uint8_t sig[6]; +}; + + +#define LSI_SIGNATURE "$XIDE$" + +static int probe_lsiraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + struct lsi_metadata *lsi; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + lsi = (struct lsi_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct lsi_metadata)); + if (!lsi) + return errno ? -errno : 1; + + if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(lsi->sig), + (unsigned char *) lsi->sig)) + return 1; + return 0; +} + +const struct blkid_idinfo lsiraid_idinfo = { + .name = "lsi_mega_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_lsiraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/luks.c b/libblkid/src/superblocks/luks.c new file mode 100644 index 0000000..0230b34 --- /dev/null +++ b/libblkid/src/superblocks/luks.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2018 Milan Broz <gmazyland@gmail.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +#define LUKS_CIPHERNAME_L 32 +#define LUKS_CIPHERMODE_L 32 +#define LUKS_HASHSPEC_L 32 +#define LUKS_DIGESTSIZE 20 +#define LUKS_SALTSIZE 32 +#define LUKS_MAGIC_L 6 +#define UUID_STRING_L 40 +#define LUKS2_LABEL_L 48 +#define LUKS2_SALT_L 64 +#define LUKS2_CHECKSUM_ALG_L 32 +#define LUKS2_CHECKSUM_L 64 + +#define LUKS_MAGIC "LUKS\xba\xbe" +#define LUKS_MAGIC_2 "SKUL\xba\xbe" + +/* Offsets for secondary header (for scan if primary header is corrupted). */ +#define LUKS2_HDR2_OFFSETS { 0x04000, 0x008000, 0x010000, 0x020000, \ + 0x40000, 0x080000, 0x100000, 0x200000, 0x400000 } + +static const uint64_t secondary_offsets[] = LUKS2_HDR2_OFFSETS; + +struct luks_phdr { + uint8_t magic[LUKS_MAGIC_L]; + uint16_t version; + uint8_t cipherName[LUKS_CIPHERNAME_L]; + uint8_t cipherMode[LUKS_CIPHERMODE_L]; + uint8_t hashSpec[LUKS_HASHSPEC_L]; + uint32_t payloadOffset; + uint32_t keyBytes; + uint8_t mkDigest[LUKS_DIGESTSIZE]; + uint8_t mkDigestSalt[LUKS_SALTSIZE]; + uint32_t mkDigestIterations; + uint8_t uuid[UUID_STRING_L]; +} __attribute__((packed)); + +struct luks2_phdr { + char magic[LUKS_MAGIC_L]; + uint16_t version; + uint64_t hdr_size; /* in bytes, including JSON area */ + uint64_t seqid; /* increased on every update */ + char label[LUKS2_LABEL_L]; + char checksum_alg[LUKS2_CHECKSUM_ALG_L]; + uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */ + char uuid[UUID_STRING_L]; + char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */ + uint64_t hdr_offset; /* offset from device start in bytes */ + char _padding[184]; + uint8_t csum[LUKS2_CHECKSUM_L]; + /* Padding to 4k, then JSON area */ +} __attribute__ ((packed)); + +static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t offset) +{ + int version; + struct luks_phdr *header_v1; + + if (blkid_probe_set_magic(pr, offset, LUKS_MAGIC_L, (unsigned char *) &header->magic)) + return BLKID_PROBE_NONE; + + version = be16_to_cpu(header->version); + blkid_probe_sprintf_version(pr, "%u", version); + + if (version == 1) { + header_v1 = (struct luks_phdr *)header; + blkid_probe_strncpy_uuid(pr, + (unsigned char *) header_v1->uuid, UUID_STRING_L); + } else if (version == 2) { + blkid_probe_strncpy_uuid(pr, + (unsigned char *) header->uuid, UUID_STRING_L); + blkid_probe_set_label(pr, + (unsigned char *) header->label, LUKS2_LABEL_L); + blkid_probe_set_id_label(pr, "SUBSYSTEM", + (unsigned char *) header->subsystem, LUKS2_LABEL_L); + } + + return BLKID_PROBE_OK; +} + +static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct luks2_phdr *header; + size_t i; + + header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, 0, sizeof(struct luks2_phdr)); + if (!header) + return errno ? -errno : BLKID_PROBE_NONE; + + if (!memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L)) { + /* LUKS primary header was found. */ + return luks_attributes(pr, header, 0); + } + + /* No primary header, scan for known offsets of LUKS2 secondary header. */ + for (i = 0; i < ARRAY_SIZE(secondary_offsets); i++) { + header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, + secondary_offsets[i], sizeof(struct luks2_phdr)); + + if (!header) + return errno ? -errno : BLKID_PROBE_NONE; + + if (!memcmp(header->magic, LUKS_MAGIC_2, LUKS_MAGIC_L)) + return luks_attributes(pr, header, secondary_offsets[i]); + } + + return BLKID_PROBE_NONE; +} + +const struct blkid_idinfo luks_idinfo = +{ + .name = "crypto_LUKS", + .usage = BLKID_USAGE_CRYPTO, + .probefunc = probe_luks, + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c new file mode 100644 index 0000000..eea74d6 --- /dev/null +++ b/libblkid/src/superblocks/lvm.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2012 Milan Broz <gmazyland@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +#define LVM1_ID_LEN 128 +#define LVM2_ID_LEN 32 + +struct lvm2_pv_label_header { + /* label_header */ + uint8_t id[8]; /* LABELONE */ + uint64_t sector_xl; /* Sector number of this label */ + uint32_t crc_xl; /* From next field to end of sector */ + uint32_t offset_xl; /* Offset from start of struct to contents */ + uint8_t type[8]; /* LVM2 001 */ + /* pv_header */ + uint8_t pv_uuid[LVM2_ID_LEN]; +} __attribute__ ((packed)); + +struct lvm1_pv_label_header { + uint8_t id[2]; /* HM */ + uint16_t version; /* version 1 or 2 */ + uint32_t _notused[10]; /* lvm1 internals */ + uint8_t pv_uuid[LVM1_ID_LEN]; +} __attribute__ ((packed)); + +#define LVM2_LABEL_SIZE 512 +static unsigned int lvm2_calc_crc(const void *buf, unsigned int size) +{ + static const unsigned int crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + unsigned int i, crc = 0xf597a6cf; + const uint8_t *data = (const uint8_t *) buf; + + for (i = 0; i < size; i++) { + crc ^= *data++; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + return crc; +} + +/* Length of real UUID is always LVM2_ID_LEN */ +static void format_lvm_uuid(char *dst_uuid, char *src_uuid) +{ + unsigned int i, b; + + for (i = 0, b = 1; i < LVM2_ID_LEN; i++, b <<= 1) { + if (b & 0x4444440) + *dst_uuid++ = '-'; + *dst_uuid++ = *src_uuid++; + } + *dst_uuid = '\0'; +} + +static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag) +{ + int sector = mag->kboff << 1; + struct lvm2_pv_label_header *label; + char uuid[LVM2_ID_LEN + 7]; + unsigned char *buf; + + buf = blkid_probe_get_buffer(pr, + mag->kboff << 10, + 512 + sizeof(struct lvm2_pv_label_header)); + if (!buf) + return errno ? -errno : 1; + + /* buf is at 0k or 1k offset; find label inside */ + if (memcmp(buf, "LABELONE", 8) == 0) { + label = (struct lvm2_pv_label_header *) buf; + } else if (memcmp(buf + 512, "LABELONE", 8) == 0) { + label = (struct lvm2_pv_label_header *)(buf + 512); + sector++; + } else { + return 1; + } + + if (le64_to_cpu(label->sector_xl) != (unsigned) sector) + return 1; + + if (!blkid_probe_verify_csum( + pr, lvm2_calc_crc( + &label->offset_xl, LVM2_LABEL_SIZE - + ((char *) &label->offset_xl - (char *) label)), + le32_to_cpu(label->crc_xl))) + return 1; + + format_lvm_uuid(uuid, (char *) label->pv_uuid); + blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid), + "%s", uuid); + + /* the mag->magic is the same string as label->type, + * but zero terminated */ + blkid_probe_set_version(pr, mag->magic); + + /* LVM (pvcreate) wipes begin of the device -- let's remember this + * to resolve conflicts between LVM and partition tables, ... + */ + blkid_probe_set_wiper(pr, 0, 8 * 1024); + + return 0; +} + +static int probe_lvm1(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct lvm1_pv_label_header *label; + char uuid[LVM2_ID_LEN + 7]; + unsigned int version; + + label = blkid_probe_get_sb(pr, mag, struct lvm1_pv_label_header); + if (!label) + return errno ? -errno : 1; + + version = le16_to_cpu(label->version); + if (version != 1 && version != 2) + return 1; + + format_lvm_uuid(uuid, (char *) label->pv_uuid); + blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid), + "%s", uuid); + + return 0; +} + +struct verity_sb { + uint8_t signature[8]; /* "verity\0\0" */ + uint32_t version; /* superblock version */ + uint32_t hash_type; /* 0 - Chrome OS, 1 - normal */ + uint8_t uuid[16]; /* UUID of hash device */ + uint8_t algorithm[32];/* hash algorithm name */ + uint32_t data_block_size; /* data block in bytes */ + uint32_t hash_block_size; /* hash block in bytes */ + uint64_t data_blocks; /* number of data blocks */ + uint16_t salt_size; /* salt size */ + uint8_t _pad1[6]; + uint8_t salt[256]; /* salt */ + uint8_t _pad2[168]; +} __attribute__((packed)); + +static int probe_verity(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct verity_sb *sb; + unsigned int version; + + sb = blkid_probe_get_sb(pr, mag, struct verity_sb); + if (sb == NULL) + return errno ? -errno : 1; + + version = le32_to_cpu(sb->version); + if (version != 1) + return 1; + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_sprintf_version(pr, "%u", version); + return 0; +} + +struct integrity_sb { + uint8_t magic[8]; + uint8_t version; + int8_t log2_interleave_sectors; + uint16_t integrity_tag_size; + uint32_t journal_sections; + uint64_t provided_data_sectors; + uint32_t flags; + uint8_t log2_sectors_per_block; +} __attribute__ ((packed)); + +static int probe_integrity(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct integrity_sb *sb; + + sb = blkid_probe_get_sb(pr, mag, struct integrity_sb); + if (sb == NULL) + return errno ? -errno : 1; + + if (!sb->version) + return 1; + + blkid_probe_sprintf_version(pr, "%u", sb->version); + return 0; +} + +/* NOTE: the original libblkid uses "lvm2pv" as a name */ +const struct blkid_idinfo lvm2_idinfo = +{ + .name = "LVM2_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_lvm2, + .magics = + { + { .magic = "LVM2 001", .len = 8, .sboff = 0x218 }, + { .magic = "LVM2 001", .len = 8, .sboff = 0x018 }, + { .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x018 }, + { .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x218 }, + { NULL } + } +}; + +const struct blkid_idinfo lvm1_idinfo = +{ + .name = "LVM1_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_lvm1, + .magics = + { + { .magic = "HM", .len = 2 }, + { NULL } + } +}; + +const struct blkid_idinfo snapcow_idinfo = +{ + .name = "DM_snapshot_cow", + .usage = BLKID_USAGE_OTHER, + .magics = + { + { .magic = "SnAp", .len = 4 }, + { NULL } + } +}; + +const struct blkid_idinfo verity_hash_idinfo = +{ + .name = "DM_verity_hash", + .usage = BLKID_USAGE_CRYPTO, + .probefunc = probe_verity, + .magics = + { + { .magic = "verity\0\0", .len = 8 }, + { NULL } + } +}; + +const struct blkid_idinfo integrity_idinfo = +{ + .name = "DM_integrity", + .usage = BLKID_USAGE_CRYPTO, + .probefunc = probe_integrity, + .magics = + { + { .magic = "integrt\0", .len = 8 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c new file mode 100644 index 0000000..c68ade9 --- /dev/null +++ b/libblkid/src/superblocks/minix.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008-2013 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <string.h> +#include "superblocks.h" +#include "minix.h" + +#define minix_swab16(doit, num) ((uint16_t) (doit ? swab16(num) : num)) +#define minix_swab32(doit, num) ((uint32_t) (doit ? swab32(num) : num)) + +static int get_minix_version(const unsigned char *data, int *other_endian) +{ + const struct minix_super_block *sb = (const struct minix_super_block *) data; + const struct minix3_super_block *sb3 = (const struct minix3_super_block *) data; + int version = 0; + char *endian; + + *other_endian = 0; + + switch (sb->s_magic) { + case MINIX_SUPER_MAGIC: + case MINIX_SUPER_MAGIC2: + version = 1; + break; + case MINIX2_SUPER_MAGIC: + case MINIX2_SUPER_MAGIC2: + version = 2; + break; + default: + if (sb3->s_magic == MINIX3_SUPER_MAGIC) + version = 3; + break; + } + + if (!version) { + *other_endian = 1; + + switch (swab16(sb->s_magic)) { + case MINIX_SUPER_MAGIC: + case MINIX_SUPER_MAGIC2: + version = 1; + break; + case MINIX2_SUPER_MAGIC: + case MINIX2_SUPER_MAGIC2: + version = 2; + break; + default: + if (sb3->s_magic == MINIX3_SUPER_MAGIC) + version = 3; + break; + } + } + if (!version) + return -1; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + endian = *other_endian ? "LE" : "BE"; +#else + endian = *other_endian ? "BE" : "LE"; +#endif + DBG(LOWPROBE, ul_debug("minix version %d detected [%s]", version, + endian)); + return version; +} + +static int probe_minix(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + unsigned char *ext; + const unsigned char *data; + int version = 0, swabme = 0; + unsigned long zones, ninodes, imaps, zmaps; + off_t firstz; + size_t zone_size; + unsigned block_size; + + data = blkid_probe_get_buffer(pr, 1024, + max(sizeof(struct minix_super_block), + sizeof(struct minix3_super_block))); + if (!data) + return errno ? -errno : 1; + version = get_minix_version(data, &swabme); + switch (version) { + case 1: + case 2: { + const struct minix_super_block *sb = (const struct minix_super_block *) data; + + uint16_t state = minix_swab16(swabme, sb->s_state); + if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state) + return 1; + + zones = version == 2 ? minix_swab32(swabme, sb->s_zones) : + minix_swab16(swabme, sb->s_nzones); + ninodes = minix_swab16(swabme, sb->s_ninodes); + imaps = minix_swab16(swabme, sb->s_imap_blocks); + zmaps = minix_swab16(swabme, sb->s_zmap_blocks); + firstz = minix_swab16(swabme, sb->s_firstdatazone); + zone_size = sb->s_log_zone_size; + block_size = 1024; + break; + } + case 3: { + const struct minix3_super_block *sb = (const struct minix3_super_block *) data; + + zones = minix_swab32(swabme, sb->s_zones); + ninodes = minix_swab32(swabme, sb->s_ninodes); + imaps = minix_swab16(swabme, sb->s_imap_blocks); + zmaps = minix_swab16(swabme, sb->s_zmap_blocks); + firstz = minix_swab16(swabme, sb->s_firstdatazone); + zone_size = sb->s_log_zone_size; + block_size = minix_swab16(swabme, sb->s_blocksize); + + break; + } + default: + return 1; + } + + /* sanity checks to be sure that the FS is really minix. + * see disk-utils/fsck.minix.c read_superblock + */ + if (zone_size != 0 || ninodes == 0 || ninodes == UINT32_MAX) + return 1; + if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1) + return 1; + if (firstz > (off_t) zones) + return 1; + if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1) + return 1; + + /* unfortunately, some parts of ext3 is sometimes possible to + * interpreted as minix superblock. So check for extN magic + * string. (For extN magic string and offsets see ext.c.) + */ + ext = blkid_probe_get_buffer(pr, 0x400 + 0x38, 2); + if (!ext) + return errno ? -errno : 1; + + if (memcmp(ext, "\123\357", 2) == 0) + return 1; + + blkid_probe_sprintf_version(pr, "%d", version); + blkid_probe_set_fsblocksize(pr, block_size); + blkid_probe_set_block_size(pr, block_size); + blkid_probe_set_fsendianness(pr, !swabme ? + BLKID_ENDIANNESS_NATIVE : BLKID_ENDIANNESS_OTHER); + return 0; +} + +const struct blkid_idinfo minix_idinfo = +{ + .name = "minix", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_minix, + .magics = + { + /* version 1 - LE */ + { .magic = "\177\023", .len = 2, .kboff = 1, .sboff = 0x10 }, + { .magic = "\217\023", .len = 2, .kboff = 1, .sboff = 0x10 }, + + /* version 1 - BE */ + { .magic = "\023\177", .len = 2, .kboff = 1, .sboff = 0x10 }, + { .magic = "\023\217", .len = 2, .kboff = 1, .sboff = 0x10 }, + + /* version 2 - LE */ + { .magic = "\150\044", .len = 2, .kboff = 1, .sboff = 0x10 }, + { .magic = "\170\044", .len = 2, .kboff = 1, .sboff = 0x10 }, + + /* version 2 - BE */ + { .magic = "\044\150", .len = 2, .kboff = 1, .sboff = 0x10 }, + { .magic = "\044\170", .len = 2, .kboff = 1, .sboff = 0x10 }, + + /* version 3 - LE */ + { .magic = "\132\115", .len = 2, .kboff = 1, .sboff = 0x18 }, + + /* version 3 - BE */ + { .magic = "\115\132", .len = 2, .kboff = 1, .sboff = 0x18 }, + + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/mpool.c b/libblkid/src/superblocks/mpool.c new file mode 100644 index 0000000..b27569e --- /dev/null +++ b/libblkid/src/superblocks/mpool.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Micron Technology, Inc. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include "crc32c.h" +#include "superblocks.h" + +#define MAX_MPOOL_NAME_LEN 32 + +struct omf_sb_descriptor { + uint64_t osb_magic; + uint8_t osb_name[MAX_MPOOL_NAME_LEN]; + unsigned char osb_poolid[16]; /* UUID of pool this drive belongs to */ + uint16_t osb_vers; + uint32_t osb_gen; + uint32_t osb_cksum1; /* crc32c of the preceding fields */ +} __attribute__((packed)); + +static int probe_mpool(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct omf_sb_descriptor *osd; + uint32_t sb_crc; + + osd = blkid_probe_get_sb(pr, mag, struct omf_sb_descriptor); + if (!osd) + return errno ? -errno : 1; + + sb_crc = crc32c(~0L, (const void *)osd, + offsetof(struct omf_sb_descriptor, osb_cksum1)); + sb_crc ^= ~0L; + + if (!blkid_probe_verify_csum(pr, sb_crc, le32_to_cpu(osd->osb_cksum1))) + return 1; + + blkid_probe_set_label(pr, osd->osb_name, sizeof(osd->osb_name)); + blkid_probe_set_uuid(pr, osd->osb_poolid); + + return 0; +} + +/* "mpoolDev" in ASCII */ +#define MPOOL_SB_MAGIC "\x6D\x70\x6f\x6f\x6c\x44\x65\x76" + +const struct blkid_idinfo mpool_idinfo = +{ + .name = "mpool", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_mpool, + .magics = + { + { .magic = MPOOL_SB_MAGIC, .len = 8}, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/netware.c b/libblkid/src/superblocks/netware.c new file mode 100644 index 0000000..af81cf5 --- /dev/null +++ b/libblkid/src/superblocks/netware.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct netware_super_block { + uint8_t SBH_Signature[4]; + uint16_t SBH_VersionMajor; + uint16_t SBH_VersionMinor; + uint16_t SBH_VersionMediaMajor; + uint16_t SBH_VersionMediaMinor; + uint32_t SBH_ItemsMoved; + uint8_t SBH_InternalID[16]; + uint32_t SBH_PackedSize; + uint32_t SBH_Checksum; + uint32_t supersyncid; + int64_t superlocation[4]; + uint32_t physSizeUsed; + uint32_t sizeUsed; + uint32_t superTimeStamp; + uint32_t reserved0[1]; + int64_t SBH_LoggedPoolDataBlk; + int64_t SBH_PoolDataBlk; + uint8_t SBH_OldInternalID[16]; + uint32_t SBH_PoolToLVStartUTC; + uint32_t SBH_PoolToLVEndUTC; + uint16_t SBH_VersionMediaMajorCreate; + uint16_t SBH_VersionMediaMinorCreate; + uint32_t SBH_BlocksMoved; + uint32_t SBH_TempBTSpBlk; + uint32_t SBH_TempFTSpBlk; + uint32_t SBH_TempFTSpBlk1; + uint32_t SBH_TempFTSpBlk2; + uint32_t nssMagicNumber; + uint32_t poolClassID; + uint32_t poolID; + uint32_t createTime; + int64_t SBH_LoggedVolumeDataBlk; + int64_t SBH_VolumeDataBlk; + int64_t SBH_SystemBeastBlkNum; + uint64_t totalblocks; + uint16_t SBH_Name[64]; + uint8_t SBH_VolumeID[16]; + uint8_t SBH_PoolID[16]; + uint8_t SBH_PoolInternalID[16]; + uint64_t SBH_Lsn; + uint32_t SBH_SS_Enabled; + uint32_t SBH_SS_CreateTime; + uint8_t SBH_SS_OriginalPoolID[16]; + uint8_t SBH_SS_OriginalVolumeID[16]; + uint8_t SBH_SS_Guid[16]; + uint16_t SBH_SS_OriginalName[64]; + uint32_t reserved2[64-(2+46)]; +} __attribute__((__packed__)); + +static int probe_netware(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct netware_super_block *nw; + + nw = blkid_probe_get_sb(pr, mag, struct netware_super_block); + if (!nw) + return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, nw->SBH_PoolID); + + blkid_probe_sprintf_version(pr, "%u.%02u", + le16_to_cpu(nw->SBH_VersionMediaMajor), + le16_to_cpu(nw->SBH_VersionMediaMinor)); + + return 0; +} + +const struct blkid_idinfo netware_idinfo = +{ + .name = "nss", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_netware, + .magics = + { + { .magic = "SPB5", .len = 4, .kboff = 4 }, + { NULL } + } +}; + + diff --git a/libblkid/src/superblocks/nilfs.c b/libblkid/src/superblocks/nilfs.c new file mode 100644 index 0000000..0226da5 --- /dev/null +++ b/libblkid/src/superblocks/nilfs.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 by Jiro SEKIBA <jir@unicus.jp> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License + */ +#include <stddef.h> +#include <string.h> + +#include "superblocks.h" +#include "crc32.h" + +struct nilfs_super_block { + uint32_t s_rev_level; + uint16_t s_minor_rev_level; + uint16_t s_magic; + + uint16_t s_bytes; + + uint16_t s_flags; + uint32_t s_crc_seed; + uint32_t s_sum; + + uint32_t s_log_block_size; + + uint64_t s_nsegments; + uint64_t s_dev_size; + uint64_t s_first_data_block; + uint32_t s_blocks_per_segment; + uint32_t s_r_segments_percentage; + + uint64_t s_last_cno; + uint64_t s_last_pseg; + uint64_t s_last_seq; + uint64_t s_free_blocks_count; + + uint64_t s_ctime; + + uint64_t s_mtime; + uint64_t s_wtime; + uint16_t s_mnt_count; + uint16_t s_max_mnt_count; + uint16_t s_state; + uint16_t s_errors; + uint64_t s_lastcheck; + + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + + uint16_t s_inode_size; + uint16_t s_dat_entry_size; + uint16_t s_checkpoint_size; + uint16_t s_segment_usage_size; + + uint8_t s_uuid[16]; + char s_volume_name[80]; + + uint32_t s_c_interval; + uint32_t s_c_block_max; + uint32_t s_reserved[192]; +} __attribute__((__packed__)); + +#define NILFS_SB_MAGIC 0x3434 +#define NILFS_SB_OFFSET 0x400 +#define NILFS_SBB_OFFSET(_sz) ((((_sz) / 0x200) - 8) * 0x200) + +static int nilfs_valid_sb(blkid_probe pr, struct nilfs_super_block *sb, int is_bak) +{ + static unsigned char sum[4]; + const int sumoff = offsetof(struct nilfs_super_block, s_sum); + size_t bytes; + const size_t crc_start = sumoff + 4; + uint32_t crc; + + if (!sb || le16_to_cpu(sb->s_magic) != NILFS_SB_MAGIC) + return 0; + + if (is_bak && blkid_probe_is_wholedisk(pr) && + sb->s_dev_size != pr->size) + return 0; + + bytes = le16_to_cpu(sb->s_bytes); + /* ensure that no underrun can happen in the length parameter + * of the crc32 call or more data are processed than read into + * sb */ + if (bytes < crc_start || bytes > sizeof(struct nilfs_super_block)) + return 0; + + crc = ul_crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff); + crc = ul_crc32(crc, sum, 4); + crc = ul_crc32(crc, (unsigned char *)sb + crc_start, bytes - crc_start); + + return blkid_probe_verify_csum(pr, crc, le32_to_cpu(sb->s_sum)); +} + +static int probe_nilfs2(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct nilfs_super_block *sb, *sbp, *sbb; + int valid[2], swp = 0; + uint64_t magoff; + + /* primary */ + sbp = (struct nilfs_super_block *) blkid_probe_get_buffer( + pr, NILFS_SB_OFFSET, sizeof(struct nilfs_super_block)); + if (!sbp) + return errno ? -errno : 1; + + valid[0] = nilfs_valid_sb(pr, sbp, 0); + + + /* backup */ + sbb = (struct nilfs_super_block *) blkid_probe_get_buffer( + pr, NILFS_SBB_OFFSET(pr->size), sizeof(struct nilfs_super_block)); + if (!sbb) { + valid[1] = 0; + + /* If the primary block is valid then continue and ignore also + * I/O errors for backup block. Note the this is probably CD + * where I/O errors and the end of the disk/session are "normal". + */ + if (!valid[0]) + return errno ? -errno : 1; + } else + valid[1] = nilfs_valid_sb(pr, sbb, 1); + + + /* + * Compare two super blocks and set 1 in swp if the secondary + * super block is valid and newer. Otherwise, set 0 in swp. + */ + if (!valid[0] && !valid[1]) + return 1; + + swp = valid[1] && (!valid[0] || + le64_to_cpu(sbp->s_last_cno) > + le64_to_cpu(sbb->s_last_cno)); + sb = swp ? sbb : sbp; + + DBG(LOWPROBE, ul_debug("nilfs2: primary=%d, backup=%d, swap=%d", + valid[0], valid[1], swp)); + + if (*(sb->s_volume_name) != '\0') + blkid_probe_set_label(pr, (unsigned char *) sb->s_volume_name, + sizeof(sb->s_volume_name)); + + blkid_probe_set_uuid(pr, sb->s_uuid); + blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(sb->s_rev_level)); + + magoff = swp ? NILFS_SBB_OFFSET(pr->size) : NILFS_SB_OFFSET; + magoff += offsetof(struct nilfs_super_block, s_magic); + + if (blkid_probe_set_magic(pr, magoff, sizeof(sb->s_magic), + (unsigned char *) &sb->s_magic)) + return 1; + + if (le32_to_cpu(sb->s_log_block_size) < 32){ + blkid_probe_set_fsblocksize(pr, 1024U << le32_to_cpu(sb->s_log_block_size)); + blkid_probe_set_block_size(pr, 1024U << le32_to_cpu(sb->s_log_block_size)); + } + + return 0; +} + +const struct blkid_idinfo nilfs2_idinfo = +{ + .name = "nilfs2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_nilfs2, + /* default min.size is 128MiB, but 1MiB for "mkfs.nilfs2 -b 1024 -B 16" */ + .minsz = (1024 * 1024), + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c new file mode 100644 index 0000000..ab8c921 --- /dev/null +++ b/libblkid/src/superblocks/ntfs.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <inttypes.h> + +#include "superblocks.h" + +struct ntfs_bios_parameters { + uint16_t sector_size; /* Size of a sector in bytes. */ + uint8_t sectors_per_cluster; /* Size of a cluster in sectors. */ + uint16_t reserved_sectors; /* zero */ + uint8_t fats; /* zero */ + uint16_t root_entries; /* zero */ + uint16_t sectors; /* zero */ + uint8_t media_type; /* 0xf8 = hard disk */ + uint16_t sectors_per_fat; /* zero */ + uint16_t sectors_per_track; /* irrelevant */ + uint16_t heads; /* irrelevant */ + uint32_t hidden_sectors; /* zero */ + uint32_t large_sectors; /* zero */ +} __attribute__ ((__packed__)); + +struct ntfs_super_block { + uint8_t jump[3]; + uint8_t oem_id[8]; /* magic string */ + + struct ntfs_bios_parameters bpb; + + uint16_t unused[2]; + uint64_t number_of_sectors; + uint64_t mft_cluster_location; + uint64_t mft_mirror_cluster_location; + int8_t clusters_per_mft_record; + uint8_t reserved1[3]; + int8_t cluster_per_index_record; + uint8_t reserved2[3]; + uint64_t volume_serial; + uint32_t checksum; +} __attribute__((packed)); + +struct master_file_table_record { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; + uint64_t lsn; + uint16_t sequence_number; + uint16_t link_count; + uint16_t attrs_offset; + uint16_t flags; + uint32_t bytes_in_use; + uint32_t bytes_allocated; +} __attribute__((__packed__)); + +struct file_attribute { + uint32_t type; + uint32_t len; + uint8_t non_resident; + uint8_t name_len; + uint16_t name_offset; + uint16_t flags; + uint16_t instance; + uint32_t value_len; + uint16_t value_offset; +} __attribute__((__packed__)); + +#define MFT_RECORD_VOLUME 3 +/* Windows 10 Creators edition has extended the cluster size limit to 2MB */ +#define NTFS_MAX_CLUSTER_SIZE (2 * 1024 * 1024) + +#define MFT_RECORD_ATTR_VOLUME_NAME 0x60 +#define MFT_RECORD_ATTR_END 0xffffffff + +static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info) +{ + struct ntfs_super_block *ns; + struct master_file_table_record *mft; + + uint32_t sectors_per_cluster, mft_record_size; + uint16_t sector_size; + uint64_t nr_clusters, off, attr_off; + unsigned char *buf_mft; + + ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); + if (!ns) + return errno ? -errno : 1; + + /* + * Check bios parameters block + */ + sector_size = le16_to_cpu(ns->bpb.sector_size); + + if (sector_size < 256 || sector_size > 4096 || !is_power_of_2(sector_size)) + return 1; + + switch (ns->bpb.sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + sectors_per_cluster = ns->bpb.sectors_per_cluster; + break; + default: + if ((ns->bpb.sectors_per_cluster < 240) + || (ns->bpb.sectors_per_cluster > 249)) + return 1; + sectors_per_cluster = 1 << (256 - ns->bpb.sectors_per_cluster); + } + + if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * + sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) + return 1; + + /* Unused fields must be zero */ + if (le16_to_cpu(ns->bpb.reserved_sectors) + || le16_to_cpu(ns->bpb.root_entries) + || le16_to_cpu(ns->bpb.sectors) + || le16_to_cpu(ns->bpb.sectors_per_fat) + || le32_to_cpu(ns->bpb.large_sectors) + || ns->bpb.fats) + return 1; + + if ((uint8_t) ns->clusters_per_mft_record < 0xe1 + || (uint8_t) ns->clusters_per_mft_record > 0xf7) { + + switch (ns->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + return 1; + } + } + + if (ns->clusters_per_mft_record > 0) { + mft_record_size = ns->clusters_per_mft_record * + sectors_per_cluster * sector_size; + } else { + int8_t mft_record_size_shift = 0 - ns->clusters_per_mft_record; + if (mft_record_size_shift < 0 || mft_record_size_shift >= 31) + return 1; + mft_record_size = 1 << mft_record_size_shift; + } + + nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster; + + if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) || + (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters)) + return 1; + + + off = le64_to_cpu(ns->mft_cluster_location) * sector_size * + sectors_per_cluster; + + DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", " + "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" " + "cluster_offset=%"PRIu64"", + sector_size, mft_record_size, + sectors_per_cluster, nr_clusters, + off)); + + if (mft_record_size < 4) + return 1; + + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); + if (!buf_mft) + return errno ? -errno : 1; + + if (memcmp(buf_mft, "FILE", 4) != 0) + return 1; + + off += MFT_RECORD_VOLUME * mft_record_size; + + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); + if (!buf_mft) + return errno ? -errno : 1; + + if (memcmp(buf_mft, "FILE", 4) != 0) + return 1; + + /* return if caller does not care about UUID and LABEL */ + if (!save_info) + return 0; + + mft = (struct master_file_table_record *) buf_mft; + attr_off = le16_to_cpu(mft->attrs_offset); + + while (attr_off + sizeof(struct file_attribute) <= mft_record_size && + attr_off <= le32_to_cpu(mft->bytes_allocated)) { + + uint32_t attr_len; + struct file_attribute *attr; + + attr = (struct file_attribute *) (buf_mft + attr_off); + attr_len = le32_to_cpu(attr->len); + if (!attr_len) + break; + + if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_END) + break; + if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_VOLUME_NAME) { + unsigned int val_off = le16_to_cpu(attr->value_offset); + unsigned int val_len = le32_to_cpu(attr->value_len); + unsigned char *val = ((uint8_t *) attr) + val_off; + + if (attr_off + val_off + val_len <= mft_record_size) + blkid_probe_set_utf8label(pr, val, val_len, + UL_ENCODE_UTF16LE); + break; + } + + attr_off += attr_len; + } + + + blkid_probe_set_fsblocksize(pr, sector_size * sectors_per_cluster); + blkid_probe_set_block_size(pr, sector_size); + blkid_probe_set_fssize(pr, le64_to_cpu(ns->number_of_sectors) * sector_size); + + blkid_probe_sprintf_uuid(pr, + (unsigned char *) &ns->volume_serial, + sizeof(ns->volume_serial), + "%016" PRIX64, le64_to_cpu(ns->volume_serial)); + return 0; +} + +static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + return __probe_ntfs(pr, mag, 1); +} + +int blkid_probe_is_ntfs(blkid_probe pr) +{ + const struct blkid_idmag *mag = NULL; + int rc; + + rc = blkid_probe_get_idmag(pr, &ntfs_idinfo, NULL, &mag); + if (rc < 0) + return rc; /* error */ + if (rc != BLKID_PROBE_OK || !mag) + return 0; + + return __probe_ntfs(pr, mag, 0) == 0 ? 1 : 0; +} + +const struct blkid_idinfo ntfs_idinfo = +{ + .name = "ntfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ntfs, + .magics = + { + { .magic = "NTFS ", .len = 8, .sboff = 3 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/nvidia_raid.c b/libblkid/src/superblocks/nvidia_raid.c new file mode 100644 index 0000000..d930f72 --- /dev/null +++ b/libblkid/src/superblocks/nvidia_raid.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct nv_metadata { + uint8_t vendor[8]; + uint32_t size; + uint32_t chksum; + uint16_t version; +} __attribute__((packed)); + +#define NVIDIA_SIGNATURE "NVIDIA " +#define NVIDIA_SUPERBLOCK_SIZE 120 + + +static int nvraid_verify_checksum(blkid_probe pr, const struct nv_metadata *nv) +{ + uint32_t csum = le32_to_cpu(nv->chksum); + for (size_t i = 0; i < le32_to_cpu(nv->size); i++) + csum += le32_to_cpu(((uint32_t *) nv)[i]); + return blkid_probe_verify_csum(pr, csum, le32_to_cpu(nv->chksum)); +} + +static int probe_nvraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + struct nv_metadata *nv; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200) - 2) * 0x200; + nv = (struct nv_metadata *) + blkid_probe_get_buffer(pr, + off, + NVIDIA_SUPERBLOCK_SIZE); + if (!nv) + return errno ? -errno : 1; + + if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0) + return 1; + if (le32_to_cpu(nv->size) * 4 != NVIDIA_SUPERBLOCK_SIZE) + return 1; + if (!nvraid_verify_checksum(pr, nv)) + return 1; + if (blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(nv->version)) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, sizeof(nv->vendor), + (unsigned char *) nv->vendor)) + return 1; + return 0; +} + +const struct blkid_idinfo nvraid_idinfo = { + .name = "nvidia_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_nvraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/ocfs.c b/libblkid/src/superblocks/ocfs.c new file mode 100644 index 0000000..e213d66 --- /dev/null +++ b/libblkid/src/superblocks/ocfs.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct ocfs_volume_header { + unsigned char minor_version[4]; + unsigned char major_version[4]; + unsigned char signature[128]; + char mount[128]; + unsigned char mount_len[2]; +} __attribute__((packed)); + +struct ocfs_volume_label { + unsigned char disk_lock[48]; + char label[64]; + unsigned char label_len[2]; + unsigned char vol_id[16]; + unsigned char vol_id_len[2]; +} __attribute__((packed)); + +#define ocfsmajor(o) ( (uint32_t) o.major_version[0] \ + + (((uint32_t) o.major_version[1]) << 8) \ + + (((uint32_t) o.major_version[2]) << 16) \ + + (((uint32_t) o.major_version[3]) << 24)) + +#define ocfsminor(o) ( (uint32_t) o.minor_version[0] \ + + (((uint32_t) o.minor_version[1]) << 8) \ + + (((uint32_t) o.minor_version[2]) << 16) \ + + (((uint32_t) o.minor_version[3]) << 24)) + +#define ocfslabellen(o) ((uint32_t)o.label_len[0] + (((uint32_t) o.label_len[1]) << 8)) +#define ocfsmountlen(o) ((uint32_t)o.mount_len[0] + (((uint32_t) o.mount_len[1]) << 8)) + +struct ocfs2_super_block { + uint8_t i_signature[8]; + uint32_t i_generation; + int16_t i_suballoc_slot; + uint16_t i_suballoc_bit; + uint32_t i_reserved0; + uint32_t i_clusters; + uint32_t i_uid; + uint32_t i_gid; + uint64_t i_size; + uint16_t i_mode; + uint16_t i_links_count; + uint32_t i_flags; + uint64_t i_atime; + uint64_t i_ctime; + uint64_t i_mtime; + uint64_t i_dtime; + uint64_t i_blkno; + uint64_t i_last_eb_blk; + uint32_t i_fs_generation; + uint32_t i_atime_nsec; + uint32_t i_ctime_nsec; + uint32_t i_mtime_nsec; + uint64_t i_reserved1[9]; + uint64_t i_pad1; + uint16_t s_major_rev_level; + uint16_t s_minor_rev_level; + uint16_t s_mnt_count; + int16_t s_max_mnt_count; + uint16_t s_state; + uint16_t s_errors; + uint32_t s_checkinterval; + uint64_t s_lastcheck; + uint32_t s_creator_os; + uint32_t s_feature_compat; + uint32_t s_feature_incompat; + uint32_t s_feature_ro_compat; + uint64_t s_root_blkno; + uint64_t s_system_dir_blkno; + uint32_t s_blocksize_bits; + uint32_t s_clustersize_bits; + uint16_t s_max_slots; + uint16_t s_reserved1; + uint32_t s_reserved2; + uint64_t s_first_cluster_group; + uint8_t s_label[64]; + uint8_t s_uuid[16]; +} __attribute__((packed)); + +struct oracle_asm_disk_label { + char dummy[32]; + char dl_tag[8]; + char dl_id[24]; +} __attribute__((packed)); + +static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + unsigned char *buf; + struct ocfs_volume_header ovh; + struct ocfs_volume_label ovl; + uint32_t maj, min; + + /* header */ + buf = blkid_probe_get_buffer(pr, mag->kboff << 10, + sizeof(struct ocfs_volume_header)); + if (!buf) + return errno ? -errno : 1; + memcpy(&ovh, buf, sizeof(ovh)); + + /* label */ + buf = blkid_probe_get_buffer(pr, (mag->kboff << 10) + 512, + sizeof(struct ocfs_volume_label)); + if (!buf) + return errno ? -errno : 1; + memcpy(&ovl, buf, sizeof(ovl)); + + maj = ocfsmajor(ovh); + min = ocfsminor(ovh); + + if (maj == 1) + blkid_probe_set_value(pr, "SEC_TYPE", + (unsigned char *) "ocfs1", sizeof("ocfs1")); + else if (maj >= 9) + blkid_probe_set_value(pr, "SEC_TYPE", + (unsigned char *) "ntocfs", sizeof("ntocfs")); + + if (ocfslabellen(ovl) < sizeof(ovl.label)) + blkid_probe_set_label(pr, (unsigned char *) ovl.label, + ocfslabellen(ovl)); + if (ocfsmountlen(ovh) < sizeof(ovh.mount)) + blkid_probe_set_value(pr, "MOUNT", (unsigned char *) ovh.mount, + ocfsmountlen(ovh)); + blkid_probe_set_uuid(pr, ovl.vol_id); + blkid_probe_sprintf_version(pr, "%u.%u", maj, min); + return 0; +} + +static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct ocfs2_super_block *osb; + + osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block); + if (!osb) + return errno ? -errno : 1; + + blkid_probe_set_label(pr, (unsigned char *) osb->s_label, sizeof(osb->s_label)); + blkid_probe_set_uuid(pr, osb->s_uuid); + + blkid_probe_sprintf_version(pr, "%u.%u", + le16_to_cpu(osb->s_major_rev_level), + le16_to_cpu(osb->s_minor_rev_level)); + + if (le32_to_cpu(osb->s_blocksize_bits) < 32){ + blkid_probe_set_fsblocksize(pr, 1U << le32_to_cpu(osb->s_blocksize_bits)); + blkid_probe_set_block_size(pr, 1U << le32_to_cpu(osb->s_blocksize_bits)); + } + + return 0; +} + +static int probe_oracleasm(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct oracle_asm_disk_label *dl; + + dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label); + if (!dl) + return errno ? -errno : 1; + + blkid_probe_set_label(pr, (unsigned char *) dl->dl_id, sizeof(dl->dl_id)); + return 0; +} + + +const struct blkid_idinfo ocfs_idinfo = +{ + .name = "ocfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ocfs, + .minsz = 14000 * 1024, + .magics = + { + { .magic = "OracleCFS", .len = 9, .kboff = 8 }, + { NULL } + } +}; + +const struct blkid_idinfo ocfs2_idinfo = +{ + .name = "ocfs2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ocfs2, + .minsz = 14000 * 1024, + .magics = + { + { .magic = "OCFSV2", .len = 6, .kboff = 1 }, + { .magic = "OCFSV2", .len = 6, .kboff = 2 }, + { .magic = "OCFSV2", .len = 6, .kboff = 4 }, + { .magic = "OCFSV2", .len = 6, .kboff = 8 }, + { NULL } + } +}; + +/* Oracle ASM (Automatic Storage Management) */ +const struct blkid_idinfo oracleasm_idinfo = +{ + .name = "oracleasm", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_oracleasm, + .magics = + { + { .magic = "ORCLDISK", .len = 8, .sboff = 32 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/promise_raid.c b/libblkid/src/superblocks/promise_raid.c new file mode 100644 index 0000000..75f3a20 --- /dev/null +++ b/libblkid/src/superblocks/promise_raid.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct promise_metadata { + uint8_t sig[24]; +}; + +#define PDC_CONFIG_OFF 0x1200 +#define PDC_SIGNATURE "Promise Technology, Inc." + +static int probe_pdcraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + size_t i; + static unsigned int sectors[] = { + 63, 255, 256, 16, 399, 591, 675, 735, 911, 974, 991, 951, 3087 + }; + uint64_t nsectors; + + if (pr->size < 0x40000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + nsectors = pr->size >> 9; + + for (i = 0; i < ARRAY_SIZE(sectors); i++) { + uint64_t off; + struct promise_metadata *pdc; + + if (nsectors < sectors[i]) + return 1; + + off = (nsectors - sectors[i]) << 9; + pdc = (struct promise_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct promise_metadata)); + if (!pdc) + return errno ? -errno : 1; + + if (memcmp(pdc->sig, PDC_SIGNATURE, + sizeof(PDC_SIGNATURE) - 1) == 0) { + + if (blkid_probe_set_magic(pr, off, sizeof(pdc->sig), + (unsigned char *) pdc->sig)) + return 1; + return 0; + } + } + return 1; +} + +const struct blkid_idinfo pdcraid_idinfo = { + .name = "promise_fasttrack_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_pdcraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/refs.c b/libblkid/src/superblocks/refs.c new file mode 100644 index 0000000..ea81f20 --- /dev/null +++ b/libblkid/src/superblocks/refs.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <inttypes.h> + +#include "superblocks.h" + + +const struct blkid_idinfo refs_idinfo = +{ + .name = "ReFS", + .usage = BLKID_USAGE_FILESYSTEM, + .magics = + { + { .magic = "\000\000\000ReFS\000", .len = 8 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/reiserfs.c b/libblkid/src/superblocks/reiserfs.c new file mode 100644 index 0000000..23d10e2 --- /dev/null +++ b/libblkid/src/superblocks/reiserfs.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" + +struct reiserfs_super_block { + uint32_t rs_blocks_count; + uint32_t rs_free_blocks; + uint32_t rs_root_block; + uint32_t rs_journal_block; + uint32_t rs_journal_dev; + uint32_t rs_orig_journal_size; + uint32_t rs_dummy2[5]; + uint16_t rs_blocksize; + uint16_t rs_dummy3[3]; + unsigned char rs_magic[12]; + uint32_t rs_dummy4[5]; + unsigned char rs_uuid[16]; + char rs_label[16]; +} __attribute__((packed)); + +struct reiser4_super_block { + unsigned char rs4_magic[16]; + uint8_t rs4_dummy[3]; + uint8_t rs4_blocksize; + unsigned char rs4_uuid[16]; + unsigned char rs4_label[16]; + uint64_t rs4_dummy2; +} __attribute__((packed)); + +static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct reiserfs_super_block *rs; + unsigned int blocksize; + + rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block); + if (!rs) + return errno ? -errno : 1; + + blocksize = le16_to_cpu(rs->rs_blocksize); + + /* The blocksize must be at least 512B */ + if ((blocksize >> 9) == 0) + return 1; + + /* If the superblock is inside the journal, we have the wrong one */ + if (mag->kboff / (blocksize >> 9) > le32_to_cpu(rs->rs_journal_block) / 2) + return 1; + + /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ + if (mag->magic[6] == '2' || mag->magic[6] == '3') { + if (*rs->rs_label) + blkid_probe_set_label(pr, + (unsigned char *) rs->rs_label, + sizeof(rs->rs_label)); + blkid_probe_set_uuid(pr, rs->rs_uuid); + } + + if (mag->magic[6] == '3') + blkid_probe_set_version(pr, "JR"); + else if (mag->magic[6] == '2') + blkid_probe_set_version(pr, "3.6"); + else + blkid_probe_set_version(pr, "3.5"); + + blkid_probe_set_fsblocksize(pr, blocksize); + blkid_probe_set_block_size(pr, blocksize); + + return 0; +} + +static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct reiser4_super_block *rs4; + unsigned int blocksize; + + rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block); + if (!rs4) + return errno ? -errno : 1; + + blocksize = rs4->rs4_blocksize * 256; + + if (*rs4->rs4_label) + blkid_probe_set_label(pr, rs4->rs4_label, sizeof(rs4->rs4_label)); + blkid_probe_set_uuid(pr, rs4->rs4_uuid); + blkid_probe_set_version(pr, "4"); + + blkid_probe_set_fsblocksize(pr, blocksize); + blkid_probe_set_block_size(pr, blocksize); + + return 0; +} + + +const struct blkid_idinfo reiser_idinfo = +{ + .name = "reiserfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_reiser, + .minsz = 128 * 1024, + .magics = + { + { .magic = "ReIsErFs", .len = 8, .kboff = 8, .sboff = 0x34 }, + { .magic = "ReIsEr2Fs", .len = 9, .kboff = 64, .sboff = 0x34 }, + { .magic = "ReIsEr3Fs", .len = 9, .kboff = 64, .sboff = 0x34 }, + { .magic = "ReIsErFs", .len = 8, .kboff = 64, .sboff = 0x34 }, + { .magic = "ReIsErFs", .len = 8, .kboff = 8, .sboff = 20 }, + { NULL } + } +}; + +const struct blkid_idinfo reiser4_idinfo = +{ + .name = "reiser4", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_reiser4, + .minsz = 128 * 1024, + .magics = + { + { .magic = "ReIsEr4", .len = 7, .kboff = 64 }, + { NULL } + } +}; + + + + diff --git a/libblkid/src/superblocks/romfs.c b/libblkid/src/superblocks/romfs.c new file mode 100644 index 0000000..456cbfb --- /dev/null +++ b/libblkid/src/superblocks/romfs.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct romfs_super_block { + unsigned char ros_magic[8]; + uint32_t ros_full_size; + uint32_t ros_checksum; + unsigned char ros_volume[16]; +} __attribute__((packed)); + +static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, + const struct romfs_super_block *ros) +{ + uint32_t csummed_size = min((uint32_t) 512, + be32_to_cpu(ros->ros_full_size)); + unsigned char *csummed; + uint32_t csum; + + if (csummed_size % sizeof(uint32_t) != 0) + return 0; + + csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); + if (!csummed) + return 0; + + csum = 0; + while (csummed_size) { + csum += be32_to_cpu(*(uint32_t *) csummed); + csummed_size -= sizeof(uint32_t); + csummed += sizeof(uint32_t); + } + return blkid_probe_verify_csum(pr, csum, 0); +} + +static int probe_romfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct romfs_super_block *ros; + + ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block); + if (!ros) + return errno ? -errno : 1; + + if (!romfs_verify_csum(pr, mag, ros)) + return 1; + + if (*((char *) ros->ros_volume) != '\0') + blkid_probe_set_label(pr, ros->ros_volume, + sizeof(ros->ros_volume)); + + blkid_probe_set_fsblocksize(pr, 1024); + blkid_probe_set_fssize(pr, be32_to_cpu(ros->ros_full_size)); + blkid_probe_set_block_size(pr, 1024); + + return 0; +} + +const struct blkid_idinfo romfs_idinfo = +{ + .name = "romfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_romfs, + .magics = + { + { .magic = "-rom1fs-", .len = 8 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/silicon_raid.c b/libblkid/src/superblocks/silicon_raid.c new file mode 100644 index 0000000..399eea8 --- /dev/null +++ b/libblkid/src/superblocks/silicon_raid.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#include <stddef.h> + +#include "superblocks.h" + +struct silicon_metadata { + uint8_t unknown0[0x2E]; + uint8_t ascii_version[0x36 - 0x2E]; + int8_t diskname[0x56 - 0x36]; + int8_t unknown1[0x60 - 0x56]; + uint32_t magic; + int8_t unknown1a[0x6C - 0x64]; + uint32_t array_sectors_low; + uint32_t array_sectors_high; + int8_t unknown2[0x78 - 0x74]; + uint32_t thisdisk_sectors; + int8_t unknown3[0x100 - 0x7C]; + int8_t unknown4[0x104 - 0x100]; + uint16_t product_id; + uint16_t vendor_id; + uint16_t minor_ver; + uint16_t major_ver; + uint8_t seconds; + uint8_t minutes; + uint8_t hour; + uint8_t day; + uint8_t month; + uint8_t year; + uint16_t raid0_stride; + int8_t unknown6[0x116 - 0x114]; + uint8_t disk_number; + uint8_t type; /* SILICON_TYPE_* */ + int8_t drives_per_striped_set; + int8_t striped_set_number; + int8_t drives_per_mirrored_set; + int8_t mirrored_set_number; + uint32_t rebuild_ptr_low; + uint32_t rebuild_ptr_high; + uint32_t incarnation_no; + uint8_t member_status; + uint8_t mirrored_set_state; /* SILICON_MIRROR_* */ + uint8_t reported_device_location; + uint8_t idechannel; + uint8_t auto_rebuild; + uint8_t unknown8; + uint8_t text_type[0x13E - 0x12E]; + uint16_t checksum1; + int8_t assumed_zeros[0x1FE - 0x140]; + uint16_t checksum2; +} __attribute__((packed)); + +#define SILICON_MAGIC 0x2F000000 + +static uint16_t silraid_checksum(struct silicon_metadata *sil) +{ + int sum = 0; + unsigned short count = offsetof(struct silicon_metadata, checksum1) / 2; + unsigned char *ptr = (unsigned char *) sil; + + while (count--) { + uint16_t val; + + memcpy(&val, ptr, sizeof(uint16_t)); + sum += le16_to_cpu(val); + + ptr += sizeof(uint16_t); + } + + return (-sum & 0xFFFF); +} + +static int probe_silraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + struct silicon_metadata *sil; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200) - 1) * 0x200; + + sil = (struct silicon_metadata *) + blkid_probe_get_buffer(pr, off, + sizeof(struct silicon_metadata)); + if (!sil) + return errno ? -errno : 1; + + if (le32_to_cpu(sil->magic) != SILICON_MAGIC) + return 1; + if (sil->disk_number >= 8) + return 1; + if (!blkid_probe_verify_csum(pr, silraid_checksum(sil), le16_to_cpu(sil->checksum1))) + return 1; + + if (blkid_probe_sprintf_version(pr, "%u.%u", + le16_to_cpu(sil->major_ver), + le16_to_cpu(sil->minor_ver)) != 0) + return 1; + + if (blkid_probe_set_magic(pr, + off + offsetof(struct silicon_metadata, magic), + sizeof(sil->magic), + (unsigned char *) &sil->magic)) + return 1; + return 0; +} + +const struct blkid_idinfo silraid_idinfo = { + .name = "silicon_medley_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_silraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/squashfs.c b/libblkid/src/superblocks/squashfs.c new file mode 100644 index 0000000..92ab77a --- /dev/null +++ b/libblkid/src/superblocks/squashfs.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "bitops.h" /* swab16() */ +#include "superblocks.h" + +struct sqsh_super_block { + uint32_t magic; + uint32_t inode_count; + uint32_t mod_time; + uint32_t block_size; + uint32_t frag_count; + uint16_t compressor; + uint16_t block_log; + uint16_t flags; + uint16_t id_count; + uint16_t version_major; + uint16_t version_minor; + uint64_t root_inode; + uint64_t bytes_used; + uint64_t id_table; + uint64_t xattr_table; + uint64_t inode_table; + uint64_t dir_table; + uint64_t frag_table; + uint64_t export_table; +} __attribute__((packed)); + +static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct sqsh_super_block *sq; + uint16_t vermaj; + uint16_t vermin; + + sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block); + if (!sq) + return errno ? -errno : 1; + + vermaj = le16_to_cpu(sq->version_major); + vermin = le16_to_cpu(sq->version_minor); + if (vermaj < 4) + return 1; + + blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin); + blkid_probe_set_fsblocksize(pr, le32_to_cpu(sq->block_size)); + blkid_probe_set_block_size(pr, le32_to_cpu(sq->block_size)); + blkid_probe_set_fssize(pr, le64_to_cpu(sq->bytes_used)); + + return 0; +} + +static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct sqsh_super_block *sq; + uint16_t vermaj; + uint16_t vermin; + enum BLKID_ENDIANNESS endianness; + + sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block); + if (!sq) + return errno ? -errno : 1; + + if (strcmp(mag->magic, "sqsh") == 0) { + vermaj = be16_to_cpu(sq->version_major); + vermin = be16_to_cpu(sq->version_minor); + endianness = BLKID_ENDIANNESS_BIG; + } else { + vermaj = le16_to_cpu(sq->version_major); + vermin = le16_to_cpu(sq->version_minor); + endianness = BLKID_ENDIANNESS_LITTLE; + } + + if (vermaj > 3) + return 1; + + blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin); + + blkid_probe_set_fsblocksize(pr, 1024); + blkid_probe_set_block_size(pr, 1024); + blkid_probe_set_fsendianness(pr, endianness); + + return 0; +} + +const struct blkid_idinfo squashfs_idinfo = +{ + .name = "squashfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_squashfs, + .magics = + { + { .magic = "hsqs", .len = 4 }, + { NULL } + } +}; + +const struct blkid_idinfo squashfs3_idinfo = +{ + .name = "squashfs3", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_squashfs3, + .magics = + { + { .magic = "sqsh", .len = 4 }, /* big endian */ + { .magic = "hsqs", .len = 4 }, /* little endian */ + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/stratis.c b/libblkid/src/superblocks/stratis.c new file mode 100644 index 0000000..fe4a452 --- /dev/null +++ b/libblkid/src/superblocks/stratis.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 Tony Asleson <tasleson@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/* + * Specification for on disk format + * https://stratis-storage.github.io/StratisSoftwareDesign.pdf + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> + +#include "superblocks.h" +#include "crc32c.h" + +/* Macro to avoid error in struct declaration. */ +#define STRATIS_UUID_LEN 32 +/* Contains 4 hyphens and trailing null byte. */ +const int STRATIS_UUID_STR_LEN = 37; + +struct stratis_sb { + uint32_t crc32; + uint8_t magic[16]; + uint64_t sectors; + uint8_t reserved[4]; + uint8_t pool_uuid[STRATIS_UUID_LEN]; + uint8_t dev_uuid[STRATIS_UUID_LEN]; + uint64_t mda_size; + uint64_t reserved_size; + uint64_t flags; + uint64_t initialization_time; +} __attribute__ ((__packed__)); + +#define BS 512 +#define FIRST_COPY_OFFSET BS +#define SECOND_COPY_OFFSET (BS * 9) +#define SB_AREA_SIZE (BS * 16) + +const char STRATIS_MAGIC[] = "!Stra0tis\x86\xff\x02^\x41rh"; +#define MAGIC_LEN (sizeof(STRATIS_MAGIC) - 1) + +#define _MAGIC_OFFSET (offsetof(const struct stratis_sb, magic)) +#define MAGIC_OFFSET_COPY_1 (FIRST_COPY_OFFSET + _MAGIC_OFFSET) +#define MAGIC_OFFSET_COPY_2 (SECOND_COPY_OFFSET + _MAGIC_OFFSET) + +static int stratis_valid_sb(uint8_t *p) +{ + const struct stratis_sb *stratis = (const struct stratis_sb *)p; + uint32_t crc = 0; + + /* generate CRC from byte position 4 for length 508 == 512 byte sector */ + crc = crc32c(~0L, p + sizeof(stratis->crc32), + BS - sizeof(stratis->crc32)); + crc ^= ~0L; + + return crc == le32_to_cpu(stratis->crc32); +} + +/* + * Format UUID string representation to include hyphens given that Stratis stores + * UUIDs without hyphens in the superblock to keep the UUID length a power of 2. + */ +static void stratis_format_uuid(const unsigned char *src_uuid, + unsigned char *dst_uuid) +{ + int i; + + for (i = 0; i < STRATIS_UUID_LEN; i++) { + *dst_uuid++ = *src_uuid++; + if (i == 7 || i == 11 || i == 15 || i == 19) + *dst_uuid ++ = '-'; + } + *dst_uuid = '\0'; +} + +static int probe_stratis(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const struct stratis_sb *stratis = NULL; + uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE); + unsigned char uuid[STRATIS_UUID_STR_LEN]; + + if (!buf) + return errno ? -errno : 1; + + if (stratis_valid_sb(buf + FIRST_COPY_OFFSET)) { + stratis = (const struct stratis_sb *)(buf + FIRST_COPY_OFFSET); + } else { + if (!stratis_valid_sb(buf + SECOND_COPY_OFFSET)) + return 1; + + stratis = (const struct stratis_sb *) + (buf + SECOND_COPY_OFFSET); + } + + stratis_format_uuid(stratis->dev_uuid, uuid); + blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid)); + + stratis_format_uuid(stratis->pool_uuid, uuid); + blkid_probe_set_value(pr, "POOL_UUID", uuid, sizeof(uuid)); + + blkid_probe_sprintf_value(pr, "BLOCKDEV_SECTORS", "%" PRIu64, + le64_to_cpu(stratis->sectors)); + blkid_probe_sprintf_value(pr, "BLOCKDEV_INITTIME", "%" PRIu64, + le64_to_cpu(stratis->initialization_time)); + return 0; +} + +const struct blkid_idinfo stratis_idinfo = { + .name = "stratis", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_stratis, + .minsz = SB_AREA_SIZE, + .magics = { + { .magic = STRATIS_MAGIC, .len = MAGIC_LEN, + .sboff = MAGIC_OFFSET_COPY_1}, + { .magic = STRATIS_MAGIC, .len = MAGIC_LEN, + .sboff = MAGIC_OFFSET_COPY_2}, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c new file mode 100644 index 0000000..96bc0ef --- /dev/null +++ b/libblkid/src/superblocks/superblocks.c @@ -0,0 +1,951 @@ +/* + * superblocks.c - reads information from filesystem and raid superblocks + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdint.h> +#include <stdarg.h> + +#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 (implemented for XFS/BTRFS/Ext only) + * + * @FSLASTBLOCK: last fsblock/total number of fsblocks + * + * @FSBLOCKSIZE: file system block size + * + * @SYSTEM_ID: ISO9660 system identifier + * + * @PUBLISHER_ID: ISO9660 publisher identifier + * + * @APPLICATION_ID: ISO9660 application identifier + * + * @BOOT_SYSTEM_ID: ISO9660 boot system identifier + * + * @BLOCK_SIZE: minimal block size accessible by file system + */ + +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[] = +{ + /* In case the volume is locked with OPAL we are going to get + * an I/O error when reading past the LUKS header, so try it + * first. */ + &luks_idinfo, + + /* 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, + &bcachefs_idinfo, + &bluestore_idinfo, + &drbd_idinfo, + &drbdmanage_idinfo, + &drbdproxy_datalog_idinfo, + &lvm2_idinfo, + &lvm1_idinfo, + &snapcow_idinfo, + &verity_hash_idinfo, + &integrity_idinfo, + &vmfs_volume_idinfo, + &ubi_idinfo, + &vdo_idinfo, + &stratis_idinfo, + &bitlocker_idinfo, + &cs_fvault2_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, + &apfs_idinfo, + &zonefs_idinfo, + &erofs_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()")); + errno = 0; + 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", + (const 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, + (const 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 = BLKID_PROBE_AMBIGUOUS; /* 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", + (const 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; +} + +int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size) +{ + return blkid_probe_sprintf_value(pr, "BLOCK_SIZE", "%u", block_size); +} + +static int blkid_probe_set_usage(blkid_probe pr, int usage) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + const 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", + (const unsigned char *) u, strlen(u) + 1); +} + +int blkid_probe_set_fssize(blkid_probe pr, uint64_t size) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!(chn->flags & BLKID_SUBLKS_FSINFO)) + return 0; + + return blkid_probe_sprintf_value(pr, "FSSIZE", "%" PRIu64, size); +} + +int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!(chn->flags & BLKID_SUBLKS_FSINFO)) + return 0; + + return blkid_probe_sprintf_value(pr, "FSLASTBLOCK", "%" PRIu64, + lastblock); +} + +int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!(chn->flags & BLKID_SUBLKS_FSINFO)) + return 0; + + return blkid_probe_sprintf_value(pr, "FSBLOCKSIZE", "%" PRIu32, + block_size); +} + +int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + const char *value; + + if (!(chn->flags & BLKID_SUBLKS_FSINFO)) + return 0; + + switch (endianness) { + case BLKID_ENDIANNESS_LITTLE: + value = "LITTLE"; + break; + case BLKID_ENDIANNESS_BIG: + value = "BIG"; + break; + default: + return -EINVAL; + } + + return blkid_probe_set_value(pr, "ENDIANNESS", + (const unsigned char *) value, strlen(value) + 1); + +} + +int blkid_probe_set_id_label(blkid_probe pr, const char *name, + const 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, + const 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->len = (len * 3) + 1; + v->data = calloc(1, v->len); + if (!v->data) + rc = -ENOMEM; + + if (!rc) { + ul_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, const 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, const 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->len = (len * 3) + 1; + v->data = calloc(1, v->len); + if (!v->data) + rc = -ENOMEM; + if (!rc) { + ul_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, const 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, const 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((const 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, const 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, const 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_type(). + */ +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); +} + + diff --git a/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h new file mode 100644 index 0000000..a990058 --- /dev/null +++ b/libblkid/src/superblocks/superblocks.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#ifndef _BLKID_SUPERBLOCKS_H +#define _BLKID_SUPERBLOCKS_H + +#include "blkidP.h" + +enum BLKID_ENDIANNESS { + BLKID_ENDIANNESS_LITTLE, + BLKID_ENDIANNESS_BIG, +}; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define BLKID_ENDIANNESS_NATIVE BLKID_ENDIANNESS_LITTLE +#define BLKID_ENDIANNESS_OTHER BLKID_ENDIANNESS_BIG +#else +#define BLKID_ENDIANNESS_NATIVE BLKID_ENDIANNESS_BIG +#define BLKID_ENDIANNESS_OTHER BLKID_ENDIANNESS_LITTLE +#endif + +extern const struct blkid_idinfo cramfs_idinfo; +extern const struct blkid_idinfo swap_idinfo; +extern const struct blkid_idinfo swsuspend_idinfo; +extern const struct blkid_idinfo adraid_idinfo; +extern const struct blkid_idinfo ddfraid_idinfo; +extern const struct blkid_idinfo iswraid_idinfo; +extern const struct blkid_idinfo jmraid_idinfo; +extern const struct blkid_idinfo lsiraid_idinfo; +extern const struct blkid_idinfo nvraid_idinfo; +extern const struct blkid_idinfo pdcraid_idinfo; +extern const struct blkid_idinfo silraid_idinfo; +extern const struct blkid_idinfo viaraid_idinfo; +extern const struct blkid_idinfo linuxraid_idinfo; +extern const struct blkid_idinfo ext4dev_idinfo; +extern const struct blkid_idinfo ext4_idinfo; +extern const struct blkid_idinfo ext3_idinfo; +extern const struct blkid_idinfo ext2_idinfo; +extern const struct blkid_idinfo jbd_idinfo; +extern const struct blkid_idinfo jfs_idinfo; +extern const struct blkid_idinfo xfs_idinfo; +extern const struct blkid_idinfo xfs_log_idinfo; +extern const struct blkid_idinfo exfs_idinfo; +extern const struct blkid_idinfo gfs_idinfo; +extern const struct blkid_idinfo gfs2_idinfo; +extern const struct blkid_idinfo romfs_idinfo; +extern const struct blkid_idinfo ocfs_idinfo; +extern const struct blkid_idinfo ocfs2_idinfo; +extern const struct blkid_idinfo oracleasm_idinfo; +extern const struct blkid_idinfo reiser_idinfo; +extern const struct blkid_idinfo reiser4_idinfo; +extern const struct blkid_idinfo hfs_idinfo; +extern const struct blkid_idinfo hfsplus_idinfo; +extern const struct blkid_idinfo ntfs_idinfo; +extern const struct blkid_idinfo refs_idinfo; +extern const struct blkid_idinfo iso9660_idinfo; +extern const struct blkid_idinfo udf_idinfo; +extern const struct blkid_idinfo vxfs_idinfo; +extern const struct blkid_idinfo minix_idinfo; +extern const struct blkid_idinfo vfat_idinfo; +extern const struct blkid_idinfo ufs_idinfo; +extern const struct blkid_idinfo hpfs_idinfo; +extern const struct blkid_idinfo lvm2_idinfo; +extern const struct blkid_idinfo lvm1_idinfo; +extern const struct blkid_idinfo snapcow_idinfo; +extern const struct blkid_idinfo verity_hash_idinfo; +extern const struct blkid_idinfo integrity_idinfo; +extern const struct blkid_idinfo luks_idinfo; +extern const struct blkid_idinfo highpoint37x_idinfo; +extern const struct blkid_idinfo highpoint45x_idinfo; +extern const struct blkid_idinfo squashfs_idinfo; +extern const struct blkid_idinfo squashfs3_idinfo; +extern const struct blkid_idinfo netware_idinfo; +extern const struct blkid_idinfo sysv_idinfo; +extern const struct blkid_idinfo xenix_idinfo; +extern const struct blkid_idinfo btrfs_idinfo; +extern const struct blkid_idinfo ubi_idinfo; +extern const struct blkid_idinfo ubifs_idinfo; +extern const struct blkid_idinfo zfs_idinfo; +extern const struct blkid_idinfo bfs_idinfo; +extern const struct blkid_idinfo vmfs_volume_idinfo; +extern const struct blkid_idinfo vmfs_fs_idinfo; +extern const struct blkid_idinfo bluestore_idinfo; +extern const struct blkid_idinfo drbd_idinfo; +extern const struct blkid_idinfo drbdmanage_idinfo; +extern const struct blkid_idinfo drbdproxy_datalog_idinfo; +extern const struct blkid_idinfo befs_idinfo; +extern const struct blkid_idinfo nilfs2_idinfo; +extern const struct blkid_idinfo exfat_idinfo; +extern const struct blkid_idinfo f2fs_idinfo; +extern const struct blkid_idinfo bcache_idinfo; +extern const struct blkid_idinfo bcachefs_idinfo; +extern const struct blkid_idinfo mpool_idinfo; +extern const struct blkid_idinfo vdo_idinfo; +extern const struct blkid_idinfo stratis_idinfo; +extern const struct blkid_idinfo bitlocker_idinfo; +extern const struct blkid_idinfo apfs_idinfo; +extern const struct blkid_idinfo zonefs_idinfo; +extern const struct blkid_idinfo erofs_idinfo; +extern const struct blkid_idinfo cs_fvault2_idinfo; + +/* + * superblock functions + */ +extern int blkid_probe_set_version(blkid_probe pr, const char *version); +extern int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + +extern int blkid_probe_set_label(blkid_probe pr, const unsigned char *label, size_t len); +extern int blkid_probe_set_utf8label(blkid_probe pr, const unsigned char *label, + size_t len, int enc); +extern int blkid_probe_sprintf_uuid(blkid_probe pr, const unsigned char *uuid, + size_t len, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 4, 5))); +extern int blkid_probe_strncpy_uuid(blkid_probe pr, const unsigned char *str, size_t len); + +extern int blkid_probe_set_uuid(blkid_probe pr, const unsigned char *uuid); +extern int blkid_probe_set_uuid_as(blkid_probe pr, const unsigned char *uuid, const char *name); + +extern int blkid_probe_set_id_label(blkid_probe pr, const char *name, + const unsigned char *data, size_t len); +extern int blkid_probe_set_utf8_id_label(blkid_probe pr, const char *name, + const unsigned char *data, size_t len, int enc); + +int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size); +int blkid_probe_set_fssize(blkid_probe pr, uint64_t size); +int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock); +int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size); +int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness); + +extern int blkid_probe_is_bitlocker(blkid_probe pr); +extern int blkid_probe_is_ntfs(blkid_probe pr); + +#endif /* _BLKID_SUPERBLOCKS_H */ diff --git a/libblkid/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c new file mode 100644 index 0000000..67224bc --- /dev/null +++ b/libblkid/src/superblocks/swap.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +/* linux-2.6/include/linux/swap.h */ +struct swap_header_v1_2 { + /* char bootbits[1024]; */ /* Space for disklabel etc. */ + uint32_t version; + uint32_t lastpage; + uint32_t nr_badpages; + unsigned char uuid[16]; + unsigned char volume[16]; + uint32_t padding[117]; + uint32_t badpages[1]; +} __attribute__((packed)); + +#define PAGESIZE_MIN 0xff6 /* 4086 (arm, i386, ...) */ +#define PAGESIZE_MAX 0xfff6 /* 65526 (ia64) */ + +#define TOI_MAGIC_STRING "\xed\xc3\x02\xe9\x98\x56\xe5\x0c" +#define TOI_MAGIC_STRLEN (sizeof(TOI_MAGIC_STRING) - 1) + +static void swap_set_info_swap1(blkid_probe pr, + const struct blkid_idmag *mag, + const struct swap_header_v1_2 *hdr) +{ + enum BLKID_ENDIANNESS endianness = le32_to_cpu(hdr->version) == 1 ? + BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG; + blkid_probe_set_fsendianness(pr, endianness); + + uint32_t pagesize = mag->sboff + mag->len; + blkid_probe_set_fsblocksize(pr, pagesize); + + uint32_t lastpage = endianness == BLKID_ENDIANNESS_LITTLE ? + le32_to_cpu(hdr->lastpage) : be32_to_cpu(hdr->lastpage); + blkid_probe_set_fssize(pr, (uint64_t) pagesize * lastpage); +} + +static int swap_set_info(blkid_probe pr, const struct blkid_idmag *mag, + const char *version) +{ + struct swap_header_v1_2 *hdr; + + /* Swap header always located at offset of 1024 bytes */ + hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024, + sizeof(struct swap_header_v1_2)); + if (!hdr) + return errno ? -errno : 1; + + /* SWAPSPACE2 - check for wrong version or zeroed pagecount */ + if (strcmp(version, "1") == 0) { + if (hdr->version != 1 && swab32(hdr->version) != 1) { + DBG(LOWPROBE, ul_debug("incorrect swap version")); + return 1; + } + if (hdr->lastpage == 0) { + DBG(LOWPROBE, ul_debug("not set last swap page")); + return 1; + } + swap_set_info_swap1(pr, mag, hdr); + } + + /* arbitrary sanity check.. is there any garbage down there? */ + if (hdr->padding[32] == 0 && hdr->padding[33] == 0) { + if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume, + sizeof(hdr->volume)) < 0) + return 1; + if (blkid_probe_set_uuid(pr, hdr->uuid) < 0) + return 1; + } + + blkid_probe_set_version(pr, version); + return 0; +} + +static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag) +{ + unsigned char *buf; + + if (!mag) + return 1; + + /* TuxOnIce keeps valid swap header at the end of the 1st page */ + buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN); + if (!buf) + return errno ? -errno : 1; + + if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0) + return 1; /* Ignore swap signature, it's TuxOnIce */ + + if (!memcmp(mag->magic, "SWAP-SPACE", mag->len)) { + /* swap v0 doesn't support LABEL or UUID */ + blkid_probe_set_version(pr, "0"); + return 0; + + } + + if (!memcmp(mag->magic, "SWAPSPACE2", mag->len)) + return swap_set_info(pr, mag, "1"); + + return 1; +} + +static int probe_swsuspend(blkid_probe pr, const struct blkid_idmag *mag) +{ + if (!mag) + return 1; + if (!memcmp(mag->magic, "S1SUSPEND", mag->len)) + return swap_set_info(pr, mag, "s1suspend"); + if (!memcmp(mag->magic, "S2SUSPEND", mag->len)) + return swap_set_info(pr, mag, "s2suspend"); + if (!memcmp(mag->magic, "ULSUSPEND", mag->len)) + return swap_set_info(pr, mag, "ulsuspend"); + if (!memcmp(mag->magic, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN)) + return swap_set_info(pr, mag, "tuxonice"); + if (!memcmp(mag->magic, "LINHIB0001", mag->len)) + return swap_set_info(pr, mag, "linhib0001"); + + return 1; /* no signature detected */ +} + +const struct blkid_idinfo swap_idinfo = +{ + .name = "swap", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_swap, + .minsz = 10 * 4096, /* 10 pages */ + .magics = + { + { .magic = "SWAP-SPACE", .len = 10, .sboff = 0xff6 }, + { .magic = "SWAPSPACE2", .len = 10, .sboff = 0xff6 }, + { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x1ff6 }, + { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x1ff6 }, + { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x3ff6 }, + { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x3ff6 }, + { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x7ff6 }, + { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x7ff6 }, + { .magic = "SWAP-SPACE", .len = 10, .sboff = 0xfff6 }, + { .magic = "SWAPSPACE2", .len = 10, .sboff = 0xfff6 }, + { NULL } + } +}; + + +const struct blkid_idinfo swsuspend_idinfo = +{ + .name = "swsuspend", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_swsuspend, + .minsz = 10 * 4096, /* 10 pages */ + .magics = + { + { .magic = TOI_MAGIC_STRING, .len = TOI_MAGIC_STRLEN }, + { .magic = "S1SUSPEND", .len = 9, .sboff = 0xff6 }, + { .magic = "S2SUSPEND", .len = 9, .sboff = 0xff6 }, + { .magic = "ULSUSPEND", .len = 9, .sboff = 0xff6 }, + { .magic = "LINHIB0001", .len = 10, .sboff = 0xff6 }, + + { .magic = "S1SUSPEND", .len = 9, .sboff = 0x1ff6 }, + { .magic = "S2SUSPEND", .len = 9, .sboff = 0x1ff6 }, + { .magic = "ULSUSPEND", .len = 9, .sboff = 0x1ff6 }, + { .magic = "LINHIB0001", .len = 10, .sboff = 0x1ff6 }, + + { .magic = "S1SUSPEND", .len = 9, .sboff = 0x3ff6 }, + { .magic = "S2SUSPEND", .len = 9, .sboff = 0x3ff6 }, + { .magic = "ULSUSPEND", .len = 9, .sboff = 0x3ff6 }, + { .magic = "LINHIB0001", .len = 10, .sboff = 0x3ff6 }, + + { .magic = "S1SUSPEND", .len = 9, .sboff = 0x7ff6 }, + { .magic = "S2SUSPEND", .len = 9, .sboff = 0x7ff6 }, + { .magic = "ULSUSPEND", .len = 9, .sboff = 0x7ff6 }, + { .magic = "LINHIB0001", .len = 10, .sboff = 0x7ff6 }, + + { .magic = "S1SUSPEND", .len = 9, .sboff = 0xfff6 }, + { .magic = "S2SUSPEND", .len = 9, .sboff = 0xfff6 }, + { .magic = "ULSUSPEND", .len = 9, .sboff = 0xfff6 }, + { .magic = "LINHIB0001", .len = 10, .sboff = 0xfff6 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c new file mode 100644 index 0000000..421660e --- /dev/null +++ b/libblkid/src/superblocks/sysv.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This is written from scratch according to Linux kernel fs/sysv/super.c file. + * It seems that sysv probing code in libvolume_id and also in the original + * blkid is useless. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <stddef.h> + +#include "superblocks.h" + +#define XENIX_NICINOD 100 +#define XENIX_NICFREE 100 + +struct xenix_super_block { + uint16_t s_isize; + uint32_t s_fsize; + uint16_t s_nfree; + uint32_t s_free[XENIX_NICFREE]; + uint16_t s_ninode; + uint16_t s_inode[XENIX_NICINOD]; + uint8_t s_flock; + uint8_t s_ilock; + uint8_t s_fmod; + uint8_t s_ronly; + uint32_t s_time; + uint32_t s_tfree; + uint16_t s_tinode; + uint16_t s_dinfo[4]; + uint8_t s_fname[6]; + uint8_t s_fpack[6]; + uint8_t s_clean; + uint8_t s_fill[371]; + uint32_t s_magic; + uint32_t s_type; +} __attribute__((packed)); + + +#define SYSV_NICINOD 100 +#define SYSV_NICFREE 50 + +struct sysv_super_block +{ + uint16_t s_isize; + uint16_t s_pad0; + uint32_t s_fsize; + uint16_t s_nfree; + uint16_t s_pad1; + uint32_t s_free[SYSV_NICFREE]; + uint16_t s_ninode; + uint16_t s_pad2; + uint16_t s_inode[SYSV_NICINOD]; + uint8_t s_flock; + uint8_t s_ilock; + uint8_t s_fmod; + uint8_t s_ronly; + uint32_t s_time; + uint16_t s_dinfo[4]; + uint32_t s_tfree; + uint16_t s_tinode; + uint16_t s_pad3; + uint8_t s_fname[6]; + uint8_t s_fpack[6]; + uint32_t s_fill[12]; + uint32_t s_state; + uint32_t s_magic; + uint32_t s_type; +}; + +static int probe_xenix(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct xenix_super_block *sb; + + sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block); + if (!sb) + return errno ? -errno : 1; + blkid_probe_set_label(pr, sb->s_fname, sizeof(sb->s_fname)); + return 0; +} + +#define SYSV_BLOCK_SIZE 1024 + +/* Note that we don't probe for Coherent FS, this FS does not have + * magic string. (It requires to probe fname/fpack field..) + */ +static int probe_sysv(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct sysv_super_block *sb; + int blocks[] = {0, 9, 15, 18}; + size_t i; + + for (i = 0; i < ARRAY_SIZE(blocks); i++) { + int off = blocks[i] * SYSV_BLOCK_SIZE + SYSV_BLOCK_SIZE/2; + + sb = (struct sysv_super_block *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct sysv_super_block)); + if (!sb) + return errno ? -errno : 1; + + if (sb->s_magic == cpu_to_le32(0xfd187e20) || + sb->s_magic == cpu_to_be32(0xfd187e20)) { + + if (blkid_probe_set_label(pr, sb->s_fname, + sizeof(sb->s_fname))) + return 1; + + if (blkid_probe_set_magic(pr, + off + offsetof(struct sysv_super_block, + s_magic), + sizeof(sb->s_magic), + (unsigned char *) &sb->s_magic)) + return 1; + + return 0; + } + } + return 1; +} + +const struct blkid_idinfo xenix_idinfo = +{ + .name = "xenix", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_xenix, + .magics = + { + { .magic = "\x2b\x55\x44", .len = 3, .kboff = 1, .sboff = 0x400 }, + { .magic = "\x44\x55\x2b", .len = 3, .kboff = 1, .sboff = 0x400 }, + { NULL } + } +}; + +const struct blkid_idinfo sysv_idinfo = +{ + .name = "sysv", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_sysv, + + /* SYSV is BE and LE and superblock could be on four positions. It's + * simpler to probe for the magic string by .probefunc(). + */ + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/superblocks/ubi.c b/libblkid/src/superblocks/ubi.c new file mode 100644 index 0000000..fbb7c5e --- /dev/null +++ b/libblkid/src/superblocks/ubi.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" +#include "crc32.h" + +struct ubi_ec_hdr { + uint32_t magic; + uint8_t version; + uint8_t padding1[3]; + uint64_t ec; + uint32_t vid_hdr_offset; + uint32_t data_offset; + uint32_t image_seq; + uint8_t padding2[32]; + uint32_t hdr_crc; +} __attribute__((packed)); + +static int ubi_verify_csum(blkid_probe pr, const struct ubi_ec_hdr *hdr) +{ + return blkid_probe_verify_csum(pr, + ul_crc32(~0LL, (unsigned char *) hdr, + sizeof(*hdr) - sizeof(hdr->hdr_crc)), + be32_to_cpu(hdr->hdr_crc)); +} + +static int probe_ubi(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct ubi_ec_hdr *hdr; + + hdr = blkid_probe_get_sb(pr, mag, struct ubi_ec_hdr); + if (!hdr) + return -1; + + if (!ubi_verify_csum(pr, hdr)) + return -1; + + blkid_probe_sprintf_version(pr, "%u", hdr->version); + blkid_probe_sprintf_uuid(pr, (unsigned char *)&hdr->image_seq, 4, "%u", + be32_to_cpu(hdr->image_seq)); + return 0; +} + +const struct blkid_idinfo ubi_idinfo = +{ + .name = "ubi", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_ubi, + .magics = + { + { .magic = "UBI#", .len = 4 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/ubifs.c b/libblkid/src/superblocks/ubifs.c new file mode 100644 index 0000000..8dece28 --- /dev/null +++ b/libblkid/src/superblocks/ubifs.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2009 Corentin Chary <corentincj@iksaif.net> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdint.h> + +#include "superblocks.h" +#include "crc32.h" + +/* + * struct ubifs_ch - common header node. + * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) + * @crc: CRC-32 checksum of the node header + * @sqnum: sequence number + * @len: full node length + * @node_type: node type + * @group_type: node group type + * @padding: reserved for future, zeroes + * + * Every UBIFS node starts with this common part. If the node has a key, the + * key always goes next. + */ +struct ubifs_ch { + uint32_t magic; + uint32_t crc; + uint64_t sqnum; + uint32_t len; + uint8_t node_type; + uint8_t group_type; + uint8_t padding[2]; +} __attribute__ ((packed)); + +/* + * struct ubifs_sb_node - superblock node. + * @ch: common header + * @padding: reserved for future, zeroes + * @key_hash: type of hash function used in keys + * @key_fmt: format of the key + * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) + * @min_io_size: minimal input/output unit size + * @leb_size: logical eraseblock size in bytes + * @leb_cnt: count of LEBs used by file-system + * @max_leb_cnt: maximum count of LEBs used by file-system + * @max_bud_bytes: maximum amount of data stored in buds + * @log_lebs: log size in logical eraseblocks + * @lpt_lebs: number of LEBs used for lprops table + * @orph_lebs: number of LEBs used for recording orphans + * @jhead_cnt: count of journal heads + * @fanout: tree fanout (max. number of links per indexing node) + * @lsave_cnt: number of LEB numbers in LPT's save table + * @fmt_version: UBIFS on-flash format version + * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) + * @padding1: reserved for future, zeroes + * @rp_uid: reserve pool UID + * @rp_gid: reserve pool GID + * @rp_size: size of the reserved pool in bytes + * @padding2: reserved for future, zeroes + * @time_gran: time granularity in nanoseconds + * @uuid: UUID generated when the file system image was created + * @ro_compat_version: UBIFS R/O compatibility version + */ +struct ubifs_sb_node { + struct ubifs_ch ch; + uint8_t padding[2]; + uint8_t key_hash; + uint8_t key_fmt; + uint32_t flags; + uint32_t min_io_size; + uint32_t leb_size; + uint32_t leb_cnt; + uint32_t max_leb_cnt; + uint64_t max_bud_bytes; + uint32_t log_lebs; + uint32_t lpt_lebs; + uint32_t orph_lebs; + uint32_t jhead_cnt; + uint32_t fanout; + uint32_t lsave_cnt; + uint32_t fmt_version; + uint16_t default_compr; + uint8_t padding1[2]; + uint32_t rp_uid; + uint32_t rp_gid; + uint64_t rp_size; + uint32_t time_gran; + uint8_t uuid[16]; + uint32_t ro_compat_version; + uint8_t padding2[3968]; +} __attribute__ ((packed)); + +static int ubifs_verify_csum(blkid_probe pr, const struct ubifs_sb_node *sb) +{ + size_t csummed_offset = offsetof(struct ubifs_ch, sqnum); + uint32_t crc = ul_crc32(~0LL, + (unsigned char *) sb + csummed_offset, + sizeof(*sb) - csummed_offset); + return blkid_probe_verify_csum(pr, crc, le32_to_cpu(sb->ch.crc)); +} + +static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct ubifs_sb_node *sb; + + sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node); + if (!sb) + return errno ? -errno : 1; + + if (!ubifs_verify_csum(pr, sb)) + return 1; + + blkid_probe_set_uuid(pr, sb->uuid); + blkid_probe_sprintf_version(pr, "w%dr%d", + le32_to_cpu(sb->fmt_version), + le32_to_cpu(sb->ro_compat_version)); + blkid_probe_set_fssize(pr, + (uint64_t) le32_to_cpu(sb->leb_size) + * le32_to_cpu(sb->leb_cnt)); + return 0; +} + +const struct blkid_idinfo ubifs_idinfo = +{ + .name = "ubifs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ubifs, + .magics = + { + { .magic = "\x31\x18\x10\x06", .len = 4 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c new file mode 100644 index 0000000..36adf60 --- /dev/null +++ b/libblkid/src/superblocks/udf.c @@ -0,0 +1,596 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2014-2017 Pali Rohár <pali.rohar@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +#define is_charset_udf(charspec) ((charspec).type == 0 && strncmp((charspec).info, "OSTA Compressed Unicode", sizeof((charspec).info)) == 0) + +#define udf_cid_to_enc(cid) ((cid) == 8 ? UL_ENCODE_LATIN1 : (cid) == 16 ? UL_ENCODE_UTF16BE : -1) + +struct charspec { + uint8_t type; + char info[63]; +} __attribute__((packed)); + +struct dstring128 { + uint8_t cid; + uint8_t c[126]; + uint8_t clen; +} __attribute__((packed)); + +struct dstring32 { + uint8_t cid; + uint8_t c[30]; + uint8_t clen; +} __attribute__((packed)); + +struct dstring36 { + uint8_t cid; + uint8_t c[34]; + uint8_t clen; +} __attribute__((packed)); + +struct volume_descriptor { + struct descriptor_tag { + uint16_t id; + uint16_t version; + uint8_t checksum; + uint8_t reserved; + uint16_t serial; + uint16_t crc; + uint16_t crc_len; + uint32_t location; + } __attribute__((packed)) tag; + + union { + struct anchor_descriptor { + uint32_t length; + uint32_t location; + } __attribute__((packed)) anchor; + + struct primary_descriptor { + uint32_t seq_num; + uint32_t desc_num; + struct dstring32 ident; + uint16_t vds_num; + uint16_t max_vol_seq; + uint16_t ichg_lvl; + uint16_t max_ichg_lvl; + uint32_t charset_list; + uint32_t max_charset_list; + struct dstring128 volset_id; + struct charspec desc_charset; + struct charspec exp_charset; + uint32_t vol_abstract[2]; + uint32_t vol_copyright[2]; + uint8_t app_id_flags; + char app_id[23]; + uint8_t app_id_reserved[8]; + uint8_t recording_date[12]; + uint8_t imp_id_flags; + char imp_id[23]; + uint8_t imp_id_os_class; + uint8_t imp_id_os_id; + uint8_t imp_id_reserved[6]; + } __attribute__((packed)) primary; + + struct logical_descriptor { + uint32_t seq_num; + struct charspec desc_charset; + struct dstring128 logvol_id; + uint32_t logical_blocksize; + uint8_t domain_id_flags; + char domain_id[23]; + uint16_t udf_rev; + uint8_t domain_suffix_flags; + uint8_t reserved[5]; + uint8_t logical_contents_use[16]; + uint32_t map_table_length; + uint32_t num_partition_maps; + uint8_t imp_id[32]; + uint8_t imp_use[128]; + uint32_t lvid_length; + uint32_t lvid_location; + } __attribute__((packed)) logical; + + struct logical_vol_integ_descriptor { + uint8_t recording_date[12]; + uint32_t type; + uint32_t next_lvid_length; + uint32_t next_lvid_location; + uint8_t logical_contents_use[32]; + uint32_t num_partitions; + uint32_t imp_use_length; + } __attribute__((packed)) logical_vol_integ; + + struct imp_use_volume_descriptor { + uint32_t seq_num; + uint8_t lvi_id_flags; + char lvi_id[23]; + uint16_t lvi_id_udf_rev; + uint8_t lvi_id_os_class; + uint8_t lvi_id_os_id; + uint8_t lvi_id_reserved[4]; + struct charspec lvi_charset; + struct dstring128 logvol_id; + struct dstring36 lvinfo1; + struct dstring36 lvinfo2; + struct dstring36 lvinfo3; + } __attribute__((packed)) imp_use_volume; + } __attribute__((packed)) type; + +} __attribute__((packed)); + +#define TAG_ID_PVD 1 +#define TAG_ID_AVDP 2 +#define TAG_ID_IUVD 4 +#define TAG_ID_LVD 6 +#define TAG_ID_TD 8 +#define TAG_ID_LVID 9 + +struct volume_structure_descriptor { + uint8_t type; + uint8_t id[5]; + uint8_t version; +} __attribute__((packed)); + +#define UDF_VSD_OFFSET 0x8000LL + +struct logical_vol_integ_descriptor_imp_use +{ + uint8_t imp_id[32]; + uint32_t num_files; + uint32_t num_dirs; + uint16_t min_udf_read_rev; + uint16_t min_udf_write_rev; + uint16_t max_udf_write_rev; +} __attribute__ ((packed)); + +#define UDF_LVIDIU_OFFSET(vd) (sizeof((vd).tag) + sizeof((vd).type.logical_vol_integ) + 2 * 4 * le32_to_cpu((vd).type.logical_vol_integ.num_partitions)) +#define UDF_LVIDIU_LENGTH(vd) (le32_to_cpu((vd).type.logical_vol_integ.imp_use_length)) + +static inline int gen_uuid_from_volset_id(unsigned char uuid[17], struct dstring128 *volset_id) +{ + int enc; + size_t i; + size_t len; + size_t clen; + size_t nonhexpos; + unsigned char buf[17]; + + memset(buf, 0, sizeof(buf)); + + clen = volset_id->clen; + if (clen > 0) + --clen; + if (clen > sizeof(volset_id->c)) + clen = sizeof(volset_id->c); + + enc = udf_cid_to_enc(volset_id->cid); + if (enc == -1) + return -1; + + len = ul_encode_to_utf8(enc, buf, sizeof(buf), volset_id->c, clen); + if (len < 8) + return -1; + + nonhexpos = 16; + for (i = 0; i < 16; ++i) { + if (!isxdigit(buf[i])) { + nonhexpos = i; + break; + } + } + + if (nonhexpos < 8) { + snprintf((char *) uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + } else if (nonhexpos < 16) { + for (i = 0; i < 8; ++i) + uuid[i] = tolower(buf[i]); + snprintf((char *) uuid + 8, 9, "%02x%02x%02x%02x", + buf[8], buf[9], buf[10], buf[11]); + } else { + for (i = 0; i < 16; ++i) + uuid[i] = tolower(buf[i]); + uuid[16] = 0; + } + + return 0; +} + +static int probe_udf(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct volume_descriptor *vd; + struct volume_structure_descriptor *vsd; + struct logical_vol_integ_descriptor_imp_use *lvidiu; + uint32_t lvid_len = 0; + uint32_t lvid_loc = 0; + uint64_t s_off; + uint32_t bs; + uint32_t b; + uint16_t type; + uint32_t count; + uint32_t loc; + size_t i; + uint32_t vsd_len; + uint16_t udf_rev = 0; + int vsd_2048_valid = -1; + int have_label = 0; + int have_uuid = 0; + int have_logvolid = 0; + int have_volid = 0; + int have_volsetid = 0; + int have_applicationid = 0; + int have_publisherid = 0; + + /* Session offset */ + if (blkid_probe_get_hint(pr, "session_offset", &s_off) < 0) + s_off = 0; + + /* The block size of a UDF filesystem is that of the underlying + * storage; we check later on for the special case of image files, + * which may have any block size valid for UDF filesystem */ + uint32_t pbs[] = { 0, 512, 1024, 2048, 4096 }; + pbs[0] = blkid_probe_get_sectorsize(pr); + + for (i = 0; i < ARRAY_SIZE(pbs); i++) { + /* Do not try with block size same as sector size two times */ + if (i != 0 && pbs[0] == pbs[i]) + continue; + + /* Do not try with block size which is not divisor of session offset */ + if (s_off % pbs[i]) + continue; + + /* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or + * its size is same as blocksize (for blocksize > 2048 bytes) + * plus padded with zeros */ + vsd_len = pbs[i] > 2048 ? pbs[i] : 2048; + + /* Process 2048 bytes long VSD on first session only once + * as its location is same for any blocksize */ + if (s_off == 0 && vsd_len == 2048) { + if (vsd_2048_valid == 0) + continue; + if (vsd_2048_valid == 1) + goto anchor; + } + + /* Check for a Volume Structure Descriptor (VSD) */ + for (b = 0; b < 64; b++) { + vsd = (struct volume_structure_descriptor *) + blkid_probe_get_buffer(pr, + s_off + UDF_VSD_OFFSET + b * vsd_len, + sizeof(*vsd)); + if (!vsd) + return errno ? -errno : 1; + if (vsd->id[0] == '\0') + break; + if (memcmp(vsd->id, "NSR02", 5) == 0 || + memcmp(vsd->id, "NSR03", 5) == 0) + goto anchor; + else if (memcmp(vsd->id, "BEA01", 5) != 0 && + memcmp(vsd->id, "BOOT2", 5) != 0 && + memcmp(vsd->id, "CD001", 5) != 0 && + memcmp(vsd->id, "CDW02", 5) != 0 && + memcmp(vsd->id, "TEA01", 5) != 0) + /* ECMA-167 2/8.3.1: The volume recognition sequence is + * terminated by the first sector which is not a valid + * descriptor. + * UDF-2.60 2.1.7: UDF 2.00 and lower revisions do not + * have requirement that NSR descriptor is in Extended Area + * (between BEA01 and TEA01) and that there is only one + * Extended Area. So do not stop scanning after TEA01. */ + break; + } + + if (s_off == 0 && vsd_len == 2048) + vsd_2048_valid = 0; + + /* NSR was not found, try with next block size */ + continue; + +anchor: + if (s_off == 0 && vsd_len == 2048) + vsd_2048_valid = 1; + + /* Read Anchor Volume Descriptor (AVDP), detect block size */ + vd = (struct volume_descriptor *) + blkid_probe_get_buffer(pr, s_off + 256 * pbs[i], sizeof(*vd)); + if (!vd) + return errno ? -errno : 1; + + /* Check that we read correct sector and detected correct block size */ + if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 256) { + type = le16_to_cpu(vd->tag.id); + if (type == TAG_ID_AVDP) + goto real_blksz; + } + + /* UDF-2.60: 2.2.3: Unclosed sequential Write-Once media may + * have a single AVDP present at either sector 256 or 512. */ + vd = (struct volume_descriptor *) + blkid_probe_get_buffer(pr, s_off + 512 * pbs[i], sizeof(*vd)); + if (!vd) + return errno ? -errno : 1; + + if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 512) { + type = le16_to_cpu(vd->tag.id); + if (type == TAG_ID_AVDP) + goto real_blksz; + } + + } + return 1; + +real_blksz: + /* Use the actual block size from here on out */ + bs = pbs[i]; + + /* get descriptor list address and block count */ + count = le32_to_cpu(vd->type.anchor.length) / bs; + loc = le32_to_cpu(vd->type.anchor.location); + + /* pick the primary descriptor from the list and read UDF identifiers */ + for (b = 0; b < count; b++) { + vd = (struct volume_descriptor *) + blkid_probe_get_buffer(pr, + (uint64_t) (loc + b) * bs, + sizeof(*vd)); + if (!vd) + return errno ? -errno : 1; + type = le16_to_cpu(vd->tag.id); + if (type == 0) + break; + if (le32_to_cpu(vd->tag.location) != loc + b) + break; + if (type == TAG_ID_TD) + break; + if (type == TAG_ID_PVD) { + if (!have_volid && is_charset_udf(vd->type.primary.desc_charset)) { + int enc = udf_cid_to_enc(vd->type.primary.ident.cid); + uint8_t clen = vd->type.primary.ident.clen; + if (clen > 0) + --clen; + if (clen > sizeof(vd->type.primary.ident.c)) + clen = sizeof(vd->type.primary.ident.c); + if (enc != -1) + have_volid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_ID", + vd->type.primary.ident.c, clen, enc); + } + if (!have_uuid && is_charset_udf(vd->type.primary.desc_charset)) { + /* VolumeSetIdentifier in UDF 2.01 specification: + * ================================================================================= + * 2.2.2.5 dstring VolumeSetIdentifier + * + * Interpreted as specifying the identifier for the volume set. + * + * The first 16 characters of this field should be set to a unique value. The + * remainder of the field may be set to any allowed value. Specifically, software + * generating volumes conforming to this specification shall not set this field to a + * fixed or trivial value. Duplicate disks which are intended to be identical may + * contain the same value in this field. + * + * NOTE: The intended purpose of this is to guarantee Volume Sets with unique + * identifiers. The first 8 characters of the unique part should come from a CS0 + * hexadecimal representation of a 32-bit time value. The remaining 8 characters + * are free for implementation use. + * ================================================================================= + * + * Implementation in libblkid: + * The first 16 (Unicode) characters of VolumeSetIdentifier are encoded to UTF-8 + * and then first 16 UTF-8 bytes are used to generate UUID. If all 16 bytes are + * hexadecimal digits then their lowercase variants are used as UUID. If one of + * the first 8 bytes (time value) is not hexadecimal digit then first 8 bytes are + * encoded to their hexadecimal representations, resulting in 16 characters and + * set as UUID. If all first 8 bytes (time value) are hexadecimal digits but some + * remaining not then lowercase variant of the first 8 bytes are used as first + * part of UUID and next 4 bytes encoded in hexadecimal representations (resulting + * in 8 characters) are used as second part of UUID string. + */ + unsigned char uuid[17]; + if (gen_uuid_from_volset_id(uuid, &vd->type.primary.volset_id) == 0) + have_uuid = !blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid)); + } + if (!have_volsetid && is_charset_udf(vd->type.primary.desc_charset)) { + int enc = udf_cid_to_enc(vd->type.primary.volset_id.cid); + uint8_t clen = vd->type.primary.volset_id.clen; + if (clen > 0) + --clen; + if (clen > sizeof(vd->type.primary.volset_id.c)) + clen = sizeof(vd->type.primary.volset_id.c); + if (enc != -1) + have_volsetid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", + vd->type.primary.volset_id.c, clen, enc); + } + if (!have_applicationid) { + /* UDF-2.60: 2.2.2.9: This field specifies a valid Entity Identifier identifying the application that last wrote this field */ + const unsigned char *app_id = (const unsigned char *)vd->type.primary.app_id; + size_t app_id_len = strnlen(vd->type.primary.app_id, sizeof(vd->type.primary.app_id)); + if (app_id_len > 0 && app_id[0] == '*') { + app_id++; + app_id_len--; + } + /* When Application Identifier is not set then use Developer ID from Implementation Identifier */ + if (app_id_len == 0) { + /* UDF-2.60: 2.1.5.2: "*Developer ID" refers to an Entity Identifier that uniquely identifies the current implementation */ + app_id = (const unsigned char *)vd->type.primary.imp_id; + app_id_len = strnlen(vd->type.primary.imp_id, sizeof(vd->type.primary.imp_id)); + if (app_id_len > 0 && app_id[0] == '*') { + app_id++; + app_id_len--; + } + } + if (app_id_len > 0) { + /* UDF-2.60: 2.1.5.2: Values used by UDF for this field are specified in terms of ASCII character strings */ + have_applicationid = !blkid_probe_set_id_label(pr, "APPLICATION_ID", app_id, app_id_len); + } + } + } else if (type == TAG_ID_LVD) { + if (!lvid_len || !lvid_loc) { + uint32_t num_partition_maps = le32_to_cpu(vd->type.logical.num_partition_maps); + /* ECMA-167 3/10.6.12: If num_partition_maps is 0, then no LVID is specified */ + if (num_partition_maps) { + lvid_len = le32_to_cpu(vd->type.logical.lvid_length); + lvid_loc = le32_to_cpu(vd->type.logical.lvid_location); + } + } + if (!udf_rev) { + /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document + * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */ + if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0) + udf_rev = le16_to_cpu(vd->type.logical.udf_rev); + } + if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) { + /* LogicalVolumeIdentifier in UDF 2.01 specification: + * =============================================================== + * 2. Basic Restrictions & Requirements + * + * Logical Volume Descriptor + * + * There shall be exactly one prevailing Logical Volume + * Descriptor recorded per Volume Set. + * + * The LogicalVolumeIdentifier field shall not be null and + * should contain an identifier that aids in the identification of + * the logical volume. Specifically, software generating + * volumes conforming to this specification shall not set this + * field to a fixed or trivial value. Duplicate disks, which are + * intended to be identical, may contain the same value in this + * field. This field is extremely important in logical volume + * identification when multiple media are present within a + * jukebox. This name is typically what is displayed to the user. + * =============================================================== + * + * Implementation in libblkid: + * The LogicalVolumeIdentifier field is used for LABEL. MS Windows + * read Volume Label also from LogicalVolumeIdentifier. Grub2 read + * LABEL also from this field. Program newfs_udf (from UDFclient) + * when formatting disk set this field from user option Disc Name. + */ + int enc = udf_cid_to_enc(vd->type.logical.logvol_id.cid); + uint8_t clen = vd->type.logical.logvol_id.clen; + if (clen > 0) + --clen; + if (clen > sizeof(vd->type.logical.logvol_id.c)) + clen = sizeof(vd->type.logical.logvol_id.c); + if (enc != -1) { + if (!have_label) + have_label = !blkid_probe_set_utf8label(pr, + vd->type.logical.logvol_id.c, clen, enc); + if (!have_logvolid) + have_logvolid = !blkid_probe_set_utf8_id_label(pr, "LOGICAL_VOLUME_ID", + vd->type.logical.logvol_id.c, clen, enc); + } + } + } else if (type == TAG_ID_IUVD) { + if (!have_publisherid && strncmp(vd->type.imp_use_volume.lvi_id, "*UDF LV Info", sizeof(vd->type.imp_use_volume.lvi_id)) == 0 && is_charset_udf(vd->type.imp_use_volume.lvi_charset)) { + /* UDF-2.60: 2.2.7.2.3: Field LVInfo1 could contain information such as Owner Name + * More UDF generating tools set this field to person who creating the filesystem + * therefore its meaning is similar to ISO9660 Publisher Identifier. So for + * compatibility with iso9660 superblock code export this field via PUBLISHER_ID. + */ + int enc = udf_cid_to_enc(vd->type.imp_use_volume.lvinfo1.cid); + uint8_t clen = vd->type.imp_use_volume.lvinfo1.clen; + if (clen > 0) + --clen; + if (clen > sizeof(vd->type.imp_use_volume.lvinfo1.c)) + clen = sizeof(vd->type.imp_use_volume.lvinfo1.c); + if (enc != -1) + have_publisherid = !blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", + vd->type.imp_use_volume.lvinfo1.c, clen, enc); + } + } + if (have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid) + break; + } + + /* Pick the first logical volume integrity descriptor and read UDF revision */ + if (lvid_loc && lvid_len >= sizeof(*vd)) { + vd = (struct volume_descriptor *) + blkid_probe_get_buffer(pr, + (uint64_t) lvid_loc * bs, + sizeof(*vd)); + if (!vd) + return errno ? -errno : 1; + type = le16_to_cpu(vd->tag.id); + if (type == TAG_ID_LVID && + le32_to_cpu(vd->tag.location) == lvid_loc && + UDF_LVIDIU_LENGTH(*vd) >= sizeof(*lvidiu)) { + /* ECMA-167 3/8.8.2: There is stored sequence of LVIDs and valid is just last + * one. So correctly we should jump to next_lvid_location and read next LVID + * until we find last one. This could be time consuming process and could + * lead to scanning lot of disk blocks. Because we use LVID only for UDF + * version, in the worst case we would report only wrong ID_FS_VERSION. */ + uint16_t lvidiu_udf_rev; + lvidiu = (struct logical_vol_integ_descriptor_imp_use *) + blkid_probe_get_buffer(pr, + (uint64_t) lvid_loc * bs + UDF_LVIDIU_OFFSET(*vd), + sizeof(*lvidiu)); + if (!lvidiu) + return errno ? -errno : 1; + /* UDF-2.60: 2. Basic Restrictions & Requirements: + * The Minimum UDF Read Revision value shall be at most #0250 + * for all media with a UDF 2.60 file system. + * Because some 2.60 implementations put 2.50 into both LVIDIU + * fields and 2.60 into LVD, use maximal value from LVD, + * Minimum UDF Read Revision and Minimum UDF Write Revision for + * ID_FS_VERSION to distinguish between UDF 2.50 and UDF 2.60 discs. */ + lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_read_rev); + if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev) + udf_rev = lvidiu_udf_rev; + lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_write_rev); + if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev) + udf_rev = lvidiu_udf_rev; + } + } + + if (udf_rev) + /* UDF revision is stored as decimal number in hexadecimal format. + * E.g. number 0x0150 is revision 1.50, number 0x0201 is revision 2.01. */ + blkid_probe_sprintf_version(pr, "%x.%02x", (unsigned int)(udf_rev >> 8), (unsigned int)(udf_rev & 0xFF)); + + blkid_probe_set_fsblocksize(pr, bs); + blkid_probe_set_block_size(pr, bs); + + return 0; +} + + +const struct blkid_idinfo udf_idinfo = +{ + .name = "udf", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_udf, + .flags = BLKID_IDINFO_TOLERANT, + .magics = + { + { .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/ufs.c b/libblkid/src/superblocks/ufs.c new file mode 100644 index 0000000..0c8d4ed --- /dev/null +++ b/libblkid/src/superblocks/ufs.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#include <stddef.h> + +#include "superblocks.h" + +struct ufs_super_block { + uint32_t fs_link; + uint32_t fs_rlink; + uint32_t fs_sblkno; + uint32_t fs_cblkno; + uint32_t fs_iblkno; + uint32_t fs_dblkno; + uint32_t fs_cgoffset; + uint32_t fs_cgmask; + uint32_t fs_time; + uint32_t fs_size; + uint32_t fs_dsize; + uint32_t fs_ncg; + uint32_t fs_bsize; + uint32_t fs_fsize; + uint32_t fs_frag; + uint32_t fs_minfree; + uint32_t fs_rotdelay; + uint32_t fs_rps; + uint32_t fs_bmask; + uint32_t fs_fmask; + uint32_t fs_bshift; + uint32_t fs_fshift; + uint32_t fs_maxcontig; + uint32_t fs_maxbpg; + uint32_t fs_fragshift; + uint32_t fs_fsbtodb; + uint32_t fs_sbsize; + uint32_t fs_csmask; + uint32_t fs_csshift; + uint32_t fs_nindir; + uint32_t fs_inopb; + uint32_t fs_nspf; + uint32_t fs_optim; + uint32_t fs_npsect_state; + uint32_t fs_interleave; + uint32_t fs_trackskew; + uint32_t fs_id[2]; + uint32_t fs_csaddr; + uint32_t fs_cssize; + uint32_t fs_cgsize; + uint32_t fs_ntrak; + uint32_t fs_nsect; + uint32_t fs_spc; + uint32_t fs_ncyl; + uint32_t fs_cpg; + uint32_t fs_ipg; + uint32_t fs_fpg; + struct ufs_csum { + uint32_t cs_ndir; + uint32_t cs_nbfree; + uint32_t cs_nifree; + uint32_t cs_nffree; + } fs_cstotal; + int8_t fs_fmod; + int8_t fs_clean; + int8_t fs_ronly; + int8_t fs_flags; + union { + struct { + int8_t fs_fsmnt[512]; + uint32_t fs_cgrotor; + uint32_t fs_csp[31]; + uint32_t fs_maxcluster; + uint32_t fs_cpc; + uint16_t fs_opostbl[16][8]; + } fs_u1; + struct { + int8_t fs_fsmnt[468]; + uint8_t fs_volname[32]; + uint64_t fs_swuid; + int32_t fs_pad; + uint32_t fs_cgrotor; + uint32_t fs_ocsp[28]; + uint32_t fs_contigdirs; + uint32_t fs_csp; + uint32_t fs_maxcluster; + uint32_t fs_active; + int32_t fs_old_cpc; + int32_t fs_maxbsize; + int64_t fs_sparecon64[17]; + int64_t fs_sblockloc; + struct ufs2_csum_total { + uint64_t cs_ndir; + uint64_t cs_nbfree; + uint64_t cs_nifree; + uint64_t cs_nffree; + uint64_t cs_numclusters; + uint64_t cs_spare[3]; + } fs_cstotal; + struct ufs_timeval { + int32_t tv_sec; + int32_t tv_usec; + } fs_time; + int64_t fs_size; + int64_t fs_dsize; + uint64_t fs_csaddr; + int64_t fs_pendingblocks; + int32_t fs_pendinginodes; + } __attribute__((packed)) fs_u2; + } fs_u11; + union { + struct { + int32_t fs_sparecon[53]; + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + int32_t fs_state; + uint32_t fs_qbmask[2]; + uint32_t fs_qfmask[2]; + } fs_sun; + struct { + int32_t fs_sparecon[53]; + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + uint32_t fs_npsect; + uint32_t fs_qbmask[2]; + uint32_t fs_qfmask[2]; + } fs_sunx86; + struct { + int32_t fs_sparecon[50]; + int32_t fs_contigsumsize; + int32_t fs_maxsymlinklen; + int32_t fs_inodefmt; + uint32_t fs_maxfilesize[2]; + uint32_t fs_qbmask[2]; + uint32_t fs_qfmask[2]; + int32_t fs_state; + } fs_44; + } fs_u2; + int32_t fs_postblformat; + int32_t fs_nrpos; + int32_t fs_postbloff; + int32_t fs_rotbloff; + uint32_t fs_magic; + uint8_t fs_space[1]; +} __attribute__((packed)); + +#define UFS_MAGIC 0x00011954 +#define UFS2_MAGIC 0x19540119 +#define UFS_MAGIC_FEA 0x00195612 +#define UFS_MAGIC_LFN 0x00095014 +#define UFS_MAGIC_SEC 0x00612195 +#define UFS_MAGIC_4GB 0x05231994 + +static int probe_ufs(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int offsets[] = { 0, 8, 64, 256 }; + uint32_t mags[] = { + UFS2_MAGIC, UFS_MAGIC, UFS_MAGIC_FEA, UFS_MAGIC_LFN, + UFS_MAGIC_SEC, UFS_MAGIC_4GB + }; + size_t i; + uint32_t magic; + struct ufs_super_block *ufs; + int is_be; + + for (i = 0; i < ARRAY_SIZE(offsets); i++) { + uint32_t magLE, magBE; + size_t y; + + ufs = (struct ufs_super_block *) + blkid_probe_get_buffer(pr, + offsets[i] * 1024, + sizeof(struct ufs_super_block)); + if (!ufs) + return errno ? -errno : 1; + + magBE = be32_to_cpu(ufs->fs_magic); + magLE = le32_to_cpu(ufs->fs_magic); + + for (y = 0; y < ARRAY_SIZE(mags); y++) { + if (magLE == mags[y] || magBE == mags[y]) { + magic = mags[y]; + is_be = (magBE == mags[y]); + goto found; + } + } + } + + return 1; + +found: + if (magic == UFS2_MAGIC) { + blkid_probe_set_version(pr, "2"); + blkid_probe_set_label(pr, ufs->fs_u11.fs_u2.fs_volname, + sizeof(ufs->fs_u11.fs_u2.fs_volname)); + } else + blkid_probe_set_version(pr, "1"); + if (ufs->fs_id[0] || ufs->fs_id[1]) + { + if (is_be) + blkid_probe_sprintf_uuid(pr, + (unsigned char *) &ufs->fs_id, + sizeof(ufs->fs_id), + "%08x%08x", + be32_to_cpu(ufs->fs_id[0]), + be32_to_cpu(ufs->fs_id[1])); + else + blkid_probe_sprintf_uuid(pr, + (unsigned char *) &ufs->fs_id, + sizeof(ufs->fs_id), + "%08x%08x", + le32_to_cpu(ufs->fs_id[0]), + le32_to_cpu(ufs->fs_id[1])); + } + + if (blkid_probe_set_magic(pr, + (offsets[i] * 1024) + + offsetof(struct ufs_super_block, fs_magic), + sizeof(ufs->fs_magic), + (unsigned char *) &ufs->fs_magic)) + return 1; + + uint32_t bsize = 0; + if (!is_be) + bsize = le32_to_cpu(ufs->fs_fsize); + else + bsize = be32_to_cpu(ufs->fs_fsize); + + blkid_probe_set_fsblocksize(pr, bsize); + blkid_probe_set_block_size(pr, bsize); + blkid_probe_set_fsendianness(pr, is_be ? + BLKID_ENDIANNESS_BIG : BLKID_ENDIANNESS_LITTLE); + + return 0; +} + +/* + * According to libvolume_id the UFS superblock could be on four positions. + * The original libblkid has checked one position (.kboff=8) only. + * + * We know four UFS magic strings and UFS could be both little-endian and + * big-endian. ... so we have: + * + * 4 position * 4 string * 2 version = 32 magic strings + * + * It seems simpler to check for these string in probing function that hardcode + * all in the .magic array. + */ +const struct blkid_idinfo ufs_idinfo = +{ + .name = "ufs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ufs, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/superblocks/vdo.c b/libblkid/src/superblocks/vdo.c new file mode 100644 index 0000000..bec686f --- /dev/null +++ b/libblkid/src/superblocks/vdo.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct vdo_super_block { + char magic[8]; /* magic number 'dmvdo001'*/ + char unused[32]; /* 32 bytes of unimportant space */ + unsigned char sb_uuid[16]; /* vdo unique id */ + + /* this is not all... but enough for libblkid */ +} __attribute__((packed)); + +static int probe_vdo(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vdo_super_block *vsb; + + vsb = blkid_probe_get_sb(pr, mag, struct vdo_super_block); + if (!vsb) + return errno ? -errno : 1; + + blkid_probe_set_uuid(pr, vsb->sb_uuid); + return 0; +} + +const struct blkid_idinfo vdo_idinfo = +{ + .name = "vdo", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_vdo, + .magics = + { + { .magic = "dmvdo001", .len = 8 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/vfat.c b/libblkid/src/superblocks/vfat.c new file mode 100644 index 0000000..6b4a3ad --- /dev/null +++ b/libblkid/src/superblocks/vfat.c @@ -0,0 +1,462 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#include <inttypes.h> + +#include "pt-mbr.h" +#include "superblocks.h" + +/* Yucky misaligned values */ +struct vfat_super_block { +/* 00*/ unsigned char vs_ignored[3]; +/* 03*/ unsigned char vs_sysid[8]; +/* 0b*/ unsigned char vs_sector_size[2]; +/* 0d*/ uint8_t vs_cluster_size; +/* 0e*/ uint16_t vs_reserved; +/* 10*/ uint8_t vs_fats; +/* 11*/ unsigned char vs_dir_entries[2]; +/* 13*/ unsigned char vs_sectors[2]; +/* 15*/ unsigned char vs_media; +/* 16*/ uint16_t vs_fat_length; +/* 18*/ uint16_t vs_secs_track; +/* 1a*/ uint16_t vs_heads; +/* 1c*/ uint32_t vs_hidden; +/* 20*/ uint32_t vs_total_sect; +/* 24*/ uint32_t vs_fat32_length; +/* 28*/ uint16_t vs_flags; +/* 2a*/ uint8_t vs_version[2]; +/* 2c*/ uint32_t vs_root_cluster; +/* 30*/ uint16_t vs_fsinfo_sector; +/* 32*/ uint16_t vs_backup_boot; +/* 34*/ uint16_t vs_reserved2[6]; +/* 40*/ unsigned char vs_drive_number; +/* 41*/ unsigned char vs_boot_flags; +/* 42*/ unsigned char vs_ext_boot_sign; /* 0x28 - without vs_label/vs_magic; 0x29 - with */ +/* 43*/ unsigned char vs_serno[4]; +/* 47*/ unsigned char vs_label[11]; +/* 52*/ unsigned char vs_magic[8]; +/* 5a*/ unsigned char vs_dummy2[0x1fe - 0x5a]; +/*1fe*/ unsigned char vs_pmagic[2]; +} __attribute__((packed)); + +/* Yucky misaligned values */ +struct msdos_super_block { +/* DOS 2.0 BPB */ +/* 00*/ unsigned char ms_ignored[3]; +/* 03*/ unsigned char ms_sysid[8]; +/* 0b*/ unsigned char ms_sector_size[2]; +/* 0d*/ uint8_t ms_cluster_size; +/* 0e*/ uint16_t ms_reserved; +/* 10*/ uint8_t ms_fats; +/* 11*/ unsigned char ms_dir_entries[2]; +/* 13*/ unsigned char ms_sectors[2]; /* =0 iff V3 or later */ +/* 15*/ unsigned char ms_media; +/* 16*/ uint16_t ms_fat_length; /* Sectors per FAT */ +/* DOS 3.0 BPB */ +/* 18*/ uint16_t ms_secs_track; +/* 1a*/ uint16_t ms_heads; +/* 1c*/ uint32_t ms_hidden; +/* DOS 3.31 BPB */ +/* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */ +/* DOS 3.4 EBPB */ +/* 24*/ unsigned char ms_drive_number; +/* 25*/ unsigned char ms_boot_flags; +/* 26*/ unsigned char ms_ext_boot_sign; /* 0x28 - DOS 3.4 EBPB; 0x29 - DOS 4.0 EBPB */ +/* 27*/ unsigned char ms_serno[4]; +/* DOS 4.0 EBPB */ +/* 2b*/ unsigned char ms_label[11]; +/* 36*/ unsigned char ms_magic[8]; +/* padding */ +/* 3e*/ unsigned char ms_dummy2[0x1fe - 0x3e]; +/*1fe*/ unsigned char ms_pmagic[2]; +} __attribute__((packed)); + +struct vfat_dir_entry { + uint8_t name[11]; + uint8_t attr; + uint16_t time_creat; + uint16_t date_creat; + uint16_t time_acc; + uint16_t date_acc; + uint16_t cluster_high; + uint16_t time_write; + uint16_t date_write; + uint16_t cluster_low; + uint32_t size; +} __attribute__((packed)); + +struct fat32_fsinfo { + uint8_t signature1[4]; + uint32_t reserved1[120]; + uint8_t signature2[4]; + uint32_t free_clusters; + uint32_t next_cluster; + uint32_t reserved2[4]; +} __attribute__((packed)); + +/* maximum number of clusters */ +#define FAT12_MAX 0xFF4 +#define FAT16_MAX 0xFFF4 +#define FAT32_MAX 0x0FFFFFF6 + +#define FAT_ATTR_VOLUME_ID 0x08 +#define FAT_ATTR_DIR 0x10 +#define FAT_ATTR_LONG_NAME 0x0f +#define FAT_ATTR_MASK 0x3f +#define FAT_ENTRY_FREE 0xe5 + +static const char *no_name = "NO NAME "; + +#define unaligned_le16(x) \ + (((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8)) + +/* + * Look for LABEL (name) in the FAT root directory. + */ +static int search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries, unsigned char out[11]) +{ + const struct vfat_dir_entry *ent, *dir = NULL; + uint32_t i; + + DBG(LOWPROBE, ul_debug("\tlook for label in root-dir " + "(entries: %"PRIu32", offset: %"PRIu64")", entries, offset)); + + if (!blkid_probe_is_tiny(pr)) { + /* large disk, read whole root directory */ + dir = (struct vfat_dir_entry *) + blkid_probe_get_buffer(pr, + offset, + (uint64_t) entries * + sizeof(struct vfat_dir_entry)); + if (!dir) + return 0; + } + + for (i = 0; i < entries; i++) { + /* + * The root directory could be relatively large (4-16kB). + * Fortunately, the LABEL is usually the first entry in the + * directory. On tiny disks we call read() per entry. + */ + if (!dir) + ent = (struct vfat_dir_entry *) + blkid_probe_get_buffer(pr, + (uint64_t) offset + (i * + sizeof(struct vfat_dir_entry)), + sizeof(struct vfat_dir_entry)); + else + ent = &dir[i]; + + if (!ent || ent->name[0] == 0x00) + break; + + if ((ent->name[0] == FAT_ENTRY_FREE) || + (ent->cluster_high != 0 || ent->cluster_low != 0) || + ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)) + continue; + + if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == + FAT_ATTR_VOLUME_ID) { + DBG(LOWPROBE, ul_debug("\tfound fs LABEL at entry %d", i)); + memcpy(out, ent->name, 11); + if (out[0] == 0x05) + out[0] = 0xE5; + return 1; + } + } + return 0; +} + +static int fat_valid_superblock(blkid_probe pr, + const struct blkid_idmag *mag, + struct msdos_super_block *ms, + struct vfat_super_block *vs, + uint32_t *cluster_count, uint32_t *fat_size, + uint32_t *sect_count) +{ + uint16_t sector_size, dir_entries, reserved; + uint32_t __sect_count, __fat_size, dir_size, __cluster_count, fat_length; + uint32_t max_count; + + /* extra check for FATs without magic strings */ + if (mag->len <= 2) { + /* Old floppies have a valid MBR signature */ + if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA) + return 0; + + /* + * OS/2 and apparently DFSee will place a FAT12/16-like + * pseudo-superblock in the first 512 bytes of non-FAT + * filesystems --- at least JFS and HPFS, and possibly others. + * So we explicitly check for those filesystems at the + * FAT12/16 filesystem magic field identifier, and if they are + * present, we rule this out as a FAT filesystem, despite the + * FAT-like pseudo-header. + */ + if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) || + (memcmp(ms->ms_magic, "HPFS ", 8) == 0)) { + DBG(LOWPROBE, ul_debug("\tJFS/HPFS detected")); + return 0; + } + } + + /* fat counts(Linux kernel expects at least 1 FAT table) */ + if (!ms->ms_fats) + return 0; + if (!ms->ms_reserved) + return 0; + if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0)) + return 0; + if (!is_power_of_2(ms->ms_cluster_size)) + return 0; + + sector_size = unaligned_le16(&ms->ms_sector_size); + if (!is_power_of_2(sector_size) || + sector_size < 512 || sector_size > 4096) + return 0; + + dir_entries = unaligned_le16(&ms->ms_dir_entries); + reserved = le16_to_cpu(ms->ms_reserved); + __sect_count = unaligned_le16(&ms->ms_sectors); + + if (__sect_count == 0) + __sect_count = le32_to_cpu(ms->ms_total_sect); + + fat_length = le16_to_cpu(ms->ms_fat_length); + if (fat_length == 0) + fat_length = le32_to_cpu(vs->vs_fat32_length); + + __fat_size = fat_length * ms->ms_fats; + dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) + + (sector_size-1)) / sector_size; + + __cluster_count = (__sect_count - (reserved + __fat_size + dir_size)) / + ms->ms_cluster_size; + if (!ms->ms_fat_length && vs->vs_fat32_length) + max_count = FAT32_MAX; + else + max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX; + + if (__cluster_count > max_count) + return 0; + + if (fat_size) + *fat_size = __fat_size; + if (cluster_count) + *cluster_count = __cluster_count; + if (sect_count) + *sect_count = __sect_count; + + if (blkid_probe_is_bitlocker(pr)) + return 0; + + return 1; /* valid */ +} + +/* function prototype to avoid warnings (duplicate in partitions/dos.c) */ +extern int blkid_probe_is_vfat(blkid_probe pr); + +/* + * This function is used by MBR partition table parser to avoid + * misinterpretation of FAT filesystem. + */ +int blkid_probe_is_vfat(blkid_probe pr) +{ + struct vfat_super_block *vs; + struct msdos_super_block *ms; + const struct blkid_idmag *mag = NULL; + int rc; + + rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag); + if (rc < 0) + return rc; /* error */ + if (rc != BLKID_PROBE_OK || !mag) + return 0; + + ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!ms) + return errno ? -errno : 0; + vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!vs) + return errno ? -errno : 0; + + return fat_valid_superblock(pr, mag, ms, vs, NULL, NULL, NULL); +} + +/* FAT label extraction from the root directory taken from Kay + * Sievers's volume_id library */ +static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vfat_super_block *vs; + struct msdos_super_block *ms; + const unsigned char *vol_label = NULL; + const unsigned char *boot_label = NULL; + unsigned char *vol_serno = NULL, vol_label_buf[11]; + uint16_t sector_size = 0, reserved; + uint32_t cluster_count, fat_size, sect_count; + const char *version = NULL; + + ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); + if (!ms) + return errno ? -errno : 1; + + vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); + if (!vs) + return errno ? -errno : 1; + + if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size, + §_count)) + return 1; + + sector_size = unaligned_le16(&ms->ms_sector_size); + reserved = le16_to_cpu(ms->ms_reserved); + + if (ms->ms_fat_length) { + /* the label may be an attribute in the root directory */ + uint32_t root_start = (reserved + fat_size) * sector_size; + uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries); + + if (search_fat_label(pr, root_start, root_dir_entries, vol_label_buf)) + vol_label = vol_label_buf; + + if (ms->ms_ext_boot_sign == 0x29) + boot_label = ms->ms_label; + + if (ms->ms_ext_boot_sign == 0x28 || ms->ms_ext_boot_sign == 0x29) + vol_serno = ms->ms_serno; + + blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos", + sizeof("msdos")); + + if (cluster_count < FAT12_MAX) + version = "FAT12"; + else if (cluster_count < FAT16_MAX) + version = "FAT16"; + + } else if (vs->vs_fat32_length) { + unsigned char *buf; + uint16_t fsinfo_sect; + int maxloop = 100; + + /* Search the FAT32 root dir for the label attribute */ + uint32_t buf_size = vs->vs_cluster_size * sector_size; + uint32_t start_data_sect = reserved + fat_size; + uint32_t entries = ((uint64_t) le32_to_cpu(vs->vs_fat32_length) + * sector_size) / sizeof(uint32_t); + uint32_t next = le32_to_cpu(vs->vs_root_cluster); + + while (next && next < entries && --maxloop) { + uint32_t next_sect_off; + uint64_t next_off, fat_entry_off; + int count; + + next_sect_off = (next - 2) * vs->vs_cluster_size; + next_off = (uint64_t)(start_data_sect + next_sect_off) * + sector_size; + + count = buf_size / sizeof(struct vfat_dir_entry); + + if (search_fat_label(pr, next_off, count, vol_label_buf)) { + vol_label = vol_label_buf; + break; + } + + /* get FAT entry */ + fat_entry_off = ((uint64_t) reserved * sector_size) + + (next * sizeof(uint32_t)); + buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size); + if (buf == NULL) + break; + + /* set next cluster */ + next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff; + } + + version = "FAT32"; + + if (vs->vs_ext_boot_sign == 0x29) + boot_label = vs->vs_label; + + vol_serno = vs->vs_serno; + + /* + * FAT32 should have a valid signature in the fsinfo block, + * but also allow all bytes set to '\0', because some volumes + * do not set the signature at all. + */ + fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector); + if (fsinfo_sect) { + struct fat32_fsinfo *fsinfo; + + buf = blkid_probe_get_buffer(pr, + (uint64_t) fsinfo_sect * sector_size, + sizeof(struct fat32_fsinfo)); + if (buf == NULL) + return errno ? -errno : 1; + + fsinfo = (struct fat32_fsinfo *) buf; + if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 && + memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0) + return 1; + if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 && + memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0) + return 1; + } + } + + if (boot_label && memcmp(boot_label, no_name, 11) != 0) + blkid_probe_set_id_label(pr, "LABEL_FATBOOT", boot_label, 11); + + if (vol_label) + blkid_probe_set_label(pr, vol_label, 11); + + /* We can't just print them as %04X, because they are unaligned */ + if (vol_serno) + blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X", + vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]); + if (version) + blkid_probe_set_version(pr, version); + + blkid_probe_set_fsblocksize(pr, vs->vs_cluster_size * sector_size); + blkid_probe_set_block_size(pr, sector_size); + blkid_probe_set_fssize(pr, (uint64_t) sector_size * sect_count); + + return 0; +} + + +const struct blkid_idinfo vfat_idinfo = +{ + .name = "vfat", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_vfat, + .magics = + { + { .magic = "MSWIN", .len = 5, .sboff = 0x52 }, + { .magic = "FAT32 ", .len = 8, .sboff = 0x52 }, + { .magic = "MSDOS", .len = 5, .sboff = 0x36 }, + { .magic = "FAT16 ", .len = 8, .sboff = 0x36 }, + { .magic = "FAT12 ", .len = 8, .sboff = 0x36 }, + { .magic = "FAT ", .len = 8, .sboff = 0x36 }, + { .magic = "\353", .len = 1, }, + { .magic = "\351", .len = 1, }, + { .magic = "\125\252", .len = 2, .sboff = 0x1fe }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/via_raid.c b/libblkid/src/superblocks/via_raid.c new file mode 100644 index 0000000..ee3ab65 --- /dev/null +++ b/libblkid/src/superblocks/via_raid.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * Inspired by libvolume_id by + * Kay Sievers <kay.sievers@vrfy.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" + +struct via_metadata { + uint16_t signature; + uint8_t version_number; + struct via_array { + uint16_t disk_bit_mask; + uint8_t disk_array_ex; + uint32_t capacity_low; + uint32_t capacity_high; + uint32_t serial_checksum; + } __attribute__((packed)) array; + uint32_t serial_checksum[8]; + uint8_t checksum; +} __attribute__((packed)); + +#define VIA_SIGNATURE 0xAA55 + +/* 8 bit checksum on first 50 bytes of metadata. */ +static uint8_t via_checksum(struct via_metadata *v) +{ + uint8_t i = 50, cs = 0; + + while (i--) + cs += ((uint8_t*) v)[i]; + + return cs; +} + +static int probe_viaraid(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + uint64_t off; + struct via_metadata *v; + + if (pr->size < 0x10000) + return 1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return 1; + + off = ((pr->size / 0x200)-1) * 0x200; + + v = (struct via_metadata *) + blkid_probe_get_buffer(pr, + off, + sizeof(struct via_metadata)); + if (!v) + return errno ? -errno : 1; + + if (le16_to_cpu(v->signature) != VIA_SIGNATURE) + return 1; + if (v->version_number > 2) + return 1; + if (!blkid_probe_verify_csum(pr, via_checksum(v), v->checksum)) + return 1; + + if (blkid_probe_sprintf_version(pr, "%u", v->version_number) != 0) + return 1; + if (blkid_probe_set_magic(pr, off, + sizeof(v->signature), + (unsigned char *) &v->signature)) + return 1; + return 0; +} + +const struct blkid_idinfo viaraid_idinfo = { + .name = "via_raid_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_viaraid, + .magics = BLKID_NONE_MAGIC +}; + + diff --git a/libblkid/src/superblocks/vmfs.c b/libblkid/src/superblocks/vmfs.c new file mode 100644 index 0000000..fac87ab --- /dev/null +++ b/libblkid/src/superblocks/vmfs.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009 Mike Hommey <mh@glandium.org> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include "superblocks.h" + +struct vmfs_fs_info { + uint32_t magic; + uint32_t volume_version; + uint8_t version; + uint8_t uuid[16]; + uint32_t mode; + char label[128]; +} __attribute__ ((__packed__)); + +struct vmfs_volume_info { + uint32_t magic; + uint32_t ver; + uint8_t irrelevant[122]; + uint8_t uuid[16]; +} __attribute__ ((__packed__)); + +static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vmfs_fs_info *header; + + header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info); + if (header == NULL) + return errno ? -errno : 1; + + blkid_probe_sprintf_uuid(pr, (unsigned char *) header->uuid, 16, + "%02x%02x%02x%02x-%02x%02x%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + header->uuid[3], header->uuid[2], header->uuid[1], + header->uuid[0], header->uuid[7], header->uuid[6], + header->uuid[5], header->uuid[4], header->uuid[9], + header->uuid[8], header->uuid[10], header->uuid[11], + header->uuid[12], header->uuid[13], header->uuid[14], + header->uuid[15]); + + blkid_probe_set_label(pr, (unsigned char *) header->label, + sizeof(header->label)); + blkid_probe_sprintf_version(pr, "%u", header->version); + return 0; +} + +static int probe_vmfs_volume(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vmfs_volume_info *header; + unsigned char *lvm_uuid; + + header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info); + if (header == NULL) + return errno ? -errno : 1; + + blkid_probe_sprintf_value(pr, "UUID_SUB", + "%02x%02x%02x%02x-%02x%02x%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + header->uuid[3], header->uuid[2], header->uuid[1], + header->uuid[0], header->uuid[7], header->uuid[6], + header->uuid[5], header->uuid[4], header->uuid[9], + header->uuid[8], header->uuid[10], header->uuid[11], + header->uuid[12], header->uuid[13], header->uuid[14], + header->uuid[15]); + blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(header->ver)); + + lvm_uuid = blkid_probe_get_buffer(pr, + 1024 * 1024 /* Start of the volume info */ + + 512 /* Offset to lvm info */ + + 20 /* Offset in lvm info */, 35); + if (lvm_uuid) + blkid_probe_strncpy_uuid(pr, lvm_uuid, 35); + + return 0; +} + +const struct blkid_idinfo vmfs_fs_idinfo = +{ + .name = "VMFS", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_vmfs_fs, + .magics = + { + { .magic = "\x5e\xf1\xab\x2f", .len = 4, .kboff = 2048 }, + { NULL } + } +}; + +const struct blkid_idinfo vmfs_volume_idinfo = +{ + .name = "VMFS_volume_member", + .usage = BLKID_USAGE_RAID, + .probefunc = probe_vmfs_volume, + .magics = + { + { .magic = "\x0d\xd0\x01\xc0", .len = 4, .kboff = 1024 }, + { NULL } + } +}; diff --git a/libblkid/src/superblocks/vxfs.c b/libblkid/src/superblocks/vxfs.c new file mode 100644 index 0000000..be66831 --- /dev/null +++ b/libblkid/src/superblocks/vxfs.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> + +#include "superblocks.h" + +struct vxfs_super_block { + uint32_t vs_magic; + int32_t vs_version; + uint32_t vs_ctime; + uint32_t vs_cutime; + uint32_t __unused1; + uint32_t __unused2; + uint32_t vs_old_logstart; + uint32_t vs_old_logend; + uint32_t vs_bsize; + uint32_t vs_size; + uint32_t vs_dsize; +}; + +static int probe_vxfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct vxfs_super_block *vxs; + + vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block); + if (!vxs) + return errno ? -errno : 1; + + if (le32_to_cpu(vxs->vs_magic) == 0xa501fcf5) { + blkid_probe_sprintf_version(pr, "%u", (unsigned int)le32_to_cpu(vxs->vs_version)); + blkid_probe_set_fsblocksize(pr, le32_to_cpu(vxs->vs_bsize)); + blkid_probe_set_block_size(pr, le32_to_cpu(vxs->vs_bsize)); + blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_LITTLE); + } else if (be32_to_cpu(vxs->vs_magic) == 0xa501fcf5) { + blkid_probe_sprintf_version(pr, "%u", (unsigned int)be32_to_cpu(vxs->vs_version)); + blkid_probe_set_fsblocksize(pr, be32_to_cpu(vxs->vs_bsize)); + blkid_probe_set_block_size(pr, be32_to_cpu(vxs->vs_bsize)); + blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_BIG); + } + return 0; +} + + +const struct blkid_idinfo vxfs_idinfo = +{ + .name = "vxfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_vxfs, + .magics = + { + { .magic = "\365\374\001\245", .len = 4, .kboff = 1 }, + { .magic = "\245\001\374\365", .len = 4, .kboff = 8 }, + { NULL } + } +}; + diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c new file mode 100644 index 0000000..f0e099e --- /dev/null +++ b/libblkid/src/superblocks/xfs.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 1999 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2001 by Andreas Dilger + * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * Copyright (C) 2013 Eric Sandeen <sandeen@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#include "superblocks.h" +#include "crc32c.h" + +struct xfs_super_block { + uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + uint64_t sb_dblocks; /* number of data blocks */ + uint64_t sb_rblocks; /* number of realtime blocks */ + uint64_t sb_rextents; /* number of realtime extents */ + unsigned char sb_uuid[16]; /* file system unique id */ + uint64_t sb_logstart; /* starting block of log if internal */ + uint64_t sb_rootino; /* root inode number */ + uint64_t sb_rbmino; /* bitmap inode for realtime extents */ + uint64_t sb_rsumino; /* summary inode for rt bitmap */ + uint32_t sb_rextsize; /* realtime extent size, blocks */ + uint32_t sb_agblocks; /* size of an allocation group */ + uint32_t sb_agcount; /* number of allocation groups */ + uint32_t sb_rbmblocks; /* number of rt bitmap blocks */ + uint32_t sb_logblocks; /* number of log blocks */ + + uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + uint64_t sb_uquotino; /* inode for user quotas */ + uint64_t sb_gquotino; /* inode for group or project quotas */ + uint16_t sb_qflags; /* quota flags */ + uint8_t sb_flags; /* misc flags */ + uint8_t sb_shared_vn; /* reserved, zeroed */ + uint32_t sb_inoalignmt; /* inode alignment */ + uint32_t sb_unit; /* stripe or raid unit */ + uint32_t sb_width; /* stripe or raid width */ + uint8_t sb_dirblklog; /* directory block allocation granularity */ + uint8_t sb_logsectlog; /* log sector sector size */ + uint16_t sb_logsectsize; /* log sector size */ + uint32_t sb_logsunit; /* log device stripe or raid unit */ + uint32_t sb_features2; /* additional version flags */ + uint32_t sb_bad_features2; /* mirror of sb_features2 */ + + /* version 5 fields */ + uint32_t sb_features_compat; /* rw compatible flags */ + uint32_t sb_features_ro_compat; /* ro compatible flags */ + uint32_t sb_features_incompat; /* rw incompatible flags */ + uint32_t sb_features_log_incompat; /* rw log incompatible flags */ + uint32_t sb_crc; /* superblock checksum */ + uint32_t sb_spino_align; /* sparse inode alignment */ + uint64_t sb_pquotino; /* project quote inode */ + uint64_t sb_lsn; /* superblock update sequence number */ + unsigned char sb_meta_uuid[16]; /* superblock meta uuid */ + uint64_t sb_rrmapino; /* realtime reversemapping inode */ +} __attribute__((packed)); + +#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ +#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ +#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG) +#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG) +#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ +#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ +#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG) +#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG) + +#define XFS_DINODE_MIN_LOG 8 +#define XFS_DINODE_MAX_LOG 11 +#define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG) +#define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG) + +#define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */ +#define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */ +#define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */ + +#define XFS_MIN_AG_BLOCKS 64 +#define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks) +#define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \ + (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) + +#define XFS_SB_VERSION_MOREBITSBIT 0x8000 +#define XFS_SB_VERSION2_CRCBIT 0x00000100 + + +static void sb_from_disk(struct xfs_super_block *from, + struct xfs_super_block *to) +{ + + to->sb_magicnum = be32_to_cpu(from->sb_magicnum); + to->sb_blocksize = be32_to_cpu(from->sb_blocksize); + to->sb_dblocks = be64_to_cpu(from->sb_dblocks); + to->sb_rblocks = be64_to_cpu(from->sb_rblocks); + to->sb_rextents = be64_to_cpu(from->sb_rextents); + to->sb_logstart = be64_to_cpu(from->sb_logstart); + to->sb_rootino = be64_to_cpu(from->sb_rootino); + to->sb_rbmino = be64_to_cpu(from->sb_rbmino); + to->sb_rsumino = be64_to_cpu(from->sb_rsumino); + to->sb_rextsize = be32_to_cpu(from->sb_rextsize); + to->sb_agblocks = be32_to_cpu(from->sb_agblocks); + to->sb_agcount = be32_to_cpu(from->sb_agcount); + to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks); + to->sb_logblocks = be32_to_cpu(from->sb_logblocks); + to->sb_versionnum = be16_to_cpu(from->sb_versionnum); + to->sb_sectsize = be16_to_cpu(from->sb_sectsize); + to->sb_inodesize = be16_to_cpu(from->sb_inodesize); + to->sb_inopblock = be16_to_cpu(from->sb_inopblock); + to->sb_blocklog = from->sb_blocklog; + to->sb_sectlog = from->sb_sectlog; + to->sb_inodelog = from->sb_inodelog; + to->sb_inopblog = from->sb_inopblog; + to->sb_agblklog = from->sb_agblklog; + to->sb_rextslog = from->sb_rextslog; + to->sb_inprogress = from->sb_inprogress; + to->sb_imax_pct = from->sb_imax_pct; + to->sb_icount = be64_to_cpu(from->sb_icount); + to->sb_ifree = be64_to_cpu(from->sb_ifree); + to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks); + to->sb_frextents = be64_to_cpu(from->sb_frextents); + to->sb_uquotino = be64_to_cpu(from->sb_uquotino); + to->sb_gquotino = be64_to_cpu(from->sb_gquotino); + to->sb_qflags = be16_to_cpu(from->sb_qflags); + to->sb_flags = from-> sb_flags; + to->sb_shared_vn = from-> sb_shared_vn; + to->sb_inoalignmt = be32_to_cpu(from->sb_inoalignmt); + to->sb_unit = be32_to_cpu(from->sb_unit); + to->sb_width = be32_to_cpu(from->sb_width); + to->sb_dirblklog = from-> sb_dirblklog; + to->sb_logsectlog = from-> sb_logsectlog; + to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize); + to->sb_logsunit = be32_to_cpu(from->sb_logsunit); + to->sb_features2 = be32_to_cpu(from->sb_features2); + to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2); + to->sb_features_compat = be32_to_cpu(from->sb_features_compat); + to->sb_features_ro_compat = be32_to_cpu(from->sb_features_ro_compat); + to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat); + to->sb_features_log_incompat = be32_to_cpu(from->sb_features_log_incompat); + to->sb_crc = be32_to_cpu(from->sb_crc); + to->sb_spino_align = be32_to_cpu(from->sb_spino_align); + to->sb_pquotino = be64_to_cpu(from->sb_pquotino); + to->sb_lsn = be64_to_cpu(from->sb_lsn); + to->sb_rrmapino = be64_to_cpu(from->sb_rrmapino); +} + +static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr, + const struct blkid_idmag *mag) +{ + struct xfs_super_block sb, *sbp = &sb; + + /* beXX_to_cpu(), but don't convert UUID and fsname! */ + sb_from_disk(ondisk, sbp); + + /* sanity checks, we don't want to rely on magic string only */ + if (sbp->sb_agcount <= 0 || + sbp->sb_sectsize < XFS_MIN_SECTORSIZE || + sbp->sb_sectsize > XFS_MAX_SECTORSIZE || + sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG || + sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG || + sbp->sb_sectsize != (1 << sbp->sb_sectlog) || + sbp->sb_blocksize < XFS_MIN_BLOCKSIZE || + sbp->sb_blocksize > XFS_MAX_BLOCKSIZE || + sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG || + sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG || + sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) || + sbp->sb_inodesize < XFS_DINODE_MIN_SIZE || + sbp->sb_inodesize > XFS_DINODE_MAX_SIZE || + sbp->sb_inodelog < XFS_DINODE_MIN_LOG || + sbp->sb_inodelog > XFS_DINODE_MAX_LOG || + sbp->sb_inodesize != (1 << sbp->sb_inodelog) || + (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) || + (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) || + (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) || + (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) || + sbp->sb_dblocks == 0 || + sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp) || + sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp)) + return 0; + + if ((sbp->sb_versionnum & 0x0f) == 5) { + uint32_t expected, crc; + unsigned char *csummed; + + if (!(sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT)) + return 0; + if (!(sbp->sb_features2 & XFS_SB_VERSION2_CRCBIT)) + return 0; + + expected = sbp->sb_crc; + csummed = blkid_probe_get_sb_buffer(pr, mag, sbp->sb_sectsize); + if (!csummed) + return 0; + + crc = ul_crc32c_exclude_offset(~0LL, csummed, sbp->sb_sectsize, + offsetof(struct xfs_super_block, sb_crc), + sizeof_member(struct xfs_super_block, sb_crc)); + crc = bswap_32(crc ^ ~0LL); + + if (!blkid_probe_verify_csum(pr, crc, expected)) + return 0; + } + + return 1; +} + +static uint64_t xfs_fssize(struct xfs_super_block *xs) +{ + uint32_t lsize = xs->sb_logstart ? xs->sb_logblocks : 0; + uint64_t avail_blocks = be64_to_cpu(xs->sb_dblocks) - be32_to_cpu(lsize); + uint64_t fssize = avail_blocks*be32_to_cpu(xs->sb_blocksize); + + return fssize; +} + +static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct xfs_super_block *xs; + + xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block); + if (!xs) + return errno ? -errno : 1; + + if (!xfs_verify_sb(xs, pr, mag)) + return 1; + + if (*xs->sb_fname != '\0') + blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname, + sizeof(xs->sb_fname)); + blkid_probe_set_uuid(pr, xs->sb_uuid); + blkid_probe_set_fssize(pr, xfs_fssize(xs)); + blkid_probe_set_fslastblock(pr, be64_to_cpu(xs->sb_dblocks)); + blkid_probe_set_fsblocksize(pr, be32_to_cpu(xs->sb_blocksize)); + blkid_probe_set_block_size(pr, be16_to_cpu(xs->sb_sectsize)); + return 0; +} + +const struct blkid_idinfo xfs_idinfo = +{ + .name = "xfs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_xfs, + .magics = + { + { .magic = "XFSB", .len = 4 }, + { NULL } + } +}; + +struct xlog_rec_header { + uint32_t h_magicno; + uint32_t h_dummy1[1]; + uint32_t h_version; + uint32_t h_len; + uint32_t h_dummy2[71]; + uint32_t h_fmt; + unsigned char h_uuid[16]; +} __attribute__((packed)); + +#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe + +/* + * For very small filesystems, the minimum log size + * can be smaller, but that seems vanishingly unlikely + * when used with an external log (which is used for + * performance reasons; tiny conflicts with that goal). + */ +#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024) + +#define XLOG_FMT_LINUX_LE 1 +#define XLOG_FMT_LINUX_BE 2 +#define XLOG_FMT_IRIX_BE 3 + +#define XLOG_VERSION_1 1 +#define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */ +#define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2) + +static int xlog_valid_rec_header(struct xlog_rec_header *rhead) +{ + uint32_t hlen; + + if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) + return 0; + + if (!rhead->h_version || + (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS))) + return 0; + + /* LR body must have data or it wouldn't have been written */ + hlen = be32_to_cpu(rhead->h_len); + if (hlen <= 0 || hlen > INT_MAX) + return 0; + + if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) && + rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) && + rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE)) + return 0; + + return 1; +} + +/* xlog record header will be in some sector in the first 256k */ +static int probe_xfs_log(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int i; + struct xlog_rec_header *rhead; + unsigned char *buf; + + buf = blkid_probe_get_buffer(pr, 0, 256*1024); + if (!buf) + return errno ? -errno : 1; + + /* check the first 512 512-byte sectors */ + for (i = 0; i < 512; i++) { + /* this is regular XFS (maybe with some sectors shift), ignore */ + if (memcmp(&buf[i*512], "XFSB", 4) == 0) + return 1; + + rhead = (struct xlog_rec_header *)&buf[i*512]; + + if (xlog_valid_rec_header(rhead)) { + blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID"); + + if (blkid_probe_set_magic(pr, i * 512, + sizeof(rhead->h_magicno), + (unsigned char *) &rhead->h_magicno)) + return 1; + + return 0; + } + } + + return 1; +} + +const struct blkid_idinfo xfs_log_idinfo = +{ + .name = "xfs_external_log", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_xfs_log, + .magics = BLKID_NONE_MAGIC, + .minsz = XFS_MIN_LOG_BYTES, +}; diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c new file mode 100644 index 0000000..1fcc384 --- /dev/null +++ b/libblkid/src/superblocks/zfs.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <inttypes.h> +#include <limits.h> + +#include "superblocks.h" + +#define VDEV_LABEL_UBERBLOCK (128 * 1024ULL) +#define VDEV_LABEL_NVPAIR ( 16 * 1024ULL) +#define VDEV_LABEL_SIZE (256 * 1024ULL) +#define UBERBLOCK_SIZE 1024ULL +#define UBERBLOCKS_COUNT 128 + +/* #include <sys/uberblock_impl.h> */ +#define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */ +struct zfs_uberblock { + uint64_t ub_magic; /* UBERBLOCK_MAGIC */ + uint64_t ub_version; /* SPA_VERSION */ + uint64_t ub_txg; /* txg of last sync */ + uint64_t ub_guid_sum; /* sum of all vdev guids */ + uint64_t ub_timestamp; /* UTC time of last sync */ + char ub_rootbp; /* MOS objset_phys_t */ +} __attribute__((packed)); + +#define ZFS_WANT 4 + +#define DATA_TYPE_UINT64 8 +#define DATA_TYPE_STRING 9 +#define DATA_TYPE_DIRECTORY 19 + +struct nvpair { + uint32_t nvp_size; + uint32_t nvp_unkown; + uint32_t nvp_namelen; + char nvp_name[0]; /* aligned to 4 bytes */ + /* aligned ptr array for string arrays */ + /* aligned array of data for value */ +}; + +struct nvstring { + uint32_t nvs_type; + uint32_t nvs_elem; + uint32_t nvs_strlen; + unsigned char nvs_string[0]; +}; + +struct nvuint64 { + uint32_t nvu_type; + uint32_t nvu_elem; + uint64_t nvu_value; +} __attribute__((packed)); + +struct nvdirectory { + uint32_t nvd_type; + uint32_t nvd_unknown[3]; +}; + +struct nvlist { + uint32_t nvl_unknown[3]; + struct nvpair nvl_nvpair; +}; + +static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, + void *value, size_t max_value_size, unsigned directory_level) +{ + if (strncmp(name, "name", namelen) == 0 && + sizeof(struct nvstring) <= max_value_size && + !directory_level) { + struct nvstring *nvs = value; + uint32_t nvs_type = be32_to_cpu(nvs->nvs_type); + uint32_t nvs_strlen = be32_to_cpu(nvs->nvs_strlen); + + if (nvs_type != DATA_TYPE_STRING || + (uint64_t)nvs_strlen + sizeof(*nvs) > max_value_size) + return; + + DBG(LOWPROBE, ul_debug("nvstring: type %u string %*s", + nvs_type, nvs_strlen, nvs->nvs_string)); + + blkid_probe_set_label(pr, nvs->nvs_string, nvs_strlen); + } else if (strncmp(name, "guid", namelen) == 0 && + sizeof(struct nvuint64) <= max_value_size && + !directory_level) { + struct nvuint64 *nvu = value; + uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); + uint64_t nvu_value; + + memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value)); + nvu_value = be64_to_cpu(nvu_value); + + if (nvu_type != DATA_TYPE_UINT64) + return; + + DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64, + nvu_type, nvu_value)); + + blkid_probe_sprintf_value(pr, "UUID_SUB", + "%"PRIu64, nvu_value); + } else if (strncmp(name, "pool_guid", namelen) == 0 && + sizeof(struct nvuint64) <= max_value_size && + !directory_level) { + struct nvuint64 *nvu = value; + uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); + uint64_t nvu_value; + + memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value)); + nvu_value = be64_to_cpu(nvu_value); + + if (nvu_type != DATA_TYPE_UINT64) + return; + + DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64, + nvu_type, nvu_value)); + + blkid_probe_sprintf_uuid(pr, (unsigned char *) &nvu_value, + sizeof(nvu_value), + "%"PRIu64, nvu_value); + } else if (strncmp(name, "ashift", namelen) == 0 && + sizeof(struct nvuint64) <= max_value_size) { + struct nvuint64 *nvu = value; + uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); + uint64_t nvu_value; + + memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value)); + nvu_value = be64_to_cpu(nvu_value); + + if (nvu_type != DATA_TYPE_UINT64) + return; + + if (nvu_value < 32){ + blkid_probe_set_fsblocksize(pr, 1U << nvu_value); + blkid_probe_set_block_size(pr, 1U << nvu_value); + } + } +} + +static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) +{ + unsigned char *p; + struct nvlist *nvl; + struct nvpair *nvp; + size_t left = 4096; + unsigned directory_level = 0; + + offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR; + + /* Note that we currently assume that the desired fields are within + * the first 4k (left) of the nvlist. This is true for all pools + * I've seen, and simplifies this code somewhat, because we don't + * have to handle an nvpair crossing a buffer boundary. */ + p = blkid_probe_get_buffer(pr, offset, left); + if (!p) + return; + + DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd", + (intmax_t)offset)); + + nvl = (struct nvlist *) p; + nvp = &nvl->nvl_nvpair; + left -= (unsigned char *)nvp - p; /* Already used up 12 bytes */ + + while (left > sizeof(*nvp)) { + uint32_t nvp_size = be32_to_cpu(nvp->nvp_size); + uint32_t nvp_namelen = be32_to_cpu(nvp->nvp_namelen); + uint64_t namesize = ((uint64_t)nvp_namelen + 3) & ~3; + size_t max_value_size; + void *value; + + if (!nvp->nvp_size) { + if (!directory_level) + break; + directory_level--; + nvp_size = 8; + goto cont; + } + + DBG(LOWPROBE, ul_debug("left %zd nvp_size %u", + left, nvp_size)); + + /* nvpair fits in buffer and name fits in nvpair? */ + if (nvp_size > left || namesize + sizeof(*nvp) > nvp_size) + break; + + DBG(LOWPROBE, + ul_debug("nvlist: size %u, namelen %u, name %*s", + nvp_size, nvp_namelen, nvp_namelen, + nvp->nvp_name)); + + max_value_size = nvp_size - (namesize + sizeof(*nvp)); + value = nvp->nvp_name + namesize; + + if (sizeof(struct nvdirectory) <= max_value_size) { + struct nvdirectory *nvu = value; + if (be32_to_cpu(nvu->nvd_type) == DATA_TYPE_DIRECTORY) { + nvp_size = sizeof(*nvp) + namesize + sizeof(*nvu); + directory_level++; + goto cont; + } + } + + zfs_process_value(pr, nvp->nvp_name, nvp_namelen, + value, max_value_size, directory_level); + +cont: + if (nvp_size > left) + break; + left -= nvp_size; + + nvp = (struct nvpair *)((char *)nvp + nvp_size); + } +} + +static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian) +{ + uint64_t swab_magic = swab64((uint64_t)UBERBLOCK_MAGIC); + const struct zfs_uberblock *ub; + int i, found = 0; + loff_t offset = VDEV_LABEL_UBERBLOCK; + + for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) { + ub = (const struct zfs_uberblock *)((const char *) label + offset); + + if (ub->ub_magic == UBERBLOCK_MAGIC) { + *ub_offset = offset; + *swap_endian = 0; + found++; + DBG(LOWPROBE, ul_debug("probe_zfs: found little-endian uberblock at %jd", (intmax_t)offset >> 10)); + } + + if (ub->ub_magic == swab_magic) { + *ub_offset = offset; + *swap_endian = 1; + found++; + DBG(LOWPROBE, ul_debug("probe_zfs: found big-endian uberblock at %jd", (intmax_t)offset >> 10)); + } + } + + return found; +} + +/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start + * of the disk, and 2 areas at the end of the disk. Check only some of them... + * #4 (@ 132kB) is the first one written on a new filesystem. */ +static int probe_zfs(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int swab_endian = 0; + struct zfs_uberblock *ub = NULL; + loff_t offset = 0, ub_offset = 0; + int label_no, found = 0, found_in_label; + void *label; + loff_t blk_align = (pr->size % (256 * 1024ULL)); + + DBG(PROBE, ul_debug("probe_zfs")); + /* Look for at least 4 uberblocks to ensure a positive match */ + for (label_no = 0; label_no < 4; label_no++) { + switch(label_no) { + case 0: // jump to L0 + offset = 0; + break; + case 1: // jump to L1 + offset = VDEV_LABEL_SIZE; + break; + case 2: // jump to L2 + offset = pr->size - 2 * VDEV_LABEL_SIZE - blk_align; + break; + case 3: // jump to L3 + offset = pr->size - VDEV_LABEL_SIZE - blk_align; + break; + } + + if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && + blkid_probe_is_covered_by_pt(pr, offset, VDEV_LABEL_SIZE)) + /* ignore this area, it's within any partition and + * we are working with whole-disk now */ + continue; + + label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE); + if (label == NULL) + return errno ? -errno : 1; + + found_in_label = find_uberblocks(label, &ub_offset, &swab_endian); + + if (found_in_label > 0) { + found+= found_in_label; + ub = (struct zfs_uberblock *)((char *) label + ub_offset); + ub_offset += offset; + + if (found >= ZFS_WANT) + break; + } + } + + if (found < ZFS_WANT) + return 1; + + /* If we found the 4th uberblock, then we will have exited from the + * scanning loop immediately, and ub will be a valid uberblock. */ + blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ? + swab64(ub->ub_version) : ub->ub_version); + + zfs_extract_guid_name(pr, offset); + + if (blkid_probe_set_magic(pr, ub_offset, + sizeof(ub->ub_magic), + (unsigned char *) &ub->ub_magic)) + return 1; + + blkid_probe_set_fsendianness(pr, !swab_endian ? + BLKID_ENDIANNESS_NATIVE : BLKID_ENDIANNESS_OTHER); + + return 0; +} + +const struct blkid_idinfo zfs_idinfo = +{ + .name = "zfs_member", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_zfs, + .minsz = 64 * 1024 * 1024, + .magics = BLKID_NONE_MAGIC +}; diff --git a/libblkid/src/superblocks/zonefs.c b/libblkid/src/superblocks/zonefs.c new file mode 100644 index 0000000..8aa45b0 --- /dev/null +++ b/libblkid/src/superblocks/zonefs.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 Western Digital Corporation or its affiliates. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License + */ +#include <stddef.h> +#include <string.h> + +#include "superblocks.h" +#include "crc32.h" + +#define ZONEFS_MAGIC "SFOZ" /* 0x5a4f4653 'Z' 'O' 'F' 'S' */ +#define ZONEFS_MAGIC_SIZE 4 +#define ZONEFS_MAGIC_OFST 0 +#define ZONEFS_UUID_SIZE 16 +#define ZONEFS_LABEL_SIZE 32 +#define ZONEFS_SB_OFST 0 + +#define ZONEFS_BLOCK_SIZE 4096U + +/* All in little-endian */ +struct zonefs_super { + + /* Magic number */ + int32_t s_magic; + + /* Checksum */ + int32_t s_crc; + + /* Volume label */ + char s_label[ZONEFS_LABEL_SIZE]; + + /* 128-bit uuid */ + uint8_t s_uuid[ZONEFS_UUID_SIZE]; + + /* Features */ + int64_t s_features; + + /* UID/GID to use for files */ + int32_t s_uid; + int32_t s_gid; + + /* File permissions */ + int32_t s_perm; + + /* Padding to 4096 bytes */ + uint8_t s_reserved[4020]; + +} __attribute__ ((packed)); + +static int zonefs_verify_csum(blkid_probe pr, const struct zonefs_super *sb) +{ + uint32_t expected = le32_to_cpu(sb->s_crc); + uint32_t crc = ul_crc32_exclude_offset( + ~0LL, (unsigned char *) sb, sizeof(*sb), + offsetof(typeof(*sb), s_crc), sizeof(sb->s_crc)); + return blkid_probe_verify_csum(pr, crc, expected); +} + +static int probe_zonefs(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const struct zonefs_super *sb; + + sb = (struct zonefs_super *) + blkid_probe_get_buffer(pr, ZONEFS_SB_OFST, + sizeof(struct zonefs_super)); + if (!sb) + return errno ? -errno : 1; + + if (!zonefs_verify_csum(pr, sb)) + return 1; + + if (sb->s_label[0]) + blkid_probe_set_label(pr, (unsigned char *) sb->s_label, + sizeof(sb->s_label)); + + blkid_probe_set_uuid(pr, sb->s_uuid); + blkid_probe_set_fsblocksize(pr, ZONEFS_BLOCK_SIZE); + blkid_probe_set_block_size(pr, ZONEFS_BLOCK_SIZE); + + return 0; +} + +const struct blkid_idinfo zonefs_idinfo = +{ + .name = "zonefs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_zonefs, + .magics = + { + { + .magic = (char *)ZONEFS_MAGIC, + .len = ZONEFS_MAGIC_SIZE, + .kboff = ZONEFS_SB_OFST, + .sboff = ZONEFS_MAGIC_OFST, + }, + { NULL } + } +}; diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c new file mode 100644 index 0000000..1783365 --- /dev/null +++ b/libblkid/src/tag.c @@ -0,0 +1,468 @@ +/* + * tag.c - allocation/initialization/free routines for tag structs + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "blkidP.h" + +static blkid_tag blkid_new_tag(void) +{ + blkid_tag tag; + + if (!(tag = calloc(1, sizeof(struct blkid_struct_tag)))) + return NULL; + + DBG(TAG, ul_debugobj(tag, "alloc")); + INIT_LIST_HEAD(&tag->bit_tags); + INIT_LIST_HEAD(&tag->bit_names); + + return tag; +} + +void blkid_free_tag(blkid_tag tag) +{ + if (!tag) + return; + + DBG(TAG, ul_debugobj(tag, "freeing tag %s (%s)", tag->bit_name, tag->bit_val)); + + list_del(&tag->bit_tags); /* list of tags for this device */ + list_del(&tag->bit_names); /* list of tags with this type */ + + free(tag->bit_name); + free(tag->bit_val); + + free(tag); +} + +/* + * Find the desired tag on a device. If value is NULL, then the + * first such tag is returned, otherwise return only exact tag if found. + */ +blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) +{ + struct list_head *p; + + list_for_each(p, &dev->bid_tags) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_tags); + + if (!strcmp(tmp->bit_name, type)) + return tmp; + } + return NULL; +} + +int blkid_dev_has_tag(blkid_dev dev, const char *type, + const char *value) +{ + blkid_tag tag; + + tag = blkid_find_tag_dev(dev, type); + if (!value) + return (tag != NULL); + if (!tag || strcmp(tag->bit_val, value) != 0) + return 0; + return 1; +} + +/* + * Find the desired tag type in the cache. + * We return the head tag for this tag type. + */ +static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) +{ + blkid_tag head = NULL, tmp; + struct list_head *p; + + if (!cache || !type) + return NULL; + + list_for_each(p, &cache->bic_tags) { + tmp = list_entry(p, struct blkid_struct_tag, bit_tags); + if (!strcmp(tmp->bit_name, type)) { + DBG(TAG, ul_debug("found cache tag head %s", type)); + head = tmp; + break; + } + } + return head; +} + +/* + * Set a tag on an existing device. + * + * If value is NULL, then delete the tags from the device. + */ +int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength) +{ + blkid_tag t = NULL, head = NULL; + char *val = NULL; + char **dev_var = NULL; + + if (value && !(val = strndup(value, vlength))) + return -BLKID_ERR_MEM; + + /* + * Certain common tags are linked directly to the device struct + * We need to know what they are before we do anything else because + * the function name parameter might get freed later on. + */ + if (!strcmp(name, "TYPE")) + dev_var = &dev->bid_type; + else if (!strcmp(name, "LABEL")) + dev_var = &dev->bid_label; + else if (!strcmp(name, "UUID")) + dev_var = &dev->bid_uuid; + + t = blkid_find_tag_dev(dev, name); + if (!value) { + if (t) + blkid_free_tag(t); + } else if (t) { + if (!strcmp(t->bit_val, val)) { + /* Same thing, exit */ + free(val); + return 0; + } + DBG(TAG, ul_debugobj(t, "update (%s) '%s' -> '%s'", t->bit_name, t->bit_val, val)); + free(t->bit_val); + t->bit_val = val; + } else { + /* Existing tag not present, add to device */ + if (!(t = blkid_new_tag())) + goto errout; + t->bit_name = strdup(name); + t->bit_val = val; + t->bit_dev = dev; + + DBG(TAG, ul_debugobj(t, "setting (%s) '%s'", t->bit_name, t->bit_val)); + list_add_tail(&t->bit_tags, &dev->bid_tags); + + if (dev->bid_cache) { + head = blkid_find_head_cache(dev->bid_cache, + t->bit_name); + if (!head) { + head = blkid_new_tag(); + if (!head) + goto errout; + + DBG(TAG, ul_debugobj(head, "creating new cache tag head %s", name)); + head->bit_name = strdup(name); + if (!head->bit_name) + goto errout; + list_add_tail(&head->bit_tags, + &dev->bid_cache->bic_tags); + } + list_add_tail(&t->bit_names, &head->bit_names); + } + } + + /* Link common tags directly to the device struct */ + if (dev_var) + *dev_var = val; + + if (dev->bid_cache) + dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; + return 0; + +errout: + if (t) + blkid_free_tag(t); + else + free(val); + if (head) + blkid_free_tag(head); + return -BLKID_ERR_MEM; +} + + +/* + * Parse a "NAME=value" string. This is slightly different than + * parse_token, because that will end an unquoted value at a space, while + * this will assume that an unquoted value is the rest of the token (e.g. + * if we are passed an already quoted string from the command-line we don't + * have to both quote and escape quote so that the quotes make it to + * us). + * + * Returns 0 on success, and -1 on failure. + */ +int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) +{ + char *name, *value, *cp; + + DBG(TAG, ul_debug("trying to parse '%s' as a tag", token)); + + if (!token || !(cp = strchr(token, '='))) + return -1; + + name = strdup(token); + if (!name) + return -1; + value = name + (cp - token); + *value++ = '\0'; + if (*value == '"' || *value == '\'') { + char c = *value++; + if (!(cp = strrchr(value, c))) + goto errout; /* missing closing quote */ + *cp = '\0'; + } + + if (ret_val) { + value = *value ? strdup(value) : NULL; + if (!value) + goto errout; + *ret_val = value; + } + + if (ret_type) + *ret_type = name; + else + free(name); + + return 0; + +errout: + DBG(TAG, ul_debug("parse error: '%s'", token)); + free(name); + return -1; +} + +/* + * Tag iteration routines for the public libblkid interface. + * + * These routines do not expose the list.h implementation, which are a + * contamination of the namespace, and which force us to reveal far, far + * too much of our internal implementation. I'm not convinced I want + * to keep list.h in the long term, anyway. It's fine for kernel + * programming, but performance is not the #1 priority for this + * library, and I really don't like the trade-off of type-safety for + * performance for this application. [tytso:20030125.2007EST] + */ + +/* + * This series of functions iterate over all tags in a device + */ +#define TAG_ITERATE_MAGIC 0x01a5284c + +struct blkid_struct_tag_iterate { + int magic; + blkid_dev dev; + struct list_head *p; +}; + +blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) +{ + blkid_tag_iterate iter; + + if (!dev) { + errno = EINVAL; + return NULL; + } + + iter = malloc(sizeof(struct blkid_struct_tag_iterate)); + if (iter) { + iter->magic = TAG_ITERATE_MAGIC; + iter->dev = dev; + iter->p = dev->bid_tags.next; + } + return (iter); +} + +/* + * Return 0 on success, -1 on error + */ +int blkid_tag_next(blkid_tag_iterate iter, + const char **type, const char **value) +{ + blkid_tag tag; + + if (!type || !value || + !iter || iter->magic != TAG_ITERATE_MAGIC || + iter->p == &iter->dev->bid_tags) + return -1; + + *type = NULL; + *value = NULL; + tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); + *type = tag->bit_name; + *value = tag->bit_val; + iter->p = iter->p->next; + return 0; +} + +void blkid_tag_iterate_end(blkid_tag_iterate iter) +{ + if (!iter || iter->magic != TAG_ITERATE_MAGIC) + return; + iter->magic = 0; + free(iter); +} + +/* + * This function returns a device which matches a particular + * type/value pair. If there is more than one device that matches the + * search specification, it returns the one with the highest priority + * value. This allows us to give preference to EVMS or LVM devices. + */ +blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value) +{ + blkid_tag head; + blkid_dev dev; + int pri; + struct list_head *p; + int probe_new = 0, probe_all = 0; + + if (!cache || !type || !value) + return NULL; + + blkid_read_cache(cache); + + DBG(TAG, ul_debug("looking for tag %s=%s in cache", type, value)); + +try_again: + pri = -1; + dev = NULL; + head = blkid_find_head_cache(cache, type); + + if (head) { + list_for_each(p, &head->bit_names) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_names); + + if (!strcmp(tmp->bit_val, value) && + (tmp->bit_dev->bid_pri > pri) && + !access(tmp->bit_dev->bid_name, F_OK)) { + dev = tmp->bit_dev; + pri = dev->bid_pri; + } + } + } + if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { + dev = blkid_verify(cache, dev); + if (!dev || dev->bid_flags & BLKID_BID_FL_VERIFIED) + goto try_again; + } + + if (!dev && !probe_new) { + if (blkid_probe_all_new(cache) < 0) + return NULL; + probe_new++; + goto try_again; + } + + if (!dev && !probe_all + && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { + if (blkid_probe_all(cache) < 0) + return NULL; + probe_all++; + goto try_again; + } + return dev; +} + +#ifdef TEST_PROGRAM +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif + +static void __attribute__((__noreturn__)) usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " + "[type value]\n", + prog); + fprintf(stderr, "\tList all tags for a device and exit\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + blkid_tag_iterate iter; + blkid_cache cache = NULL; + blkid_dev dev; + int c, ret, found; + int flags = BLKID_DEV_FIND; + char *tmp; + char *file = NULL; + char *devname = NULL; + char *search_type = NULL; + char *search_value = NULL; + const char *type, *value; + + while ((c = getopt (argc, argv, "m:f:")) != EOF) + switch (c) { + case 'f': + file = optarg; + break; + case 'm': + { + int mask = strtoul (optarg, &tmp, 0); + if (*tmp) { + fprintf(stderr, "Invalid debug mask: %s\n", + optarg); + exit(1); + } + blkid_init_debug(mask); + break; + } + case '?': + usage(argv[0]); + } + if (argc > optind) + devname = argv[optind++]; + if (argc > optind) + search_type = argv[optind++]; + if (argc > optind) + search_value = argv[optind++]; + if (!devname || (argc != optind)) + usage(argv[0]); + + if ((ret = blkid_get_cache(&cache, file)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + + dev = blkid_get_dev(cache, devname, flags); + if (!dev) { + fprintf(stderr, "%s: cannot find device in blkid cache\n", + devname); + exit(1); + } + if (search_type) { + found = blkid_dev_has_tag(dev, search_type, search_value); + printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), + search_type, search_value ? search_value : "NULL", + found ? "FOUND" : "NOT FOUND"); + return(!found); + } + printf("Device %s...\n", blkid_dev_devname(dev)); + + iter = blkid_tag_iterate_begin(dev); + while (blkid_tag_next(iter, &type, &value) == 0) { + printf("\tTag %s has value %s\n", type, value); + } + blkid_tag_iterate_end(iter); + + blkid_put_cache(cache); + return (0); +} +#endif diff --git a/libblkid/src/topology/dm.c b/libblkid/src/topology/dm.c new file mode 100644 index 0000000..612b5df --- /dev/null +++ b/libblkid/src/topology/dm.c @@ -0,0 +1,134 @@ +/* + * device-mapper (dm) topology + * -- this is fallback for old systems where the topology information is not + * exported by sysfs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "topology.h" + +static int is_dm_device(dev_t devno) +{ + return blkid_driver_has_major("device-mapper", major(devno)); +} + +static int probe_dm_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const char * const paths[] = { + "/usr/local/sbin/dmsetup", + "/usr/sbin/dmsetup", + "/sbin/dmsetup" + }; + int dmpipe[] = { -1, -1 }, stripes = 0, stripesize = 0; + const char *cmd = NULL; + FILE *stream = NULL; + long long offset = 0, size = 0; + size_t i; + dev_t devno = blkid_probe_get_devno(pr); + + if (!devno) + goto nothing; /* probably not a block device */ + if (!is_dm_device(devno)) + goto nothing; + + for (i = 0; i < ARRAY_SIZE(paths); i++) { + struct stat sb; + if (stat(paths[i], &sb) == 0) { + cmd = paths[i]; + break; + } + } + + if (!cmd) + goto nothing; + if (pipe(dmpipe) < 0) { + DBG(LOWPROBE, ul_debug("Failed to open pipe: errno=%d", errno)); + goto nothing; + } + + switch (fork()) { + case 0: + { + const char *dmargv[7]; + char maj[16], min[16]; + + /* Plumbing */ + close(dmpipe[0]); + + if (dmpipe[1] != STDOUT_FILENO) + dup2(dmpipe[1], STDOUT_FILENO); + + if (drop_permissions() != 0) + exit(1); + + snprintf(maj, sizeof(maj), "%d", major(devno)); + snprintf(min, sizeof(min), "%d", minor(devno)); + + dmargv[0] = cmd; + dmargv[1] = "table"; + dmargv[2] = "-j"; + dmargv[3] = maj; + dmargv[4] = "-m"; + dmargv[5] = min; + dmargv[6] = NULL; + + execv(dmargv[0], (char * const *) dmargv); + + DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno)); + exit(1); + } + case -1: + DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno)); + goto nothing; + default: + break; + } + + stream = fdopen(dmpipe[0], "r" UL_CLOEXECSTR); + if (!stream) + goto nothing; + + if (dmpipe[1] != -1) { + close(dmpipe[1]); + } + + if (fscanf(stream, "%lld %lld striped %d %d ", + &offset, &size, &stripes, &stripesize) != 0) + goto nothing; + + blkid_topology_set_minimum_io_size(pr, stripesize << 9); + blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 9); + + fclose(stream); + return 0; + +nothing: + if (stream) + fclose(stream); + else if (dmpipe[0] != -1) + close(dmpipe[0]); + return 1; +} + +const struct blkid_idinfo dm_tp_idinfo = +{ + .name = "dm", + .probefunc = probe_dm_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/evms.c b/libblkid/src/topology/evms.c new file mode 100644 index 0000000..1aa32f9 --- /dev/null +++ b/libblkid/src/topology/evms.c @@ -0,0 +1,77 @@ +/* + * Evms topology + * -- this is fallback for old systems where the topology information is not + * exported by sysfs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "topology.h" + +#define EVMS_MAJOR 117 + +#ifndef _IOT__IOTBASE_u_int32_t +#define _IOT__IOTBASE_u_int32_t IOT_SIMPLE(uint32_t) +#endif +#define _IOT_evms_stripe_info _IOT (_IOTS(uint32_t), 2, 0, 0, 0, 0) +#define EVMS_GET_STRIPE_INFO _IOR(EVMS_MAJOR, 0xF0, struct evms_stripe_info) + +struct evms_stripe_info { + uint32_t size; /* stripe unit 512-byte blocks */ + uint32_t width; /* the number of stripe members or RAID data disks */ +}; + +static int is_evms_device(dev_t devno) +{ + if (major(devno) == EVMS_MAJOR) + return 1; + return blkid_driver_has_major("evms", major(devno)); +} + +static int probe_evms_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct evms_stripe_info evms; + dev_t devno = blkid_probe_get_devno(pr); + + if (!devno) + goto nothing; /* probably not a block device */ + + if (!is_evms_device(devno)) + goto nothing; + + memset(&evms, 0, sizeof(evms)); + + if (ioctl(pr->fd, EVMS_GET_STRIPE_INFO, &evms)) + goto nothing; + + blkid_topology_set_minimum_io_size(pr, evms.size << 9); + blkid_topology_set_optimal_io_size(pr, (evms.size * evms.width) << 9); + + return 0; + +nothing: + return 1; +} + +const struct blkid_idinfo evms_tp_idinfo = +{ + .name = "evms", + .probefunc = probe_evms_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/ioctl.c b/libblkid/src/topology/ioctl.c new file mode 100644 index 0000000..3560a2f --- /dev/null +++ b/libblkid/src/topology/ioctl.c @@ -0,0 +1,83 @@ +/* + * ioctl based topology -- gathers topology information + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "topology.h" + +/* + * ioctl topology values + */ +static const struct topology_val { + + long ioc; + + /* functions to set probing result */ + int (*set_ulong)(blkid_probe, unsigned long); + int (*set_int)(blkid_probe, int); + int (*set_u64)(blkid_probe, uint64_t); + +} topology_vals[] = { + { BLKALIGNOFF, NULL, blkid_topology_set_alignment_offset }, + { BLKIOMIN, blkid_topology_set_minimum_io_size }, + { BLKIOOPT, blkid_topology_set_optimal_io_size }, + { BLKPBSZGET, blkid_topology_set_physical_sector_size }, + { BLKGETDISKSEQ, .set_u64 = blkid_topology_set_diskseq }, + /* we read BLKSSZGET in topology.c */ +}; + +static int probe_ioctl_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(topology_vals); i++) { + const struct topology_val *val = &topology_vals[i]; + int rc = 1; + union { + unsigned long ul; + int i; + uint64_t u64; + } data; + + if (ioctl(pr->fd, val->ioc, &data) == -1) + goto nothing; + + if (val->set_int) + rc = val->set_int(pr, data.i); + else if (val->set_ulong) + rc = val->set_ulong(pr, data.ul); + else + rc = val->set_u64(pr, data.u64); + + if (rc) + goto err; + } + + return 0; +nothing: + return 1; +err: + return -1; +} + +const struct blkid_idinfo ioctl_tp_idinfo = +{ + .name = "ioctl", + .probefunc = probe_ioctl_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/lvm.c b/libblkid/src/topology/lvm.c new file mode 100644 index 0000000..af1a612 --- /dev/null +++ b/libblkid/src/topology/lvm.c @@ -0,0 +1,144 @@ +/* + * lvm topology + * -- this is fallback for old systems where the topology information is not + * exported by sysfs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "topology.h" + +#ifndef LVM_BLK_MAJOR +# define LVM_BLK_MAJOR 58 +#endif + +static int is_lvm_device(dev_t devno) +{ + if (major(devno) == LVM_BLK_MAJOR) + return 1; + return blkid_driver_has_major("lvm", major(devno)); +} + +static int probe_lvm_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + const char * const paths[] = { + "/usr/local/sbin/lvdisplay", + "/usr/sbin/lvdisplay", + "/sbin/lvdisplay" + }; + int lvpipe[] = { -1, -1 }, stripes = 0, stripesize = 0; + FILE *stream = NULL; + char *cmd = NULL, *devname = NULL, buf[1024]; + size_t i; + dev_t devno = blkid_probe_get_devno(pr); + + if (!devno) + goto nothing; /* probably not a block device */ + if (!is_lvm_device(devno)) + goto nothing; + + for (i = 0; i < ARRAY_SIZE(paths); i++) { + struct stat sb; + if (stat(paths[i], &sb) == 0) { + cmd = (char *) paths[i]; + break; + } + } + + if (!cmd) + goto nothing; + + devname = blkid_devno_to_devname(devno); + if (!devname) + goto nothing; + + if (pipe(lvpipe) < 0) { + DBG(LOWPROBE, ul_debug("Failed to open pipe: errno=%d", errno)); + goto nothing; + } + + switch (fork()) { + case 0: + { + char *lvargv[3]; + + /* Plumbing */ + close(lvpipe[0]); + + if (lvpipe[1] != STDOUT_FILENO) + dup2(lvpipe[1], STDOUT_FILENO); + + if (drop_permissions() != 0) + exit(1); + + lvargv[0] = cmd; + lvargv[1] = devname; + lvargv[2] = NULL; + + execv(lvargv[0], lvargv); + + DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno)); + exit(1); + } + case -1: + DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno)); + goto nothing; + default: + break; + } + + stream = fdopen(lvpipe[0], "r" UL_CLOEXECSTR); + if (!stream) + goto nothing; + + while (fgets(buf, sizeof(buf), stream) != NULL) { + if (!strncmp(buf, "Stripes", 7)) + ignore_result( sscanf(buf, "Stripes %d", &stripes) ); + + if (!strncmp(buf, "Stripe size", 11)) + ignore_result( sscanf(buf, "Stripe size (KByte) %d", &stripesize) ); + } + + if (!stripes) + goto nothing; + + blkid_topology_set_minimum_io_size(pr, stripesize << 10); + blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 10); + + free(devname); + fclose(stream); + close(lvpipe[1]); + return 0; + +nothing: + free(devname); + if (stream) + fclose(stream); + else if (lvpipe[0] != -1) + close(lvpipe[0]); + if (lvpipe[1] != -1) + close(lvpipe[1]); + return 1; +} + +const struct blkid_idinfo lvm_tp_idinfo = +{ + .name = "lvm", + .probefunc = probe_lvm_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/md.c b/libblkid/src/topology/md.c new file mode 100644 index 0000000..02f27a8 --- /dev/null +++ b/libblkid/src/topology/md.c @@ -0,0 +1,154 @@ +/* + * Linux Software RAID (md) topology + * -- this is fallback for old systems where the topology information is not + * exported by sysfs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + */ +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "topology.h" + +#ifndef MD_MAJOR +#define MD_MAJOR 9 +#endif + +#ifndef _IOT__IOTBASE_uint32_t +#define _IOT__IOTBASE_uint32_t IOT_SIMPLE(uint32_t) +#endif +#define _IOT_md_array_info _IOT (_IOTS(uint32_t), 18, 0, 0, 0, 0) +#define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, struct md_array_info) + +struct md_array_info { + /* + * Generic constant information + */ + uint32_t major_version; + uint32_t minor_version; + uint32_t patch_version; + uint32_t ctime; + uint32_t level; + uint32_t size; + uint32_t nr_disks; + uint32_t raid_disks; + uint32_t md_minor; + uint32_t not_persistent; + + /* + * Generic state information + */ + uint32_t utime; /* 0 Superblock update time */ + uint32_t state; /* 1 State bits (clean, ...) */ + uint32_t active_disks; /* 2 Number of currently active disks */ + uint32_t working_disks; /* 3 Number of working disks */ + uint32_t failed_disks; /* 4 Number of failed disks */ + uint32_t spare_disks; /* 5 Number of spare disks */ + + /* + * Personality information + */ + uint32_t layout; /* 0 the array's physical layout */ + uint32_t chunk_size; /* 1 chunk size in bytes */ + +}; + +static int is_md_device(dev_t devno) +{ + if (major(devno) == MD_MAJOR) + return 1; + return blkid_driver_has_major("md", major(devno)); +} + +static int probe_md_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + int fd = -1; + dev_t disk = 0; + dev_t devno = blkid_probe_get_devno(pr); + struct md_array_info md; + + if (!devno) + goto nothing; /* probably not a block device */ + + if (!is_md_device(devno)) + goto nothing; + + if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk)) + goto nothing; + + if (disk == devno) + fd = pr->fd; + else { + char *diskpath = blkid_devno_to_devname(disk); + + if (!diskpath) + goto nothing; + + fd = open(diskpath, O_RDONLY|O_CLOEXEC); + free(diskpath); + + if (fd == -1) + goto nothing; + } + + memset(&md, 0, sizeof(md)); + + if (ioctl(fd, GET_ARRAY_INFO, &md)) + goto nothing; + + if (fd >= 0 && fd != pr->fd) { + close(fd); + fd = -1; + } + + /* + * Ignore levels we don't want aligned (e.g. linear) + * and deduct disk(s) from stripe width on RAID4/5/6 + */ + switch (md.level) { + case 6: + md.raid_disks--; + /* fallthrough */ + case 5: + case 4: + md.raid_disks--; + /* fallthrough */ + case 1: + case 0: + case 10: + break; + default: + goto nothing; + } + + blkid_topology_set_minimum_io_size(pr, md.chunk_size); + blkid_topology_set_optimal_io_size(pr, (unsigned long) md.chunk_size * md.raid_disks); + + return 0; + +nothing: + if (fd >= 0 && fd != pr->fd) + close(fd); + return 1; +} + +const struct blkid_idinfo md_tp_idinfo = +{ + .name = "md", + .probefunc = probe_md_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c new file mode 100644 index 0000000..25debd0 --- /dev/null +++ b/libblkid/src/topology/sysfs.c @@ -0,0 +1,132 @@ +/* + * sysfs based topology -- gathers topology information from Linux sysfs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * For more information see Linux kernel Documentation/ABI/testing/sysfs-block. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "sysfs.h" +#include "topology.h" + +/* + * Sysfs topology values (since 2.6.31, May 2009). + */ +static const struct topology_val { + + /* /sys/dev/block/<maj>:<min>/<ATTR> */ + const char *attr; + + /* functions to set probing result */ + int (*set_ulong)(blkid_probe, unsigned long); + int (*set_int)(blkid_probe, int); + int (*set_u64)(blkid_probe, uint64_t); + +} topology_vals[] = { + { "alignment_offset", NULL, blkid_topology_set_alignment_offset }, + { "queue/minimum_io_size", blkid_topology_set_minimum_io_size }, + { "queue/optimal_io_size", blkid_topology_set_optimal_io_size }, + { "queue/physical_block_size", blkid_topology_set_physical_sector_size }, + { "queue/dax", blkid_topology_set_dax }, + { "diskseq", .set_u64 = blkid_topology_set_diskseq }, +}; + +static int probe_sysfs_tp(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + dev_t dev; + int rc, set_parent = 1; + struct path_cxt *pc; + size_t i, count = 0; + + dev = blkid_probe_get_devno(pr); + if (!dev) + return 1; + pc = ul_new_sysfs_path(dev, NULL, NULL); + if (!pc) + return 1; + + rc = 1; /* nothing (default) */ + + for (i = 0; i < ARRAY_SIZE(topology_vals); i++) { + const struct topology_val *val = &topology_vals[i]; + int ok = ul_path_access(pc, F_OK, val->attr) == 0; + + rc = 1; /* nothing */ + + if (!ok && set_parent) { + dev_t disk = blkid_probe_get_wholedisk_devno(pr); + set_parent = 0; + + /* + * Read attributes from "disk" if the current device is + * a partition. Note that sysfs ul_path_* API is able + * to redirect requests to attributes if parent is set. + */ + if (disk && disk != dev) { + struct path_cxt *parent = ul_new_sysfs_path(disk, NULL, NULL); + if (!parent) + goto done; + + sysfs_blkdev_set_parent(pc, parent); + ul_unref_path(parent); + + /* try it again */ + ok = ul_path_access(pc, F_OK, val->attr) == 0; + } + } + if (!ok) + continue; /* attribute does not exist */ + + if (val->set_ulong) { + uint64_t data; + + if (ul_path_read_u64(pc, &data, val->attr) != 0) + continue; + rc = val->set_ulong(pr, (unsigned long) data); + + } else if (val->set_int) { + int64_t data; + + if (ul_path_read_s64(pc, &data, val->attr) != 0) + continue; + rc = val->set_int(pr, (int) data); + } else if (val->set_u64) { + uint64_t data; + + if (ul_path_read_u64(pc, &data, val->attr) != 0) + continue; + rc = val->set_u64(pr, data); + } + + if (rc < 0) + goto done; /* error */ + if (rc == 0) + count++; + } + +done: + ul_unref_path(pc); /* unref pc and parent */ + if (count) + return 0; /* success */ + return rc; /* error or nothing */ +} + +const struct blkid_idinfo sysfs_tp_idinfo = +{ + .name = "sysfs", + .probefunc = probe_sysfs_tp, + .magics = BLKID_NONE_MAGIC +}; + diff --git a/libblkid/src/topology/topology.c b/libblkid/src/topology/topology.c new file mode 100644 index 0000000..67df3e3 --- /dev/null +++ b/libblkid/src/topology/topology.c @@ -0,0 +1,423 @@ +/* + * topology - gathers information about device topology + * + * Copyright 2009 Red Hat, Inc. All rights reserved. + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <inttypes.h> + +#include "topology.h" + +/** + * SECTION:topology + * @title: Topology information + * @short_description: block device topology information. + * + * The topology chain provides details about Linux block devices, for more + * information see: + * + * Linux kernel Documentation/ABI/testing/sysfs-block + * + * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(), + * and provides: + * + * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can + * address. It is typically 512 bytes. + * + * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device + * can write atomically. It is usually the same as the + * logical sector size but may be bigger. + * + * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O. + * For RAID arrays it is often the stripe chunk size. + * + * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays + * it is usually the stripe width or the internal track size. + * + * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is + * offset from the disk's natural alignment. + * + * The NAME=value tags are not defined when the corresponding topology value + * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides + * topology information. + * + * Binary interface: + * + * blkid_probe_get_topology() + * + * blkid_topology_get_'VALUENAME'() + */ +static int topology_probe(blkid_probe pr, struct blkid_chain *chn); +static void topology_free(blkid_probe pr, void *data); +static int topology_is_complete(blkid_probe pr); +static int topology_set_logical_sector_size(blkid_probe pr); + +/* + * Binary interface + */ +struct blkid_struct_topology { + unsigned long alignment_offset; + unsigned long minimum_io_size; + unsigned long optimal_io_size; + unsigned long logical_sector_size; + unsigned long physical_sector_size; + unsigned long dax; + uint64_t diskseq; +}; + +/* + * Topology chain probing functions + */ +static const struct blkid_idinfo *idinfos[] = +{ +#ifdef __linux__ + &sysfs_tp_idinfo, + &ioctl_tp_idinfo, + &md_tp_idinfo, + &dm_tp_idinfo, + &lvm_tp_idinfo, + &evms_tp_idinfo +#endif +}; + + +/* + * Driver definition + */ +const struct blkid_chaindrv topology_drv = { + .id = BLKID_CHAIN_TOPLGY, + .name = "topology", + .dflt_enabled = FALSE, + .idinfos = idinfos, + .nidinfos = ARRAY_SIZE(idinfos), + .probe = topology_probe, + .safeprobe = topology_probe, + .free_data = topology_free +}; + +/** + * blkid_probe_enable_topology: + * @pr: probe + * @enable: TRUE/FALSE + * + * Enables/disables the topology probing for non-binary interface. + * + * Returns: 0 on success, or -1 in case of error. + */ +int blkid_probe_enable_topology(blkid_probe pr, int enable) +{ + pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable; + return 0; +} + +/** + * blkid_probe_get_topology: + * @pr: probe + * + * This is a binary interface for topology values. See also blkid_topology_* + * functions. + * + * This function is independent on blkid_do_[safe,full]probe() and + * blkid_probe_enable_topology() calls. + * + * WARNING: the returned object will be overwritten by the next + * blkid_probe_get_topology() call for the same @pr. If you want to + * use more blkid_topology objects in the same time you have to create + * more blkid_probe handlers (see blkid_new_probe()). + * + * Returns: blkid_topology, or NULL in case of error. + */ +blkid_topology blkid_probe_get_topology(blkid_probe pr) +{ + return (blkid_topology) blkid_probe_get_binary_data(pr, + &pr->chains[BLKID_CHAIN_TOPLGY]); +} + +/* + * The blkid_do_probe() backend. + */ +static int topology_probe(blkid_probe pr, struct blkid_chain *chn) +{ + size_t i; + + if (chn->idx < -1) + return -1; + + if (!S_ISBLK(pr->mode)) + return -EINVAL; /* nothing, works with block devices only */ + + if (chn->binary) { + DBG(LOWPROBE, ul_debug("initialize topology binary data")); + + if (chn->data) + /* reset binary data */ + memset(chn->data, 0, + sizeof(struct blkid_struct_topology)); + else { + chn->data = calloc(1, + sizeof(struct blkid_struct_topology)); + if (!chn->data) + return -ENOMEM; + } + } + + blkid_probe_chain_reset_values(pr, chn); + + DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]", + chn->idx)); + + i = chn->idx < 0 ? 0 : chn->idx + 1U; + + for ( ; i < ARRAY_SIZE(idinfos); i++) { + const struct blkid_idinfo *id = idinfos[i]; + + chn->idx = i; + + if (id->probefunc) { + DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name)); + + errno = 0; + if (id->probefunc(pr, NULL) != 0) + continue; + } + + if (!topology_is_complete(pr)) + continue; + + /* generic for all probing drivers */ + topology_set_logical_sector_size(pr); + + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]", + id->name, chn->idx)); + return BLKID_PROBE_OK; + } + + DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]", + chn->idx)); + return BLKID_PROBE_NONE; +} + +static void topology_free(blkid_probe pr __attribute__((__unused__)), + void *data) +{ + free(data); +} + +static int topology_set_value(blkid_probe pr, const char *name, + size_t structoff, unsigned long data) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!chn) + return -1; + if (!data) + return 0; /* ignore zeros */ + + if (chn->binary) { + memcpy((char *) chn->data + structoff, &data, sizeof(data)); + return 0; + } + return blkid_probe_sprintf_value(pr, name, "%lu", data); +} + +static int topology_set_value64(blkid_probe pr, const char *name, + size_t structoff, uint64_t data) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!chn) + return -1; + if (!data) + return 0; /* ignore zeros */ + + if (chn->binary) { + memcpy((char *) chn->data + structoff, &data, sizeof(data)); + return 0; + } + return blkid_probe_sprintf_value(pr, name, "%"PRIu64, data); +} + + +/* the topology info is complete when we have at least "minimum_io_size" which + * is provided by all blkid topology drivers */ +static int topology_is_complete(blkid_probe pr) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + if (!chn) + return FALSE; + + if (chn->binary && chn->data) { + blkid_topology tp = (blkid_topology) chn->data; + if (tp->minimum_io_size) + return TRUE; + } + + return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE; +} + +int blkid_topology_set_alignment_offset(blkid_probe pr, int val) +{ + unsigned long xval; + + /* Welcome to Hell. The kernel is able to return -1 as an + * alignment_offset if no compatible sizes and alignments + * exist for stacked devices. + * + * There is no way how libblkid caller can respond to the value -1, so + * we will hide this corner case... + * + * (TODO: maybe we can export an extra boolean value 'misaligned' rather + * then complete hide this problem.) + */ + xval = val < 0 ? 0 : val; + + return topology_set_value(pr, + "ALIGNMENT_OFFSET", + offsetof(struct blkid_struct_topology, alignment_offset), + xval); +} + +int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val) +{ + return topology_set_value(pr, + "MINIMUM_IO_SIZE", + offsetof(struct blkid_struct_topology, minimum_io_size), + val); +} + +int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val) +{ + return topology_set_value(pr, + "OPTIMAL_IO_SIZE", + offsetof(struct blkid_struct_topology, optimal_io_size), + val); +} + +/* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to + * waste time with sysfs. + */ +static int topology_set_logical_sector_size(blkid_probe pr) +{ + unsigned long val = blkid_probe_get_sectorsize(pr); + + if (!val) + return -1; + + return topology_set_value(pr, + "LOGICAL_SECTOR_SIZE", + offsetof(struct blkid_struct_topology, logical_sector_size), + val); +} + +int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val) +{ + return topology_set_value(pr, + "PHYSICAL_SECTOR_SIZE", + offsetof(struct blkid_struct_topology, physical_sector_size), + val); +} + +int blkid_topology_set_dax(blkid_probe pr, unsigned long val) +{ + return topology_set_value(pr, + "DAX", + offsetof(struct blkid_struct_topology, dax), + val); +} + +int blkid_topology_set_diskseq(blkid_probe pr, uint64_t val) +{ + return topology_set_value64(pr, + "DISKSEQ", + offsetof(struct blkid_struct_topology, diskseq), + val); +} + +/** + * blkid_topology_get_alignment_offset: + * @tp: topology + * + * Returns: alignment offset in bytes or 0. + */ +unsigned long blkid_topology_get_alignment_offset(blkid_topology tp) +{ + return tp->alignment_offset; +} + +/** + * blkid_topology_get_minimum_io_size: + * @tp: topology + * + * Returns: minimum io size in bytes or 0. + */ +unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp) +{ + return tp->minimum_io_size; +} + +/** + * blkid_topology_get_optimal_io_size + * @tp: topology + * + * Returns: optimal io size in bytes or 0. + */ +unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp) +{ + return tp->optimal_io_size; +} + +/** + * blkid_topology_get_logical_sector_size + * @tp: topology + * + * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0. + */ +unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp) +{ + return tp->logical_sector_size; +} + +/** + * blkid_topology_get_physical_sector_size + * @tp: topology + * + * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0. + */ +unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp) +{ + return tp->physical_sector_size; +} + +/** + * blkid_topology_get_dax + * @tp: topology + * + * Returns: 1 if dax is supported, 0 otherwise. + * + * Since: 2.36 + */ +unsigned long blkid_topology_get_dax(blkid_topology tp) +{ + return tp->dax; +} + +/** + * blkid_topology_get_diskseq + * @tp: topology + * + * Returns: disk sequence number + * + * Since: 2.39 + */ +uint64_t blkid_topology_get_diskseq(blkid_topology tp) +{ + return tp->diskseq; +} diff --git a/libblkid/src/topology/topology.h b/libblkid/src/topology/topology.h new file mode 100644 index 0000000..9810fe4 --- /dev/null +++ b/libblkid/src/topology/topology.h @@ -0,0 +1,26 @@ +#ifndef BLKID_TOPOLOGY_H +#define BLKID_TOPOLOGY_H + +#include "blkidP.h" + +extern int blkid_topology_set_alignment_offset(blkid_probe pr, int val); +extern int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val); +extern int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val); +extern int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val); +extern int blkid_topology_set_dax(blkid_probe pr, unsigned long val); +extern int blkid_topology_set_diskseq(blkid_probe pr, uint64_t val); + +/* + * topology probers + */ +#ifdef __linux__ +extern const struct blkid_idinfo ioctl_tp_idinfo; +extern const struct blkid_idinfo md_tp_idinfo; +extern const struct blkid_idinfo evms_tp_idinfo; +extern const struct blkid_idinfo sysfs_tp_idinfo; +extern const struct blkid_idinfo dm_tp_idinfo; +extern const struct blkid_idinfo lvm_tp_idinfo; +#endif + +#endif /* BLKID_TOPOLOGY_H */ + diff --git a/libblkid/src/verify.c b/libblkid/src/verify.c new file mode 100644 index 0000000..3b9754f --- /dev/null +++ b/libblkid/src/verify.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "blkidP.h" +#include "sysfs.h" + +static void blkid_probe_to_tags(blkid_probe pr, blkid_dev dev) +{ + const char *data; + const char *name; + int nvals, n; + size_t len; + + nvals = blkid_probe_numof_values(pr); + + for (n = 0; n < nvals; n++) { + if (blkid_probe_get_value(pr, n, &name, &data, &len) != 0) + continue; + if (strncmp(name, "PART_ENTRY_", 11) == 0) { + if (strcmp(name, "PART_ENTRY_UUID") == 0) + blkid_set_tag(dev, "PARTUUID", data, len); + else if (strcmp(name, "PART_ENTRY_NAME") == 0) + blkid_set_tag(dev, "PARTLABEL", data, len); + + } else if (!strstr(name, "_ID")) { + /* superblock UUID, LABEL, ... + * but not {SYSTEM,APPLICATION,..._ID} */ + blkid_set_tag(dev, name, data, len); + } + } +} + +/* + * Verify that the data in dev is consistent with what is on the actual + * block device (using the devname field only). Normally this will be + * called when finding items in the cache, but for long running processes + * is also desirable to revalidate an item before use. + * + * If we are unable to revalidate the data, we return the old data and + * do not set the BLKID_BID_FL_VERIFIED flag on it. + */ +blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) +{ + blkid_tag_iterate iter; + const char *type, *value; + struct stat st; + time_t diff, now; + int fd; + + if (!dev || !cache) + return NULL; + + now = time(NULL); + diff = (uintmax_t)now - dev->bid_time; + + if (stat(dev->bid_name, &st) < 0) { + DBG(PROBE, ul_debug("blkid_verify: error %s (%d) while " + "trying to stat %s", strerror(errno), errno, + dev->bid_name)); + open_err: + if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) { + /* We don't have read permission, just return cache data. */ + DBG(PROBE, ul_debug("returning unverified data for %s", + dev->bid_name)); + return dev; + } + blkid_free_dev(dev); + return NULL; + } + + if (now >= dev->bid_time && +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + (st.st_mtime < dev->bid_time || + (st.st_mtime == dev->bid_time && + st.st_mtim.tv_nsec / 1000 <= dev->bid_utime)) && +#else + st.st_mtime <= dev->bid_time && +#endif + diff >= 0 && diff < BLKID_PROBE_MIN) { + dev->bid_flags |= BLKID_BID_FL_VERIFIED; + return dev; + } + +#ifndef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + DBG(PROBE, ul_debug("need to revalidate %s (cache time %lld, stat time %lld,\t" + "time since last check %lld)", + dev->bid_name, (long long)dev->bid_time, + (long long)st.st_mtime, (long long)diff)); +#else + DBG(PROBE, ul_debug("need to revalidate %s (cache time %lld.%lld, stat time %lld.%lld,\t" + "time since last check %lld)", + dev->bid_name, + (long long)dev->bid_time, (long long)dev->bid_utime, + (long long)st.st_mtime, (long long)st.st_mtim.tv_nsec / 1000, + (long long)diff)); +#endif + + if (sysfs_devno_is_dm_private(st.st_rdev, NULL)) { + blkid_free_dev(dev); + return NULL; + } + if (!cache->probe) { + cache->probe = blkid_new_probe(); + if (!cache->probe) { + blkid_free_dev(dev); + return NULL; + } + } + + fd = open(dev->bid_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + DBG(PROBE, ul_debug("blkid_verify: error %s (%d) while " + "opening %s", strerror(errno), errno, + dev->bid_name)); + goto open_err; + } + + if (blkid_probe_set_device(cache->probe, fd, 0, 0)) { + /* failed to read the device */ + close(fd); + blkid_free_dev(dev); + return NULL; + } + + /* remove old cache info */ + iter = blkid_tag_iterate_begin(dev); + while (blkid_tag_next(iter, &type, &value) == 0) + blkid_set_tag(dev, type, NULL, 0); + blkid_tag_iterate_end(iter); + + /* enable superblocks probing */ + blkid_probe_enable_superblocks(cache->probe, TRUE); + blkid_probe_set_superblocks_flags(cache->probe, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE); + + /* enable partitions probing */ + blkid_probe_enable_partitions(cache->probe, TRUE); + blkid_probe_set_partitions_flags(cache->probe, BLKID_PARTS_ENTRY_DETAILS); + + /* probe */ + if (blkid_do_safeprobe(cache->probe)) { + /* found nothing or error */ + blkid_free_dev(dev); + dev = NULL; + } + + if (dev) { +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + struct timeval tv; + if (!gettimeofday(&tv, NULL)) { + dev->bid_time = tv.tv_sec; + dev->bid_utime = tv.tv_usec; + } else +#endif + dev->bid_time = time(NULL); + + dev->bid_devno = st.st_rdev; + dev->bid_flags |= BLKID_BID_FL_VERIFIED; + cache->bic_flags |= BLKID_BIC_FL_CHANGED; + + blkid_probe_to_tags(cache->probe, dev); + + DBG(PROBE, ul_debug("%s: devno 0x%04llx, type %s", + dev->bid_name, (long long)st.st_rdev, dev->bid_type)); + } + + /* reset prober */ + blkid_probe_reset_superblocks_filter(cache->probe); + blkid_probe_set_device(cache->probe, -1, 0, 0); + close(fd); + + return dev; +} + +#ifdef TEST_PROGRAM +int main(int argc, char **argv) +{ + blkid_dev dev; + blkid_cache cache; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s device\n" + "Probe a single device to determine type\n", argv[0]); + exit(1); + } + if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL); + if (!dev) { + printf("%s: %s has an unsupported type\n", argv[0], argv[1]); + return (1); + } + printf("TYPE='%s'\n", dev->bid_type ? dev->bid_type : "(null)"); + if (dev->bid_label) + printf("LABEL='%s'\n", dev->bid_label); + if (dev->bid_uuid) + printf("UUID='%s'\n", dev->bid_uuid); + + blkid_free_dev(dev); + return (0); +} +#endif diff --git a/libblkid/src/version.c b/libblkid/src/version.c new file mode 100644 index 0000000..ed92db9 --- /dev/null +++ b/libblkid/src/version.c @@ -0,0 +1,62 @@ +/* + * version.c --- Return the version of the blkid library + * + * Copyright (C) 2004 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Lesser General + * Public License. + * %End-Header% + */ + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "blkid.h" + +/* LIBBLKID_* defined in the global config.h */ +static const char *lib_version = LIBBLKID_VERSION; /* release version */ +static const char *lib_date = LIBBLKID_DATE; + +/** + * blkid_parse_version_string: + * @ver_string: version string (e.g. "2.16.0") + * + * Returns: release version code. + */ +int blkid_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') + continue; + if (!isdigit(*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + +/** + * blkid_get_library_version: + * @ver_string: returns release version (!= SONAME version) + * @date_string: returns date + * + * Returns: release version code. + */ +int blkid_get_library_version(const char **ver_string, + const char **date_string) +{ + if (ver_string) + *ver_string = lib_version; + if (date_string) + *date_string = lib_date; + + return blkid_parse_version_string(lib_version); +} |